From d7772df75d41f63ce861cf195d51581541bed9fc Mon Sep 17 00:00:00 2001 From: mboehmlaender Date: Tue, 17 Mar 2026 15:11:22 +0000 Subject: [PATCH] 0.10.2-24 Laout --- backend/package-lock.json | 4 +- backend/package.json | 2 +- frontend/package-lock.json | 4 +- frontend/package.json | 2 +- frontend/src/components/JobDetailDialog.jsx | 156 ++++++++++++++------ frontend/src/pages/HistoryPage.jsx | 8 +- frontend/src/styles/app.css | 88 ++++++++++- package-lock.json | 4 +- package.json | 2 +- 9 files changed, 206 insertions(+), 64 deletions(-) diff --git a/backend/package-lock.json b/backend/package-lock.json index faaaebf..415a963 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -1,12 +1,12 @@ { "name": "ripster-backend", - "version": "0.10.2-23", + "version": "0.10.2-24", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ripster-backend", - "version": "0.10.2-23", + "version": "0.10.2-24", "dependencies": { "archiver": "^7.0.1", "cors": "^2.8.5", diff --git a/backend/package.json b/backend/package.json index ecb9926..a01519e 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "ripster-backend", - "version": "0.10.2-23", + "version": "0.10.2-24", "private": true, "type": "commonjs", "scripts": { diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 8751001..e673bde 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "ripster-frontend", - "version": "0.10.2-23", + "version": "0.10.2-24", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ripster-frontend", - "version": "0.10.2-23", + "version": "0.10.2-24", "dependencies": { "primeicons": "^7.0.0", "primereact": "^10.9.2", diff --git a/frontend/package.json b/frontend/package.json index e14d713..97abdb9 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "ripster-frontend", - "version": "0.10.2-23", + "version": "0.10.2-24", "private": true, "type": "module", "scripts": { diff --git a/frontend/src/components/JobDetailDialog.jsx b/frontend/src/components/JobDetailDialog.jsx index 804d5ce..44eb040 100644 --- a/frontend/src/components/JobDetailDialog.jsx +++ b/frontend/src/components/JobDetailDialog.jsx @@ -2,7 +2,6 @@ import { useEffect, useState } from 'react'; import { Link } from 'react-router-dom'; import { Dialog } from 'primereact/dialog'; import { Button } from 'primereact/button'; -import MediaInfoReviewPanel from './MediaInfoReviewPanel'; import blurayIndicatorIcon from '../assets/media-bluray.svg'; import discIndicatorIcon from '../assets/media-disc.svg'; import otherIndicatorIcon from '../assets/media-other.svg'; @@ -118,6 +117,55 @@ function shellQuote(value) { return `'${raw.replace(/'/g, `'"'"'`)}'`; } +function trackLang(value) { + const raw = String(value || '').trim().toLowerCase(); + if (!raw) return 'und'; + const map = { en: 'en', eng: 'en', de: 'de', deu: 'de', ger: 'de', tr: 'tr', tur: 'tr', fr: 'fr', fra: 'fr', fre: 'fr', es: 'es', spa: 'es', it: 'it', ita: 'it' }; + if (map[raw]) return map[raw]; + return raw.length >= 2 ? raw.slice(0, 2) : raw; +} + +function trackCodec(type, value, hint = null) { + const raw = String(value || '').trim(); + const merged = `${raw} ${String(hint || '')}`.toLowerCase(); + if (!raw) return '-'; + if (type === 'subtitle') return merged.includes('pgs') ? 'PGS' : raw.toUpperCase(); + if (merged.includes('dts-hd ma') || merged.includes('dts hd ma')) return 'DTS-HD MA'; + if (merged.includes('dts-hd')) return 'DTS-HD'; + if (merged.includes('dts') || merged.includes('dca')) return 'DTS'; + if (merged.includes('truehd')) return 'TRUEHD'; + if (merged.includes('e-ac-3') || merged.includes('eac3') || merged.includes('dd+')) return 'E-AC-3'; + if (merged.includes('ac-3') || merged.includes('ac3') || merged.includes('dolby digital')) return 'AC-3'; + return raw.toUpperCase(); +} + +function trackChLayout(rawValue) { + const raw = String(rawValue || '').trim().toLowerCase(); + if (!raw) return null; + if (raw.includes('7.1')) return '8ch | Surround 7.1'; + if (raw.includes('5.1')) return '6ch | Surround 5.1'; + if (raw.includes('stereo') || raw.includes('2.0') || raw.includes('downmix')) return '2ch | Stereo'; + if (raw.includes('mono') || raw.includes('1.0')) return '1ch | Mono'; + const nMatch = raw.match(/^([\d.]+)$/); + if (nMatch) { + const n = Number(nMatch[1]); + if (Number.isFinite(n) && n > 0) return `${Math.trunc(n)}ch`; + } + const chMatch = raw.match(/(\d+)\s*ch/); + if (chMatch) return `${chMatch[1]}ch`; + return null; +} + +function trackSizeFormat(bytes) { + const value = Number(bytes || 0); + if (!Number.isFinite(value) || value <= 0) return '-'; + const units = ['B', 'KB', 'MB', 'GB', 'TB']; + let size = value; + let i = 0; + while (size >= 1024 && i < units.length - 1) { size /= 1024; i++; } + return `${size.toFixed(2)} ${units[i]}`; +} + function buildExecutedHandBrakeCommand(handbrakeInfo) { const cmd = String(handbrakeInfo?.cmd || '').trim(); const args = Array.isArray(handbrakeInfo?.args) ? handbrakeInfo.args : []; @@ -546,35 +594,9 @@ export default function JobDetailDialog({ || configuredSelection.postScriptIds.length > 0 || configuredSelection.preChainIds.length > 0 || configuredSelection.postChainIds.length > 0; - const reviewPreEncodeItems = [ - ...configuredSelection.preScriptIds.map((id) => ({ type: 'script', id })), - ...configuredSelection.preChainIds.map((id) => ({ type: 'chain', id })) - ]; - const reviewPostEncodeItems = [ - ...configuredSelection.postScriptIds.map((id) => ({ type: 'script', id })), - ...configuredSelection.postChainIds.map((id) => ({ type: 'chain', id })) - ]; const encodePlanUserPreset = job?.encodePlan?.userPreset && typeof job.encodePlan.userPreset === 'object' ? job.encodePlan.userPreset : null; - const encodePlanUserPresetId = Number(encodePlanUserPreset?.id); - const reviewUserPresets = encodePlanUserPreset ? [encodePlanUserPreset] : []; - const encodePlanManualTrackSelection = job?.encodePlan?.manualTrackSelection - && typeof job.encodePlan.manualTrackSelection === 'object' - ? job.encodePlan.manualTrackSelection - : null; - const reviewTrackSelectionByTitle = encodePlanManualTrackSelection?.titleId != null - ? { - [encodePlanManualTrackSelection.titleId]: { - audioTrackIds: Array.isArray(encodePlanManualTrackSelection.audioTrackIds) - ? encodePlanManualTrackSelection.audioTrackIds - : [], - subtitleTrackIds: Array.isArray(encodePlanManualTrackSelection.subtitleTrackIds) - ? encodePlanManualTrackSelection.subtitleTrackIds - : [] - } - } - : {}; const reviewSelectedEncodeTitleId = job?.encodePlan?.encodeInputTitleId ?? null; const executedHandBrakeCommand = buildExecutedHandBrakeCommand(job?.handbrakeInfo); const canDownloadRaw = Boolean(job?.raw_path && job?.rawStatus?.exists && typeof onDownloadArchive === 'function'); @@ -851,25 +873,67 @@ export default function JobDetailDialog({ ) : null} - {/* Titel-Liste */} - {!isCd && !isAudiobook && job.encodePlan ? ( -
-

Spurauswahl

- 0 - ? Math.trunc(encodePlanUserPresetId) - : null} - /> -
+ {/* Spurauswahl – read-only */} + {job.encodePlan && Array.isArray(job.encodePlan.titles) && job.encodePlan.titles.length > 0 ? ( +
+
Spurauswahl
+ {job.encodePlan.titles + .filter((t) => t.selectedForEncode || t.encodeInput || String(t.id) === String(reviewSelectedEncodeTitleId)) + .map((title) => { + const audioTracks = Array.isArray(title.audioTracks) ? title.audioTracks : []; + const subtitleTracks = Array.isArray(title.subtitleTracks) ? title.subtitleTracks : []; + return ( +
+
+ #{title.id} | {title.fileName} | {title.durationMinutes != null ? `${Number(title.durationMinutes).toFixed(2)} min` : '-'} | {trackSizeFormat(title.sizeBytes)} + {title.encodeInput ? Encode-Input : null} +
+ {audioTracks.length > 0 ? ( +
+
Tonspuren (Titel #{title.id})
+ {audioTracks.map((track) => { + const lang = trackLang(track.language || track.languageLabel); + const codec = trackCodec('audio', track.format, track.description || track.title); + const chLayout = trackChLayout(track.channels); + let displayText = `#${track.id} | ${lang} | ${codec}`; + if (chLayout) displayText += ` | ${chLayout}`; + const actionInfo = track.selectedForEncode + ? (String(track.encodePreviewSummary || track.encodeActionSummary || '').trim() || 'Copy') + : 'Nicht übernommen'; + return ( +
+ {displayText} + Encode: {actionInfo} +
+ ); + })} +
+ ) : null} + {subtitleTracks.length > 0 ? ( +
+
Subtitles (Titel #{title.id})
+ {subtitleTracks.map((track) => { + const lang = trackLang(track.language || track.languageLabel); + const codec = trackCodec('subtitle', track.format); + const displayText = `#${track.id} | ${lang} | ${codec}`; + const rawAction = String(track.subtitlePreviewSummary || track.subtitleActionSummary || '').trim(); + const actionInfo = track.selectedForEncode + ? (/^nicht übernommen$/i.test(rawAction) ? 'Übernehmen' : (rawAction || 'Übernehmen')) + : 'Nicht übernommen'; + return ( +
+ {displayText} + Encode: {actionInfo} +
+ ); + })} +
+ ) : null} +
+ ); + }) + } +
) : null} ) : null} diff --git a/frontend/src/pages/HistoryPage.jsx b/frontend/src/pages/HistoryPage.jsx index 795bbbf..9aaa88c 100644 --- a/frontend/src/pages/HistoryPage.jsx +++ b/frontend/src/pages/HistoryPage.jsx @@ -866,11 +866,15 @@ export default function HistoryPage({ refreshToken = 0 }) { const renderPoster = (row, className = 'history-dv-poster') => { const mediaMeta = resolveMediaTypeMeta(row); + const isAudioSquare = ['cd', 'audiobook'].includes(mediaMeta.mediaType); + const imgClass = className === 'history-dv-poster-grid' && isAudioSquare + ? 'history-dv-poster-grid-audio' + : className; const title = row?.title || row?.detected_title || 'Poster'; if (row?.poster_url && row.poster_url !== 'N/A') { - return {title}; + return {title}; } - return
{['cd', 'audiobook'].includes(mediaMeta.mediaType) ? 'Kein Cover' : 'Kein Poster'}
; + return
{isAudioSquare ? 'Kein Cover' : 'Kein Poster'}
; }; const renderPresenceChip = (label, available) => ( diff --git a/frontend/src/styles/app.css b/frontend/src/styles/app.css index 8920ac1..c860995 100644 --- a/frontend/src/styles/app.css +++ b/frontend/src/styles/app.css @@ -2334,10 +2334,10 @@ body { } .history-dv-poster, -.history-dv-poster-grid { +.history-dv-poster-grid, +.history-dv-poster-grid-audio { display: block; width: 100%; - object-fit: cover; border-radius: 0.35rem; border: 1px solid #cba266; background: #f6ebd6; @@ -2345,10 +2345,27 @@ body { .history-dv-poster { height: 88px; + object-fit: cover; } .history-dv-poster-grid { height: 164px; + object-fit: contain; +} + +.history-dv-poster-grid-audio { + aspect-ratio: 1; + object-fit: cover; +} + +.history-dv-grid-poster-wrap { + width: min(120px, 100%); + height: 164px; + margin: 0 auto; + display: flex; + align-items: flex-start; + justify-content: center; + overflow: hidden; } .history-dv-poster-fallback { @@ -2380,11 +2397,6 @@ body { flex: 1; } -.history-dv-grid-poster-wrap { - width: min(120px, 100%); - margin: 0 auto; -} - .history-dv-grid-status-overlay { position: absolute; top: 0.5rem; @@ -3023,6 +3035,68 @@ body { color: var(--rip-muted); } +.spurauswahl-block { + display: grid; + gap: 0.5rem; + margin-top: 0.25rem; +} + +.spurauswahl-label { + font-weight: 600; + font-size: 0.85rem; + color: var(--rip-muted); + text-transform: uppercase; + letter-spacing: 0.03em; +} + +.track-title-block { + border: 1px solid var(--rip-border); + border-radius: 0.45rem; + padding: 0.55rem 0.65rem; + background: var(--rip-panel-soft); + display: grid; + gap: 0.5rem; +} + +.track-title-row { + display: flex; + align-items: center; + gap: 0.5rem; + flex-wrap: wrap; + font-size: 0.875rem; + font-weight: 500; +} + +.track-input-badge { + font-size: 0.72rem; + font-weight: 600; + padding: 0.1rem 0.4rem; + border-radius: 0.3rem; + background: var(--rip-accent-soft, #e8f4ea); + color: var(--rip-success, #2d7a3a); + border: 1px solid var(--rip-success, #2d7a3a); +} + +.track-group { + display: grid; + gap: 0.25rem; + padding-left: 0.5rem; + border-left: 2px solid var(--rip-border); +} + +.track-group-label { + font-size: 0.78rem; + font-weight: 600; + color: var(--rip-muted); + margin-bottom: 0.1rem; +} + +.track-item { + display: grid; + gap: 0.1rem; + font-size: 0.85rem; +} + .media-title-block { border: 1px solid var(--rip-border); border-radius: 0.45rem; diff --git a/package-lock.json b/package-lock.json index c80e727..920a965 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ripster", - "version": "0.10.2-23", + "version": "0.10.2-24", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ripster", - "version": "0.10.2-23", + "version": "0.10.2-24", "devDependencies": { "concurrently": "^9.1.2" } diff --git a/package.json b/package.json index 3e27643..c56e83b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "ripster", "private": true, - "version": "0.10.2-23", + "version": "0.10.2-24", "scripts": { "dev": "concurrently \"npm run dev --prefix backend\" \"npm run dev --prefix frontend\"", "dev:backend": "npm run dev --prefix backend",