diff --git a/backend/.env b/backend/.env index c1ed77b..e33764f 100644 --- a/backend/.env +++ b/backend/.env @@ -1,2 +1,2 @@ -HTTPS_ENABLED=0 +HTTPS_ENABLED=1 AUTH_RP_ORIGINS=https://l14pbbk95100006.tegdssd.de:9999,https://l14pbbk95100006.tegdssd.de:5173,https://localhost:9999,https://127.0.0.1:9999,https://10.0.1.25:9999,http://localhost:5173,http://127.0.0.1:5173,http://10.0.1.25:5173 \ No newline at end of file diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 93eb37d..64d5374 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -8,7 +8,7 @@ import Tabs, { type TabItem } from './components/ui/Tabs' import RecorderSettings from './components/ui/RecorderSettings' import FinishedDownloads from './components/ui/FinishedDownloads' import Player from './components/ui/Player' -import type { RecordJob } from './types' +import type { RecordJob, JobEvent, RecorderSettingsState, TaskStateEvent, AnalysisProgressEvent, BackgroundSplitProgress } from './types' import Downloads from './components/ui/Downloads' import ModelsTab from './components/ui/ModelsTab' import ProgressBar from './components/ui/ProgressBar' @@ -57,118 +57,6 @@ async function apiJSON(url: string, init?: RequestInit): Promise { return res.json() as Promise } -type RecorderSettingsState = { - recordDir: string - doneDir: string - ffmpegPath?: string - autoAddToDownloadList?: boolean - autoStartAddedDownloads?: boolean - enableConcurrentDownloadsLimit?: boolean - maxConcurrentDownloads?: number - useChaturbateApi?: boolean - useMyFreeCamsWatcher?: boolean - autoDeleteSmallDownloads?: boolean - autoDeleteSmallDownloadsBelowMB?: number - blurPreviews?: boolean - teaserPlayback?: 'still' | 'hover' | 'all' - teaserAudio?: boolean - lowDiskPauseBelowGB?: number - enableNotifications?: boolean -} - -type JobEvent = { - type?: 'job_upsert' | 'job_remove' - model?: string - jobId?: string - status?: string - phase?: string - progress?: number - sourceUrl?: string - output?: string - startedAt?: string - startedAtMs?: number - endedAt?: string - endedAtMs?: number - sizeBytes?: number - durationSeconds?: number - previewState?: string - roomStatus?: string - isOnline?: boolean - modelImageUrl?: string - modelChatRoomUrl?: string - ts?: number - error?: string - - postWorkKey?: string - postWork?: { - state?: string - position?: number - waiting?: number - running?: number - maxParallel?: number - } - - // ✅ neu für finished_postwork - file?: string - assetId?: string - queue?: 'postwork' | 'enrich' - state?: 'queued' | 'running' | 'done' | 'error' | 'missing' - label?: string - position?: number - waiting?: number - running?: number - maxParallel?: number -} - -type TaskStateEvent = { - type?: 'task_state' - generateAssetsRunning?: boolean - regenerateAssetsRunning?: boolean - checkVideosRunning?: boolean - cleanupRunning?: boolean - anyRunning?: boolean - ts?: number -} - -type AnalysisProgressEvent = { - type?: 'analysis_progress' - scope?: string - requestId?: string - analysisRequestId?: string - running?: boolean - phase?: 'starting' | 'running' | 'done' | 'error' | string - progress?: number - current?: number - total?: number - file?: string - sourceFile?: string - message?: string - error?: string - startedAtMs?: number - finishedAtMs?: number - durationMs?: number - ts?: number -} - -type BackgroundSplitProgress = { - phase: 'preparing' | 'splitting' | 'done' | 'error' - total: number - current: number - completed: number - segmentProgress?: number - overallProgress?: number - message?: string - segment?: { - index: number - label: string - start: number - end: number - duration: number - } - startedAtMs: number - sourceFile: string -} - const DEFAULT_RECORDER_SETTINGS: RecorderSettingsState = { recordDir: 'records', doneDir: 'records/done', diff --git a/frontend/src/components/ui/FinishedDownloads.tsx b/frontend/src/components/ui/FinishedDownloads.tsx index fa2f1fb..b1f9b4e 100644 --- a/frontend/src/components/ui/FinishedDownloads.tsx +++ b/frontend/src/components/ui/FinishedDownloads.tsx @@ -2274,15 +2274,18 @@ export default function FinishedDownloads({ showOnlyCorrupt const globalFilterActive = searchActiveForGlobalFetch - const effectiveAllMode = globalFilterActive || loadMode === 'all' + const mobileCardStackMode = isSmall && view === 'cards' + + const shouldLoadAllDoneJobs = + globalFilterActive || loadMode === 'all' || mobileCardStackMode + + const effectiveAllMode = shouldLoadAllDoneJobs const DEFAULT_GALLERY_PAGE_SIZE = 8 const [galleryPageSize, setGalleryPageSize] = useState( DEFAULT_GALLERY_PAGE_SIZE ) - const lastGalleryScaleIndexRef = useRef(galleryCardScaleIndex) - const handleGalleryRecommendedPageSizeChange = useCallback((rawSize: number) => { const rounded = Math.round(Number(rawSize)) if (!Number.isFinite(rounded) || rounded <= 0) return @@ -2290,23 +2293,16 @@ export default function FinishedDownloads({ const next = Math.max(1, Math.min(24, rounded)) setGalleryPageSize((current) => { - const scaleChanged = lastGalleryScaleIndexRef.current !== galleryCardScaleIndex - - if (scaleChanged) { - lastGalleryScaleIndexRef.current = galleryCardScaleIndex - return next - } - - // Wichtig: - // Nach Page-Wechsel/ResizeObserver kann kurz 8 statt 10 gemeldet werden. - // Nicht automatisch verkleinern, sonst verschwinden bereits geladene Cards. - if (next < current) { - return current - } - + if (current === next) return current return next }) - }, [galleryCardScaleIndex]) + + onPageSizeChange?.(next) + + if (page !== 1) { + onPageChange(1) + } + }, [onPageSizeChange, onPageChange, page]) const effectivePageSize = view === 'gallery' @@ -2314,12 +2310,12 @@ export default function FinishedDownloads({ : pageSize const doneJobsPage = - globalFilterActive + shouldLoadAllDoneJobs ? (allDoneJobs ?? doneJobs) : doneJobs const doneTotalPage = - globalFilterActive + shouldLoadAllDoneJobs ? (allDoneJobs?.length ?? doneTotal) : doneTotal @@ -2578,12 +2574,12 @@ export default function FinishedDownloads({ return visibleRows.slice(start, end) } - if (view === 'gallery') { - return visibleRows.slice(0, effectivePageSize) - } - return visibleRows - }, [visibleRows, page, effectivePageSize, effectiveAllMode, view]) + }, [visibleRows, page, effectivePageSize, effectiveAllMode]) + + const cardRows = mobileCardStackMode ? visibleRows : pageRows + + const activeVisibleRows = pageRows const emptyFolder = !finishedDownloadsLoading && @@ -2605,10 +2601,10 @@ export default function FinishedDownloads({ const allPageSelected = useMemo(() => { return ( - pageRows.length > 0 && - pageRows.every((j) => selectionStore.isSelected(keyFor(j))) + activeVisibleRows.length > 0 && + activeVisibleRows.every((j) => selectionStore.isSelected(keyFor(j))) ) - }, [pageRows, keyFor, selectionStore, selectionVersion]) + }, [activeVisibleRows, keyFor, selectionStore, selectionVersion]) const hasSearchQuery = (searchQuery || '').trim() !== '' const selectionActionsVisible = selectedCount > 0 && selectionActionsOpen @@ -2617,7 +2613,7 @@ export default function FinishedDownloads({ const selectedKeys = useMemo(() => { const next = new Set() - for (const j of pageRows) { + for (const j of activeVisibleRows) { const k = keyFor(j) if (selectionStore.isSelected(k)) { next.add(k) @@ -2625,7 +2621,7 @@ export default function FinishedDownloads({ } return next - }, [pageRows, keyFor, selectionStore, selectionVersion]) + }, [activeVisibleRows, keyFor, selectionStore, selectionVersion]) // ----------------------------------------------------------------------------- // callbacks @@ -3801,7 +3797,7 @@ export default function FinishedDownloads({ // ----------------------------------------------------------------------------- useEffect(() => { - if (!globalFilterActive) { + if (!shouldLoadAllDoneJobs) { setAllDoneJobs(null) setAllDoneJobsLoading(false) return @@ -3832,7 +3828,7 @@ export default function FinishedDownloads({ return () => { cancelled = true } - }, [globalFilterActive, loadAllDoneJobs]) + }, [shouldLoadAllDoneJobs, loadAllDoneJobs]) useEffect(() => { if (!showOnlyCorrupt) return @@ -5421,7 +5417,7 @@ export default function FinishedDownloads({ {view === 'cards' && (
-
-
-
- { - flushSync(() => { - setInlinePlay(null) - setTeaserKey(null) - setHoverTeaserKey(null) - }) - scrollFinishedListToTop() - onPageChange(p) - }} - showSummary={false} - prevLabel="Zurück" - nextLabel="Weiter" - className="mt-0" - /> + {!mobileCardStackMode ? ( +
+
+
+ { + flushSync(() => { + setInlinePlay(null) + setTeaserKey(null) + setHoverTeaserKey(null) + }) + scrollFinishedListToTop() + onPageChange(p) + }} + showSummary={false} + prevLabel="Zurück" + nextLabel="Weiter" + className="mt-0" + /> +
-
+ ) : null} ) } diff --git a/frontend/src/components/ui/FinishedDownloadsCardsView.tsx b/frontend/src/components/ui/FinishedDownloadsCardsView.tsx index c59fa64..1863b1b 100644 --- a/frontend/src/components/ui/FinishedDownloadsCardsView.tsx +++ b/frontend/src/components/ui/FinishedDownloadsCardsView.tsx @@ -1010,11 +1010,13 @@ function FinishedDownloadsCardsView({ }, [isSmall, mobileOrderedRows, keyFor]) const mobileStackDepth = 3 - const mobilePreloadDepth = mobileStackDepth + 3 - const mobileLoadedStackRows = isSmall ? mobileOrderedRows.slice(0, mobilePreloadDepth) : [] - const mobileVisibleStackRows = mobileLoadedStackRows.slice(0, mobileStackDepth) - const mobilePreloadedRow = mobileLoadedStackRows[mobileStackDepth] ?? null + const mobileLoadedStackRows = isSmall + ? mobileOrderedRows.slice(0, mobileStackDepth) + : [] + + const mobileVisibleStackRows = mobileLoadedStackRows + const mobilePreloadedRow = null const mobileTopRow = mobileVisibleStackRows[0] ?? null const mobileTopKey = isSmall && mobileVisibleStackRows[0] diff --git a/frontend/src/components/ui/FinishedDownloadsGalleryView.tsx b/frontend/src/components/ui/FinishedDownloadsGalleryView.tsx index bc40448..d042e4b 100644 --- a/frontend/src/components/ui/FinishedDownloadsGalleryView.tsx +++ b/frontend/src/components/ui/FinishedDownloadsGalleryView.tsx @@ -193,8 +193,7 @@ function FinishedDownloadsGalleryView({ Math.floor((containerWidth + gapPx) / (minCardWidth + gapPx)) ) - // mobil etwas mehr vertikale Füllung, desktop wie bisher ca. 2 Reihen - const rowsPerPage = containerWidth < 640 ? 3 : 2 + const rowsPerPage = 2 const nextPageSize = clamp(columns * rowsPerPage, rowsPerPage, 24) diff --git a/frontend/src/components/ui/TrainingTab.tsx b/frontend/src/components/ui/TrainingTab.tsx index 23a66b9..e411f83 100644 --- a/frontend/src/components/ui/TrainingTab.tsx +++ b/frontend/src/components/ui/TrainingTab.tsx @@ -809,7 +809,7 @@ function TrainingStageOverlay(props: { aria-hidden="true" draggable={false} className={[ - 'absolute inset-0 z-0 h-full w-full scale-105 object-cover blur-[1px]', + 'absolute inset-0 z-0 h-full w-full object-contain blur-[1px]', 'transition-opacity duration-500 ease-out will-change-opacity motion-reduce:transition-none', backgroundVisible ? 'opacity-80' : 'opacity-0', ].join(' ')} @@ -4955,7 +4955,7 @@ export default function TrainingTab(props: {
+ { /* Rechte Seite */ }