Keep pause across Coolify restarts, apply the chosen still from the image picker, and remux clips with Range/faststart so finished video can be scrubbed.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Towsty
2026-08-28 08:15:52 -05:00
co-authored by Cursor
parent 870150f052
commit 7d783bd97e
12 changed files with 338 additions and 49 deletions
+18 -7
View File
@@ -90,7 +90,8 @@
<article
v-for="still in visibleStills"
:key="still.id"
class="library-thumb overflow-hidden rounded-2xl border border-white/10 bg-zinc-950"
class="library-thumb cursor-pointer overflow-hidden rounded-2xl border border-white/10 bg-zinc-950"
@click="pickStill(still)"
>
<div class="relative h-28 overflow-hidden bg-zinc-900">
<img
@@ -108,7 +109,7 @@
type="button"
class="rounded-full bg-zinc-950/80 p-1.5 text-amber-100 hover:bg-zinc-900"
:title="isRevealed(still.id) ? 'Hide thumbnail' : 'Reveal thumbnail'"
@click="toggleReveal(still.id)"
@click.stop="toggleReveal(still.id)"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="h-4 w-4">
<template v-if="isRevealed(still.id)">
@@ -125,7 +126,7 @@
type="button"
class="rounded-full bg-amber-400 p-1.5 text-zinc-950 hover:bg-amber-300"
title="Use as input"
@click="pickStill(still)"
@click.stop="pickStill(still)"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" class="h-4 w-4">
<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" />
@@ -135,7 +136,7 @@
</div>
<div class="flex items-center justify-between gap-2 px-3 py-2">
<span class="truncate text-xs text-zinc-400">{{ still.name || still.filename }}</span>
<button type="button" class="text-[10px] text-red-300 hover:text-red-200" @click="removeStill(still)">Delete</button>
<button type="button" class="text-[10px] text-red-300 hover:text-red-200" @click.stop="removeStill(still)">Delete</button>
</div>
</article>
</div>
@@ -190,6 +191,7 @@ const activeFolderId = ref(props.folderId)
const tab = ref<'input' | 'output'>('input')
const revealAll = ref(false)
const revealedIds = ref<string[]>([])
const picking = ref(false)
function onKey(event: KeyboardEvent) {
if (event.key === 'Escape') emit('close')
@@ -266,7 +268,10 @@ async function ingest(file: File) {
try {
const result = await $fetch<{ still?: { id: string; filename: string; hideInput?: boolean } }>('/api/library/stills', { method: 'POST', body })
emit('updated', result)
if (result.still?.id) emit('pick', result.still)
if (props.stillOnly) {
if (result.still?.id) emit('pick', result.still)
return
}
emit('select', file, props.hideInputPreview === true, result.still?.id)
} catch (error: any) {
emit('error', error?.data?.statusMessage || 'Could not save image')
@@ -287,14 +292,20 @@ function onDrop(event: DragEvent) {
}
async function pickStill(still: PickerStill) {
emit('pick', { id: still.id, filename: still.filename, hideInput: still.hideInput })
if (props.stillOnly) return
if (picking.value) return
picking.value = true
try {
if (props.stillOnly) {
emit('pick', { id: still.id, filename: still.filename, hideInput: still.hideInput })
return
}
const blob = await $fetch<Blob>(`/api/library/stills/${still.id}`, { responseType: 'blob' })
const file = new File([blob], still.filename, { type: blob.type || 'image/png' })
emit('select', file, still.hideInput === true, still.id)
} catch (error: any) {
emit('error', error?.data?.statusMessage || 'Could not load that still')
} finally {
picking.value = false
}
}