From 1a559fe76d62f880f5a270f3ae4582996d503f3e Mon Sep 17 00:00:00 2001 From: Linrador <68631622+Linrador@users.noreply.github.com> Date: Sat, 6 Jun 2026 14:58:55 +0200 Subject: [PATCH] fixed finished downloads --- backend/__pycache__/ai_server.cpython-314.pyc | Bin 20461 -> 21458 bytes backend/tasks_cleanup.go | 33 +++ backend/tray.go | 2 +- frontend/src/App.tsx | 199 ++++++------------ frontend/src/components/ui/RatingOverlay.tsx | 39 ++-- .../src/components/ui/RecorderSettings.tsx | 7 +- frontend/src/components/ui/ToastProvider.tsx | 37 ++-- frontend/src/components/ui/TrainingTab.tsx | 101 +++++++-- 8 files changed, 227 insertions(+), 191 deletions(-) diff --git a/backend/__pycache__/ai_server.cpython-314.pyc b/backend/__pycache__/ai_server.cpython-314.pyc index 7369f49f2612e98961ce6955124f79c25b7d9249..02cc6c3058cb4ae3749a3c52df54b275f71647d0 100644 GIT binary patch delta 825 zcmZ9KO=uHA6vyA&-F$a9jcH8URM<3@uv971)GF54rm1bxDs>M5aYb4aP)P}F>O~J( zK{OuAoJGATdJ&0u@FI9q5Q3!;^x|0%QVTt*^Saq64$SZU=e_^ToBjCl2tPkVWlWL% zz(?xk$c=}`H;hN?3-nABHs)TXkBWGPt%+Z8hGirTXW2FBqjY&WKeHRMy^S~Ps@`a)a|I9rOK3v6>?v?_811=OW(bakWK5FD@WF{GaM8nUAe z#ZhVAlZNj5r`m7l0D4uw5oU?S$jOLPDWXC5-XH01zf^Z!N&pOlG1y%~5m(}DXLN`? zSUgmBJK3H)y6^sbx1OWdT``88UE_fye>?hr87+9; delta 150 zcmcb#obl~^Mm}vmUM>b85dO%NwcltWp9CKdD+5C!LjyyQjW|Pz(?%0rW=7k|NzAty z?Izo?XfoPQ&R{vk7R0E`py{x=f>ptV(Q5Kc&uT`^$)a8gjNFsWysmC;@Y7&qb}r)G yyxKpBk(Zm1fuTqQXon_a5$|OAKuIncpa>8#6vu9M3XEpr`otu_$W|l@6afIx^&&F> diff --git a/backend/tasks_cleanup.go b/backend/tasks_cleanup.go index b621e72..8042f8c 100644 --- a/backend/tasks_cleanup.go +++ b/backend/tasks_cleanup.go @@ -224,6 +224,21 @@ func settingsCleanupHandler(w http.ResponseWriter, r *http.Request) { } } + // Neuer Cleanup-Abschnitt: alten 100%-Fortschritt vom Small-Files-Scan zurücksetzen. + updateCleanupJobState(jobID, func(st *CleanupTaskState) { + st.Queued = false + st.Running = true + if st.StartedAt.IsZero() { + st.StartedAt = time.Now() + } + st.Done = 0 + st.Total = 0 + st.CurrentFile = "" + st.Text = "Räume Record-Reste auf…" + st.Error = "" + st.FinishedAt = nil + }) + if err := cleanupRecordDirOrphanAVFiles(ctx, jobID, recordAbs, &resp); err != nil { finishCleanupJob(jobID, "", err) return @@ -236,7 +251,19 @@ func settingsCleanupHandler(w http.ResponseWriter, r *http.Request) { default: } + updateCleanupJobState(jobID, func(st *CleanupTaskState) { + st.Queued = false + st.Running = true + st.Done = 0 + st.Total = 0 + st.CurrentFile = "" + st.Text = "Räume Generated-Assets auf…" + st.Error = "" + st.FinishedAt = nil + }) + gcStats := triggerGeneratedGarbageCollectorSync() + resp.GeneratedOrphansChecked = gcStats.Checked resp.GeneratedOrphansRemoved = gcStats.Removed resp.GeneratedOrphansRemovedBytes = gcStats.RemovedBytes @@ -494,8 +521,14 @@ func cleanupDeleteRecordOrphanFile(ctx context.Context, jobID string, path strin } updateCleanupJobState(jobID, func(st *CleanupTaskState) { + st.Queued = false + st.Running = true + st.Done = 0 + st.Total = 0 st.CurrentFile = name st.Text = "Räume Record-Reste auf…" + st.Error = "" + st.FinishedAt = nil }) if err := removeWithRetry(path); err != nil && !os.IsNotExist(err) { diff --git a/backend/tray.go b/backend/tray.go index c6fb60b..67c4b79 100644 --- a/backend/tray.go +++ b/backend/tray.go @@ -108,7 +108,7 @@ func runTray(onQuit func(), statsFn func() TrayStats) { updateStatus() case <-mOpenFrontend.ClickedCh: - _ = openBrowser("http://localhost:9999") + _ = openBrowser("https://localhost:9999") case <-mOpenRecordDir.ClickedCh: _ = openFolder(resolveConfiguredDir(getSettings().RecordDir)) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 90561db..93b533f 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -914,10 +914,16 @@ export default function App() { type DonePrefetch = { key: string items: RecordJob[] + totalCount: number ts: number } - const makePrefetchKey = (page: number, sort: DoneSortMode) => `${sort}::${page}` + const makePrefetchKey = ( + page: number, + sort: DoneSortMode, + pageSize: number, + keep: boolean + ) => `${sort}::${pageSize}::${keep ? 1 : 0}::${page}` const getDoneTotalPages = (count: number, pageSize = donePageSize) => { const safeCount = Number.isFinite(count) && count > 0 ? count : 0 @@ -928,7 +934,7 @@ export default function App() { if (pageToFetch < 1) return if (donePrefetchInFlightRef.current) return - const key = makePrefetchKey(pageToFetch, doneSort) + const key = makePrefetchKey(pageToFetch, doneSort, donePageSize, includeKeep) const cur = donePrefetchRef.current if (cur?.key === key && Date.now() - cur.ts < 15_000) { // frisch genug @@ -938,7 +944,7 @@ export default function App() { donePrefetchInFlightRef.current = true try { const res = await fetch( - `/api/record/done?page=${pageToFetch}&pageSize=${donePageSize}&sort=${encodeURIComponent(doneSort)}${ + `/api/record/done?page=${pageToFetch}&pageSize=${donePageSize}&sort=${encodeURIComponent(doneSort)}&withCount=1${ includeKeep ? '&includeKeep=1' : '' }`, { cache: 'no-store' as any } @@ -952,66 +958,23 @@ export default function App() { ? (data as RecordJob[]) : [] - donePrefetchRef.current = { key, items, ts: Date.now() } + const totalCountRaw = Number(data?.count ?? data?.totalCount ?? 0) + const totalCount = + Number.isFinite(totalCountRaw) && totalCountRaw >= 0 + ? totalCountRaw + : 0 + + donePrefetchRef.current = { + key, + items, + totalCount, + ts: Date.now(), + } } finally { donePrefetchInFlightRef.current = false } }, [doneSort, includeKeep, donePageSize]) - function doneJobMergeKey(job: RecordJob): string { - const output = String(job?.output ?? '').trim() - const id = String((job as any)?.id ?? '').trim() - - if (output) return output - if (id) return id - - return JSON.stringify([ - String((job as any)?.sourceUrl ?? ''), - String((job as any)?.startedAt ?? ''), - String((job as any)?.endedAt ?? ''), - ]) - } - - function mergeDoneJobsStable( - prev: RecordJob[], - incoming: RecordJob[] - ): RecordJob[] { - if (!Array.isArray(prev) || prev.length === 0) return incoming - if (!Array.isArray(incoming) || incoming.length === 0) return prev - - const incomingByKey = new Map() - for (const item of incoming) { - incomingByKey.set(doneJobMergeKey(item), item) - } - - const seen = new Set() - const merged: RecordJob[] = [] - - // 1) Bestehende sichtbare Reihenfolge beibehalten, - // aber Daten der vorhandenen Elemente aktualisieren. - for (const oldItem of prev) { - const key = doneJobMergeKey(oldItem) - const nextItem = incomingByKey.get(key) - - if (nextItem) { - merged.push(nextItem) - seen.add(key) - } else { - merged.push(oldItem) - } - } - - // 2) Wirklich neue Finished-Downloads hinten anhängen, - // ohne bestehende Karten umzuschichten. - for (const item of incoming) { - const key = doneJobMergeKey(item) - if (seen.has(key)) continue - merged.push(item) - } - - return merged - } - const loadDoneCount = useCallback(async () => { const now = Date.now() @@ -1043,20 +1006,33 @@ export default function App() { const loadDonePage = useCallback(async ( pageToLoad = donePage, - sortToLoad = doneSort, - opts?: { preserveOrder?: boolean } + sortToLoad = doneSort ) => { try { - const preserveOrder = Boolean(opts?.preserveOrder) - const key = makePrefetchKey(pageToLoad, sortToLoad) + const key = makePrefetchKey( + pageToLoad, + sortToLoad, + donePageSize, + includeKeep + ) + const prefetched = donePrefetchRef.current if (prefetched?.key === key && Array.isArray(prefetched.items)) { - setDoneJobs((prev) => - preserveOrder ? mergeDoneJobsStable(prev, prefetched.items) : prefetched.items - ) + setDoneJobs(prefetched.items) + + const count = Number(prefetched.totalCount) + if (Number.isFinite(count) && count >= 0 && count !== doneCount) { + setDoneCount(count) + } donePrefetchRef.current = null + + const totalPages = getDoneTotalPages(count, donePageSize) + if (pageToLoad < totalPages) { + void prefetchDonePage(pageToLoad + 1) + } + return } @@ -1066,6 +1042,7 @@ export default function App() { }`, { cache: 'no-store' as any } ) + if (!res.ok) return const data = await res.json().catch(() => null) @@ -1074,9 +1051,7 @@ export default function App() { ? (data.items as RecordJob[]) : [] - setDoneJobs((prev) => - preserveOrder ? mergeDoneJobsStable(prev, items) : items - ) + setDoneJobs(items) const countRaw = Number(data?.count ?? doneCount) const count = Number.isFinite(countRaw) && countRaw >= 0 ? countRaw : 0 @@ -1093,6 +1068,12 @@ export default function App() { // ignore } }, [donePage, doneSort, donePageSize, prefetchDonePage, includeKeep, doneCount]) + + const loadDonePageRef = useRef(loadDonePage) + + useEffect(() => { + loadDonePageRef.current = loadDonePage + }, [loadDonePage]) useEffect(() => { try { @@ -2773,7 +2754,8 @@ export default function App() { void loadTaskStateOnce() if (selectedTabRef.current === 'finished') { - void loadDonePage(donePageRef.current, doneSortRef.current) + donePrefetchRef.current = null + void loadDonePageRef.current(donePageRef.current, doneSortRef.current) } }, document.hidden ? 60000 : 5000) } @@ -2802,9 +2784,8 @@ export default function App() { void loadDoneCount() if (selectedTabRef.current === 'finished') { - void loadDonePage(donePageRef.current, doneSortRef.current, { - preserveOrder: true, - }) + donePrefetchRef.current = null + void loadDonePageRef.current(donePageRef.current, doneSortRef.current) } } @@ -3009,7 +2990,8 @@ export default function App() { void loadAutostartState().catch(() => {}) if (selectedTabRef.current === 'finished') { - void loadDonePage(donePageRef.current, doneSortRef.current) + donePrefetchRef.current = null + void loadDonePageRef.current(donePageRef.current, doneSortRef.current) } } @@ -3306,47 +3288,6 @@ export default function App() { } } - const fillDonePageFromPrefetch = useCallback((opts?: { removedFile?: string }) => { - const removedFile = String(opts?.removedFile ?? '').trim() - - setDoneJobs((prev) => { - const filtered = removedFile - ? prev.filter((j) => baseName(j.output || '') !== removedFile) - : [...prev] - - const need = Math.max(0, donePageSize - filtered.length) - if (need <= 0) return filtered - - const prefetchKey = makePrefetchKey(donePage + 1, doneSort) - const buf = donePrefetchRef.current - - if (!buf || buf.key !== prefetchKey || !Array.isArray(buf.items) || buf.items.length === 0) { - return filtered - } - - const next: RecordJob[] = [...filtered] - const used = new Set( - next.map((x) => String(x.id || baseName(x.output || '')).trim()) - ) - - while (next.length < donePageSize && buf.items.length > 0) { - const cand = buf.items.shift()! - const id = String(cand.id || baseName(cand.output || '')).trim() - if (!id || used.has(id)) continue - used.add(id) - next.push(cand) - } - - donePrefetchRef.current = { - ...buf, - items: buf.items, - ts: buf.ts, - } - - return next - }) - }, [donePage, donePageSize, doneSort]) - const doneCountRef = useRef(doneCount) useEffect(() => { @@ -3369,18 +3310,12 @@ export default function App() { ), onSuccess: async () => { window.setTimeout(() => { - fillDonePageFromPrefetch({ removedFile: file }) - setJobs((prev) => prev.filter((j) => baseName(j.output || '') !== file)) setPlayerJob((prev) => (prev && baseName(prev.output || '') === file ? null : prev)) - const nextDoneCount = Math.max(0, doneCountRef.current - 1) - const totalPagesAfterDelete = getDoneTotalPages(nextDoneCount, donePageSize) - - if (donePage < totalPagesAfterDelete) { - void prefetchDonePage(donePage + 1) - } + donePrefetchRef.current = null + void loadDonePageRef.current(donePageRef.current, doneSortRef.current) void loadDoneCount() }, 320) }, @@ -3400,9 +3335,6 @@ export default function App() { [ runFinishedFileAction, notify, - fillDonePageFromPrefetch, - donePage, - prefetchDonePage, loadDoneCount, ] ) @@ -3423,9 +3355,12 @@ export default function App() { ), onSuccess: async () => { window.setTimeout(() => { - fillDonePageFromPrefetch({ removedFile: file }) setJobs((prev) => prev.filter((j) => baseName(j.output || '') !== file)) setPlayerJob((prev) => (prev && baseName(prev.output || '') === file ? null : prev)) + + donePrefetchRef.current = null + + void loadDonePageRef.current(donePageRef.current, doneSortRef.current) void loadDoneCount() }, 320) }, @@ -3439,7 +3374,7 @@ export default function App() { return } }, - [runFinishedFileAction, notify, fillDonePageFromPrefetch, loadDoneCount] + [runFinishedFileAction, notify, loadDoneCount] ) const handleToggleHot = useCallback( @@ -3493,13 +3428,18 @@ export default function App() { }) ) + donePrefetchRef.current = null + + void loadDonePageRef.current(donePageRef.current, doneSortRef.current) + void loadDoneCount() + return res } catch (e: any) { notify.error('Umbenennen fehlgeschlagen: ', baseName(job.output)) return } }, - [notify] + [notify, loadDoneCount] ) const handleDeleteJob = useCallback( @@ -4541,7 +4481,8 @@ export default function App() { } bumpAssets() - void loadDonePage(donePageRef.current, doneSortRef.current) + donePrefetchRef.current = null + void loadDonePageRef.current(donePageRef.current, doneSortRef.current) void loadDoneCount() } catch (err) { const message = diff --git a/frontend/src/components/ui/RatingOverlay.tsx b/frontend/src/components/ui/RatingOverlay.tsx index 75bce54..cedfdfb 100644 --- a/frontend/src/components/ui/RatingOverlay.tsx +++ b/frontend/src/components/ui/RatingOverlay.tsx @@ -1850,62 +1850,50 @@ function RatingHeroHeader({ toneLabel, segmentsCount, entriesCount, - onClose, }: { stars: number toneLabel: string segmentsCount: number entriesCount: number - onClose?: () => void }) { const tone = ratingTone(stars) const progress = Math.max(0, Math.min(100, Math.round((stars / 5) * 100))) return ( -
-
-
+
+
+
-
+
AI Rating
- + {toneLabel}
-
+
{stars}/5 · {segmentsCount} Stellen · {entriesCount} Details
- - {onClose ? ( - - ) : null}
-
+
diff --git a/frontend/src/components/ui/RecorderSettings.tsx b/frontend/src/components/ui/RecorderSettings.tsx index 0813b24..fdd2c35 100644 --- a/frontend/src/components/ui/RecorderSettings.tsx +++ b/frontend/src/components/ui/RecorderSettings.tsx @@ -223,7 +223,12 @@ function SettingsSection(props: {
- - {trainingRunning ? (