add: android preview to main video
Signed-off-by: pakintada@gmail.com <Pakin>
This commit is contained in:
parent
1156b5e6c3
commit
af9a4a2be5
1 changed files with 237 additions and 64 deletions
|
|
@ -27,6 +27,7 @@
|
|||
import * as adb from '$lib/core/adb/adb';
|
||||
import { env } from '$env/dynamic/public';
|
||||
import { AdbInstance } from '../../../state.svelte';
|
||||
import ScrcpyDialog from '$lib/components/scrcpy-dialog.svelte';
|
||||
|
||||
const CREATE_ENDPOINT = '/api/video-mainpage';
|
||||
const LIST_ENDPOINT = '/api/video-mainpage/list';
|
||||
|
|
@ -121,6 +122,8 @@
|
|||
let editBrewingTxtEnFile = $state<File | null>(null);
|
||||
let editSaving = $state(false);
|
||||
|
||||
let showScreenMirror = $state(false);
|
||||
|
||||
const editBrewingPlaySeconds = $derived(Math.max(1, Math.round(editBrewingRaw) - DURATION_TRIM));
|
||||
|
||||
function toIso(d: string): string {
|
||||
|
|
@ -203,7 +206,8 @@
|
|||
const file = input.files?.[0];
|
||||
input.value = '';
|
||||
if (!file) return;
|
||||
if (!file.name.toLowerCase().endsWith('.png')) return addNotification('WARN:Text overlay must be .png');
|
||||
if (!file.name.toLowerCase().endsWith('.png'))
|
||||
return addNotification('WARN:Text overlay must be .png');
|
||||
set(file, URL.createObjectURL(file));
|
||||
}
|
||||
function pickBrewingTxt(e: Event) {
|
||||
|
|
@ -286,7 +290,14 @@
|
|||
const user = $auth;
|
||||
if (!user) return addNotification('ERR:Not logged in');
|
||||
if (!AdbInstance.instance) return addNotification('ERR:Connect a machine first');
|
||||
if (!name.trim() || !startDate || !mainFile || !brewingFile || !brewingTxtFile || !brewingTxtEnFile)
|
||||
if (
|
||||
!name.trim() ||
|
||||
!startDate ||
|
||||
!mainFile ||
|
||||
!brewingFile ||
|
||||
!brewingTxtFile ||
|
||||
!brewingTxtEnFile
|
||||
)
|
||||
return addNotification(
|
||||
'ERR:Need a name, start date, both videos, and both brewing text overlays (TH + EN)'
|
||||
);
|
||||
|
|
@ -355,7 +366,9 @@
|
|||
managed = data.managed ?? [];
|
||||
readonlyList = data.readonly ?? [];
|
||||
} catch (error) {
|
||||
addNotification(`ERR:Load list failed: ${error instanceof Error ? error.message : 'unknown'}`);
|
||||
addNotification(
|
||||
`ERR:Load list failed: ${error instanceof Error ? error.message : 'unknown'}`
|
||||
);
|
||||
} finally {
|
||||
loadingList = false;
|
||||
}
|
||||
|
|
@ -395,7 +408,8 @@
|
|||
const file = input.files?.[0];
|
||||
input.value = '';
|
||||
if (!file) return;
|
||||
if (!file.name.toLowerCase().endsWith('.png')) return addNotification('WARN:Text overlay must be .png');
|
||||
if (!file.name.toLowerCase().endsWith('.png'))
|
||||
return addNotification('WARN:Text overlay must be .png');
|
||||
if (en) editBrewingTxtEnFile = file;
|
||||
else editBrewingTxtFile = file;
|
||||
}
|
||||
|
|
@ -418,7 +432,8 @@
|
|||
fd.append('start', toIso(editStart));
|
||||
fd.append('end', editEnd ? toIso(editEnd) : 'NONE');
|
||||
if (editBrewingFile) fd.append('brewing_duration', String(editBrewingPlaySeconds));
|
||||
else if (target.brewing?.duration) fd.append('brewing_duration', String(target.brewing.duration));
|
||||
else if (target.brewing?.duration)
|
||||
fd.append('brewing_duration', String(target.brewing.duration));
|
||||
if (editMainFile) fd.append('video', editMainFile);
|
||||
if (editBrewingFile) fd.append('brewing_video', editBrewingFile);
|
||||
if (editBrewingTxtFile) fd.append('brewing_txt', editBrewingTxtFile);
|
||||
|
|
@ -476,7 +491,9 @@
|
|||
<div class="flex items-center justify-between px-8 py-4">
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold">Advertisement Videos</h1>
|
||||
<p class="text-sm text-muted-foreground">Main-page & brewing-page videos, scheduled by date</p>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
Main-page & brewing-page videos, scheduled by date
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<Badge variant={isAdbConnected ? 'default' : 'secondary'}>
|
||||
|
|
@ -484,7 +501,13 @@
|
|||
</Badge>
|
||||
{#if !isAdbConnected}
|
||||
<Button variant="outline" onclick={connectMachine} disabled={connecting}>
|
||||
{#if connecting}<Spinner class="mr-2 h-4 w-4" />Connecting...{:else}<MonitorPlay class="mr-2 h-4 w-4" />Connect Machine{/if}
|
||||
{#if connecting}<Spinner class="mr-2 h-4 w-4" />Connecting...{:else}<MonitorPlay
|
||||
class="mr-2 h-4 w-4"
|
||||
/>Connect Machine{/if}
|
||||
</Button>
|
||||
{:else}
|
||||
<Button variant="outline" onclick={() => (showScreenMirror = true)}>
|
||||
Show Android Screen
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
@ -502,7 +525,8 @@
|
|||
<Card.Description>
|
||||
Upload the same clip twice — the main-page version and the brewing-page
|
||||
<code class="font-mono">_long</code> version. Auto-named
|
||||
<code class="font-mono">brewing_adv<N></code> (next free 1–40, never overwrites a video in use).
|
||||
<code class="font-mono">brewing_adv<N></code> (next free 1–40, never overwrites a video
|
||||
in use).
|
||||
</Card.Description>
|
||||
</Card.Header>
|
||||
<Card.Content class="space-y-6">
|
||||
|
|
@ -518,20 +542,29 @@
|
|||
</Select.Content>
|
||||
</Select.Root>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
Writes to <code class="font-mono">inter/{country}/video</code>{country === 'tha' ? ' + flat video/' : ''}.
|
||||
Writes to <code class="font-mono">inter/{country}/video</code>{country === 'tha'
|
||||
? ' + flat video/'
|
||||
: ''}.
|
||||
</p>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="v-name">Name</Label>
|
||||
<Input id="v-name" bind:value={name} placeholder="e.g. Bas Bew Bow Brewing" />
|
||||
<p class="text-xs text-muted-foreground">Used for the comment & the <code class="font-mono">…VideoEnable</code> variable.</p>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
Used for the comment & the <code class="font-mono">…VideoEnable</code> variable.
|
||||
</p>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="v-start" class="flex items-center gap-1.5"><CalendarDays class="h-3.5 w-3.5" /> Start date</Label>
|
||||
<Label for="v-start" class="flex items-center gap-1.5"
|
||||
><CalendarDays class="h-3.5 w-3.5" /> Start date</Label
|
||||
>
|
||||
<Input id="v-start" type="date" bind:value={startDate} />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="v-end" class="flex items-center gap-1.5"><CalendarDays class="h-3.5 w-3.5" /> End date <span class="text-muted-foreground">(optional)</span></Label>
|
||||
<Label for="v-end" class="flex items-center gap-1.5"
|
||||
><CalendarDays class="h-3.5 w-3.5" /> End date
|
||||
<span class="text-muted-foreground">(optional)</span></Label
|
||||
>
|
||||
<Input id="v-end" type="date" bind:value={endDate} />
|
||||
{#if !endDate}<p class="text-xs text-muted-foreground">Blank = open-ended</p>{/if}
|
||||
</div>
|
||||
|
|
@ -543,17 +576,27 @@
|
|||
<div class="rounded-xl border p-3">
|
||||
<div class="mb-2 flex items-center gap-2 text-sm font-semibold">
|
||||
<Film class="h-4 w-4 text-muted-foreground" /> Main-page video
|
||||
<Badge variant="outline" class="ml-auto font-mono text-[10px]">brewing_adv<N>.mp4</Badge>
|
||||
<Badge variant="outline" class="ml-auto font-mono text-[10px]"
|
||||
>brewing_adv<N>.mp4</Badge
|
||||
>
|
||||
</div>
|
||||
{#if mainFile}
|
||||
<div class="relative aspect-video overflow-hidden rounded-lg bg-black">
|
||||
<!-- svelte-ignore a11y_media_has_caption -->
|
||||
<video src={mainPreview} class="h-full w-full object-contain" muted controls></video>
|
||||
<button class="absolute right-1.5 top-1.5 rounded-full bg-black/60 p-1 text-white" onclick={clearMain}><X class="h-3.5 w-3.5" /></button>
|
||||
<video src={mainPreview} class="h-full w-full object-contain" muted controls
|
||||
></video>
|
||||
<button
|
||||
class="absolute top-1.5 right-1.5 rounded-full bg-black/60 p-1 text-white"
|
||||
onclick={clearMain}><X class="h-3.5 w-3.5" /></button
|
||||
>
|
||||
</div>
|
||||
<p class="mt-2 truncate text-xs text-muted-foreground" title={mainFile.name}>{mainFile.name} · {fmtMB(mainFile.size)}</p>
|
||||
<p class="mt-2 truncate text-xs text-muted-foreground" title={mainFile.name}>
|
||||
{mainFile.name} · {fmtMB(mainFile.size)}
|
||||
</p>
|
||||
{:else}
|
||||
<label class="flex aspect-video cursor-pointer flex-col items-center justify-center rounded-lg border-2 border-dashed text-muted-foreground transition hover:bg-muted/50 hover:text-foreground">
|
||||
<label
|
||||
class="flex aspect-video cursor-pointer flex-col items-center justify-center rounded-lg border-2 border-dashed text-muted-foreground transition hover:bg-muted/50 hover:text-foreground"
|
||||
>
|
||||
<input type="file" accept=".mp4,video/mp4" class="hidden" onchange={pickMain} />
|
||||
<Film class="mb-2 h-8 w-8" />
|
||||
<span class="text-sm font-medium">Click to select .mp4</span>
|
||||
|
|
@ -565,23 +608,40 @@
|
|||
<div class="rounded-xl border p-3">
|
||||
<div class="mb-2 flex items-center gap-2 text-sm font-semibold">
|
||||
<CoffeeIcon class="h-4 w-4 text-muted-foreground" /> Brewing-page video
|
||||
<Badge variant="outline" class="ml-auto font-mono text-[10px]">brewing_adv<N>_long.mp4</Badge>
|
||||
<Badge variant="outline" class="ml-auto font-mono text-[10px]"
|
||||
>brewing_adv<N>_long.mp4</Badge
|
||||
>
|
||||
</div>
|
||||
{#if brewingFile}
|
||||
<div class="relative aspect-video overflow-hidden rounded-lg bg-black">
|
||||
<!-- svelte-ignore a11y_media_has_caption -->
|
||||
<video src={brewingPreview} class="h-full w-full object-contain" muted controls></video>
|
||||
<button class="absolute right-1.5 top-1.5 rounded-full bg-black/60 p-1 text-white" onclick={clearBrewing}><X class="h-3.5 w-3.5" /></button>
|
||||
<video src={brewingPreview} class="h-full w-full object-contain" muted controls
|
||||
></video>
|
||||
<button
|
||||
class="absolute top-1.5 right-1.5 rounded-full bg-black/60 p-1 text-white"
|
||||
onclick={clearBrewing}><X class="h-3.5 w-3.5" /></button
|
||||
>
|
||||
</div>
|
||||
<p class="mt-2 truncate text-xs text-muted-foreground" title={brewingFile.name}>{brewingFile.name} · {fmtMB(brewingFile.size)}</p>
|
||||
<p class="mt-2 truncate text-xs text-muted-foreground" title={brewingFile.name}>
|
||||
{brewingFile.name} · {fmtMB(brewingFile.size)}
|
||||
</p>
|
||||
<p class="mt-1 flex items-center gap-1.5 text-xs font-medium">
|
||||
<Clock class="h-3.5 w-3.5 text-muted-foreground" />
|
||||
Plays {brewingPlaySeconds}s
|
||||
<span class="text-muted-foreground">(length {Math.round(brewingRawSeconds)}s − {DURATION_TRIM})</span>
|
||||
<span class="text-muted-foreground"
|
||||
>(length {Math.round(brewingRawSeconds)}s − {DURATION_TRIM})</span
|
||||
>
|
||||
</p>
|
||||
{:else}
|
||||
<label class="flex aspect-video cursor-pointer flex-col items-center justify-center rounded-lg border-2 border-dashed text-muted-foreground transition hover:bg-muted/50 hover:text-foreground">
|
||||
<input type="file" accept=".mp4,video/mp4" class="hidden" onchange={pickBrewing} />
|
||||
<label
|
||||
class="flex aspect-video cursor-pointer flex-col items-center justify-center rounded-lg border-2 border-dashed text-muted-foreground transition hover:bg-muted/50 hover:text-foreground"
|
||||
>
|
||||
<input
|
||||
type="file"
|
||||
accept=".mp4,video/mp4"
|
||||
class="hidden"
|
||||
onchange={pickBrewing}
|
||||
/>
|
||||
<CoffeeIcon class="mb-2 h-8 w-8" />
|
||||
<span class="text-sm font-medium">Click to select _long .mp4</span>
|
||||
</label>
|
||||
|
|
@ -599,13 +659,29 @@
|
|||
{#if t.file}
|
||||
<div class="relative overflow-hidden rounded-md border bg-muted/30">
|
||||
<img src={t.preview} alt={t.label} class="h-20 w-full object-contain" />
|
||||
<button class="absolute right-1 top-1 rounded-full bg-black/60 p-0.5 text-white" onclick={t.clear}><X class="h-3 w-3" /></button>
|
||||
<button
|
||||
class="absolute top-1 right-1 rounded-full bg-black/60 p-0.5 text-white"
|
||||
onclick={t.clear}><X class="h-3 w-3" /></button
|
||||
>
|
||||
</div>
|
||||
<p class="mt-1 truncate text-[10px] text-muted-foreground" title={t.file.name}>{t.file.name}</p>
|
||||
<p
|
||||
class="mt-1 truncate text-[10px] text-muted-foreground"
|
||||
title={t.file.name}
|
||||
>
|
||||
{t.file.name}
|
||||
</p>
|
||||
{:else}
|
||||
<label class="flex h-20 cursor-pointer flex-col items-center justify-center gap-1 rounded-md border border-dashed text-[11px] text-muted-foreground transition hover:bg-muted/50">
|
||||
<input type="file" accept=".png,image/png" class="hidden" onchange={t.pick} />
|
||||
<ImageIcon class="h-4 w-4" /> {t.label} .png
|
||||
<label
|
||||
class="flex h-20 cursor-pointer flex-col items-center justify-center gap-1 rounded-md border border-dashed text-[11px] text-muted-foreground transition hover:bg-muted/50"
|
||||
>
|
||||
<input
|
||||
type="file"
|
||||
accept=".png,image/png"
|
||||
class="hidden"
|
||||
onchange={t.pick}
|
||||
/>
|
||||
<ImageIcon class="h-4 w-4" />
|
||||
{t.label} .png
|
||||
</label>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
@ -618,13 +694,17 @@
|
|||
{#if pushProgress.active && submitting}
|
||||
<div class="space-y-1">
|
||||
<Progress value={pushProgress.percent} max={100} class="h-2" />
|
||||
<p class="text-center text-xs text-muted-foreground">Pushing {pushProgress.name} ({pushProgress.percent}%)</p>
|
||||
<p class="text-center text-xs text-muted-foreground">
|
||||
Pushing {pushProgress.name} ({pushProgress.percent}%)
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
</Card.Content>
|
||||
<Card.Footer class="justify-end gap-2 border-t bg-muted/30 py-4">
|
||||
<Button size="lg" onclick={handleSubmit} disabled={submitting || !isAdbConnected}>
|
||||
{#if submitting}<Spinner class="mr-2 h-4 w-4" />Saving...{:else}<Upload class="mr-2 h-4 w-4" />Create & Push{/if}
|
||||
{#if submitting}<Spinner class="mr-2 h-4 w-4" />Saving...{:else}<Upload
|
||||
class="mr-2 h-4 w-4"
|
||||
/>Create & Push{/if}
|
||||
</Button>
|
||||
</Card.Footer>
|
||||
</Card.Root>
|
||||
|
|
@ -634,7 +714,9 @@
|
|||
<div class="flex items-center justify-between">
|
||||
<h2 class="text-lg font-semibold">Existing videos</h2>
|
||||
<Button variant="ghost" size="sm" onclick={loadList} disabled={loadingList}>
|
||||
{#if loadingList}<Spinner class="mr-2 h-3.5 w-3.5" />{:else}<RefreshCw class="mr-2 h-3.5 w-3.5" />{/if}Refresh
|
||||
{#if loadingList}<Spinner class="mr-2 h-3.5 w-3.5" />{:else}<RefreshCw
|
||||
class="mr-2 h-3.5 w-3.5"
|
||||
/>{/if}Refresh
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
|
|
@ -651,29 +733,59 @@
|
|||
<div class="bg-black">
|
||||
{#if v.main}
|
||||
<!-- svelte-ignore a11y_media_has_caption -->
|
||||
<video src={videoUrl(v.main.video)} class="aspect-video w-full object-contain" preload="metadata" muted controls></video>
|
||||
<video
|
||||
src={videoUrl(v.main.video)}
|
||||
class="aspect-video w-full object-contain"
|
||||
preload="metadata"
|
||||
muted
|
||||
controls
|
||||
></video>
|
||||
{:else}
|
||||
<div class="flex aspect-video items-center justify-center text-xs text-muted-foreground">no main</div>
|
||||
<div
|
||||
class="flex aspect-video items-center justify-center text-xs text-muted-foreground"
|
||||
>
|
||||
no main
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="bg-black">
|
||||
{#if v.brewing}
|
||||
<!-- svelte-ignore a11y_media_has_caption -->
|
||||
<video src={videoUrl(v.brewing.video)} class="aspect-video w-full object-contain" preload="metadata" muted controls></video>
|
||||
<video
|
||||
src={videoUrl(v.brewing.video)}
|
||||
class="aspect-video w-full object-contain"
|
||||
preload="metadata"
|
||||
muted
|
||||
controls
|
||||
></video>
|
||||
{:else}
|
||||
<div class="flex aspect-video items-center justify-center text-xs text-muted-foreground">no brewing</div>
|
||||
<div
|
||||
class="flex aspect-video items-center justify-center text-xs text-muted-foreground"
|
||||
>
|
||||
no brewing
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-between gap-2 p-3">
|
||||
<div class="min-w-0">
|
||||
<p class="truncate text-sm font-semibold" title={v.name}>{v.name}</p>
|
||||
<div class="mt-1 flex flex-wrap items-center gap-1.5 text-[11px] text-muted-foreground">
|
||||
<div
|
||||
class="mt-1 flex flex-wrap items-center gap-1.5 text-[11px] text-muted-foreground"
|
||||
>
|
||||
<Badge variant="secondary" class="font-mono">brewing_adv{v.n}</Badge>
|
||||
<span class="flex items-center gap-1"><CalendarDays class="h-3 w-3" />{v.range_label}</span>
|
||||
{#if v.brewing?.duration}<span class="flex items-center gap-1"><Clock class="h-3 w-3" />{v.brewing.duration}s</span>{/if}
|
||||
<Badge variant={v.main ? 'default' : 'outline'} class="text-[10px]">main</Badge>
|
||||
<Badge variant={v.brewing ? 'default' : 'outline'} class="text-[10px]">brewing</Badge>
|
||||
<span class="flex items-center gap-1"
|
||||
><CalendarDays class="h-3 w-3" />{v.range_label}</span
|
||||
>
|
||||
{#if v.brewing?.duration}<span class="flex items-center gap-1"
|
||||
><Clock class="h-3 w-3" />{v.brewing.duration}s</span
|
||||
>{/if}
|
||||
<Badge variant={v.main ? 'default' : 'outline'} class="text-[10px]"
|
||||
>main</Badge
|
||||
>
|
||||
<Badge variant={v.brewing ? 'default' : 'outline'} class="text-[10px]"
|
||||
>brewing</Badge
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<Button variant="outline" size="sm" onclick={() => openEdit(v)}>
|
||||
|
|
@ -692,19 +804,31 @@
|
|||
class="flex w-full items-center gap-2 p-3 text-sm font-semibold text-muted-foreground transition hover:bg-muted/40"
|
||||
onclick={() => (showReadonly = !showReadonly)}
|
||||
>
|
||||
<ChevronDown class="h-4 w-4 shrink-0 transition-transform {showReadonly ? 'rotate-180' : ''}" />
|
||||
<ChevronDown
|
||||
class="h-4 w-4 shrink-0 transition-transform {showReadonly ? 'rotate-180' : ''}"
|
||||
/>
|
||||
<Lock class="h-3.5 w-3.5" />
|
||||
Hand-maintained videos ({readonlyList.length})
|
||||
<Badge variant="secondary" class="ml-1 text-[10px]">read-only</Badge>
|
||||
<span class="ml-auto text-xs font-normal">{showReadonly ? 'Click to hide' : 'Click to show'}</span>
|
||||
<span class="ml-auto text-xs font-normal"
|
||||
>{showReadonly ? 'Click to hide' : 'Click to show'}</span
|
||||
>
|
||||
</button>
|
||||
{#if showReadonly}
|
||||
<div class="grid grid-cols-2 gap-3 p-3 pt-0 sm:grid-cols-3 lg:grid-cols-5">
|
||||
{#each readonlyList as v (v.filename)}
|
||||
<div class="overflow-hidden rounded-md border bg-muted/30">
|
||||
<!-- svelte-ignore a11y_media_has_caption -->
|
||||
<video src={videoUrl(v.video)} class="aspect-video w-full bg-black object-contain" preload="metadata" muted controls></video>
|
||||
<p class="truncate px-1.5 py-1 font-mono text-[10px]" title={v.filename}>{v.filename}</p>
|
||||
<video
|
||||
src={videoUrl(v.video)}
|
||||
class="aspect-video w-full bg-black object-contain"
|
||||
preload="metadata"
|
||||
muted
|
||||
controls
|
||||
></video>
|
||||
<p class="truncate px-1.5 py-1 font-mono text-[10px]" title={v.filename}>
|
||||
{v.filename}
|
||||
</p>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
|
@ -721,7 +845,8 @@
|
|||
<Dialog.Header>
|
||||
<Dialog.Title>Edit video</Dialog.Title>
|
||||
<Dialog.Description>
|
||||
{#if editTarget}<code class="font-mono">brewing_adv{editTarget.n}</code> · change dates, rename, or replace either clip{/if}
|
||||
{#if editTarget}<code class="font-mono">brewing_adv{editTarget.n}</code> · change dates, rename,
|
||||
or replace either clip{/if}
|
||||
</Dialog.Description>
|
||||
</Dialog.Header>
|
||||
|
||||
|
|
@ -743,37 +868,77 @@
|
|||
|
||||
<div class="grid gap-3 sm:grid-cols-2">
|
||||
<div class="rounded-lg border p-2">
|
||||
<p class="mb-1.5 flex items-center gap-1.5 text-xs font-semibold"><Film class="h-3.5 w-3.5 text-muted-foreground" /> Main-page</p>
|
||||
<p class="mb-1.5 flex items-center gap-1.5 text-xs font-semibold">
|
||||
<Film class="h-3.5 w-3.5 text-muted-foreground" /> Main-page
|
||||
</p>
|
||||
{#if editTarget?.main}
|
||||
<!-- svelte-ignore a11y_media_has_caption -->
|
||||
<video src={videoUrl(editTarget.main.video)} class="aspect-video w-full rounded bg-black object-contain" preload="metadata" muted controls></video>
|
||||
<video
|
||||
src={videoUrl(editTarget.main.video)}
|
||||
class="aspect-video w-full rounded bg-black object-contain"
|
||||
preload="metadata"
|
||||
muted
|
||||
controls
|
||||
></video>
|
||||
{/if}
|
||||
<label class="mt-2 flex cursor-pointer items-center justify-center gap-1.5 rounded border border-dashed p-1.5 text-xs text-muted-foreground hover:bg-muted/50">
|
||||
<label
|
||||
class="mt-2 flex cursor-pointer items-center justify-center gap-1.5 rounded border border-dashed p-1.5 text-xs text-muted-foreground hover:bg-muted/50"
|
||||
>
|
||||
<input type="file" accept=".mp4,video/mp4" class="hidden" onchange={pickEditMain} />
|
||||
<Upload class="h-3.5 w-3.5" /> {editMainFile ? editMainFile.name : 'Replace (optional)'}
|
||||
<Upload class="h-3.5 w-3.5" />
|
||||
{editMainFile ? editMainFile.name : 'Replace (optional)'}
|
||||
</label>
|
||||
</div>
|
||||
<div class="rounded-lg border p-2">
|
||||
<p class="mb-1.5 flex items-center gap-1.5 text-xs font-semibold"><CoffeeIcon class="h-3.5 w-3.5 text-muted-foreground" /> Brewing-page</p>
|
||||
<p class="mb-1.5 flex items-center gap-1.5 text-xs font-semibold">
|
||||
<CoffeeIcon class="h-3.5 w-3.5 text-muted-foreground" /> Brewing-page
|
||||
</p>
|
||||
{#if editTarget?.brewing}
|
||||
<!-- svelte-ignore a11y_media_has_caption -->
|
||||
<video src={videoUrl(editTarget.brewing.video)} class="aspect-video w-full rounded bg-black object-contain" preload="metadata" muted controls></video>
|
||||
<video
|
||||
src={videoUrl(editTarget.brewing.video)}
|
||||
class="aspect-video w-full rounded bg-black object-contain"
|
||||
preload="metadata"
|
||||
muted
|
||||
controls
|
||||
></video>
|
||||
{/if}
|
||||
<label class="mt-2 flex cursor-pointer items-center justify-center gap-1.5 rounded border border-dashed p-1.5 text-xs text-muted-foreground hover:bg-muted/50">
|
||||
<label
|
||||
class="mt-2 flex cursor-pointer items-center justify-center gap-1.5 rounded border border-dashed p-1.5 text-xs text-muted-foreground hover:bg-muted/50"
|
||||
>
|
||||
<input type="file" accept=".mp4,video/mp4" class="hidden" onchange={pickEditBrewing} />
|
||||
<Upload class="h-3.5 w-3.5" /> {editBrewingFile ? editBrewingFile.name : 'Replace (optional)'}
|
||||
<Upload class="h-3.5 w-3.5" />
|
||||
{editBrewingFile ? editBrewingFile.name : 'Replace (optional)'}
|
||||
</label>
|
||||
{#if editBrewingFile}
|
||||
<p class="mt-1 flex items-center gap-1 text-[11px] text-muted-foreground"><Clock class="h-3 w-3" />Plays {editBrewingPlaySeconds}s</p>
|
||||
<p class="mt-1 flex items-center gap-1 text-[11px] text-muted-foreground">
|
||||
<Clock class="h-3 w-3" />Plays {editBrewingPlaySeconds}s
|
||||
</p>
|
||||
{/if}
|
||||
<div class="mt-2 grid grid-cols-2 gap-1.5">
|
||||
<label class="flex cursor-pointer items-center justify-center gap-1 rounded border border-dashed p-1.5 text-[11px] text-muted-foreground hover:bg-muted/50">
|
||||
<input type="file" accept=".png,image/png" class="hidden" onchange={(e) => pickEditTxt(e, false)} />
|
||||
<ImageIcon class="h-3 w-3" /> {editBrewingTxtFile ? 'TH ✓' : 'Text TH (.png)'}
|
||||
<label
|
||||
class="flex cursor-pointer items-center justify-center gap-1 rounded border border-dashed p-1.5 text-[11px] text-muted-foreground hover:bg-muted/50"
|
||||
>
|
||||
<input
|
||||
type="file"
|
||||
accept=".png,image/png"
|
||||
class="hidden"
|
||||
onchange={(e) => pickEditTxt(e, false)}
|
||||
/>
|
||||
<ImageIcon class="h-3 w-3" />
|
||||
{editBrewingTxtFile ? 'TH ✓' : 'Text TH (.png)'}
|
||||
</label>
|
||||
<label class="flex cursor-pointer items-center justify-center gap-1 rounded border border-dashed p-1.5 text-[11px] text-muted-foreground hover:bg-muted/50">
|
||||
<input type="file" accept=".png,image/png" class="hidden" onchange={(e) => pickEditTxt(e, true)} />
|
||||
<ImageIcon class="h-3 w-3" /> {editBrewingTxtEnFile ? 'EN ✓' : 'Text EN (.png)'}
|
||||
<label
|
||||
class="flex cursor-pointer items-center justify-center gap-1 rounded border border-dashed p-1.5 text-[11px] text-muted-foreground hover:bg-muted/50"
|
||||
>
|
||||
<input
|
||||
type="file"
|
||||
accept=".png,image/png"
|
||||
class="hidden"
|
||||
onchange={(e) => pickEditTxt(e, true)}
|
||||
/>
|
||||
<ImageIcon class="h-3 w-3" />
|
||||
{editBrewingTxtEnFile ? 'EN ✓' : 'Text EN (.png)'}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -782,16 +947,24 @@
|
|||
{#if pushProgress.active}
|
||||
<div class="space-y-1">
|
||||
<Progress value={pushProgress.percent} max={100} class="h-2" />
|
||||
<p class="text-center text-xs text-muted-foreground">Pushing {pushProgress.name} ({pushProgress.percent}%)</p>
|
||||
<p class="text-center text-xs text-muted-foreground">
|
||||
Pushing {pushProgress.name} ({pushProgress.percent}%)
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<Dialog.Footer>
|
||||
<Button variant="outline" onclick={() => (editOpen = false)} disabled={editSaving}>Cancel</Button>
|
||||
<Button variant="outline" onclick={() => (editOpen = false)} disabled={editSaving}
|
||||
>Cancel</Button
|
||||
>
|
||||
<Button onclick={submitEdit} disabled={editSaving || !isAdbConnected}>
|
||||
{#if editSaving}<Spinner class="mr-2 h-4 w-4" />Saving...{:else}<Upload class="mr-2 h-4 w-4" />Save & Push{/if}
|
||||
{#if editSaving}<Spinner class="mr-2 h-4 w-4" />Saving...{:else}<Upload
|
||||
class="mr-2 h-4 w-4"
|
||||
/>Save & Push{/if}
|
||||
</Button>
|
||||
</Dialog.Footer>
|
||||
</Dialog.Content>
|
||||
</Dialog.Root>
|
||||
|
||||
<ScrcpyDialog bind:open={showScreenMirror} />
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue