0.10.2-8 Layout

This commit is contained in:
2026-03-16 09:13:54 +00:00
parent ed44d866df
commit 45b2272f1a
10 changed files with 121 additions and 20 deletions
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "ripster-backend", "name": "ripster-backend",
"version": "0.10.2-7", "version": "0.10.2-8",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "ripster-backend", "name": "ripster-backend",
"version": "0.10.2-7", "version": "0.10.2-8",
"dependencies": { "dependencies": {
"archiver": "^7.0.1", "archiver": "^7.0.1",
"cors": "^2.8.5", "cors": "^2.8.5",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "ripster-backend", "name": "ripster-backend",
"version": "0.10.2-7", "version": "0.10.2-8",
"private": true, "private": true,
"type": "commonjs", "type": "commonjs",
"scripts": { "scripts": {
+19
View File
@@ -11562,6 +11562,7 @@ class PipelineService extends EventEmitter {
{ {
contextPatch: { contextPatch: {
outputPath: incompleteOutputPath, outputPath: incompleteOutputPath,
completedChapterCount: index,
currentChapter: { currentChapter: {
index: index + 1, index: index + 1,
total: outputFiles.length, total: outputFiles.length,
@@ -11618,6 +11619,24 @@ class PipelineService extends EventEmitter {
chapterTitle, chapterTitle,
outputPath: entry.outputPath outputPath: entry.outputPath
}); });
await this.updateProgress(
'ENCODING',
endPercent,
null,
`Audiobook-Encoding Kapitel ${index + 1}/${outputFiles.length} abgeschlossen`,
jobId,
{
contextPatch: {
completedChapterCount: index + 1,
currentChapter: {
index: index + 1,
total: outputFiles.length,
title: chapterTitle
}
}
}
);
} }
ffmpegRunInfo = { ffmpegRunInfo = {
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "ripster-frontend", "name": "ripster-frontend",
"version": "0.10.2-7", "version": "0.10.2-8",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "ripster-frontend", "name": "ripster-frontend",
"version": "0.10.2-7", "version": "0.10.2-8",
"dependencies": { "dependencies": {
"primeicons": "^7.0.0", "primeicons": "^7.0.0",
"primereact": "^10.9.2", "primereact": "^10.9.2",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "ripster-frontend", "name": "ripster-frontend",
"version": "0.10.2-7", "version": "0.10.2-8",
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {
@@ -158,8 +158,13 @@ export default function AudiobookConfigPanel({
const schema = AUDIOBOOK_FORMAT_SCHEMAS[format] || AUDIOBOOK_FORMAT_SCHEMAS.mp3; const schema = AUDIOBOOK_FORMAT_SCHEMAS[format] || AUDIOBOOK_FORMAT_SCHEMAS.mp3;
const canStart = Boolean(jobId) && (state === 'READY_TO_START' || state === 'ERROR' || state === 'CANCELLED'); const canStart = Boolean(jobId) && (state === 'READY_TO_START' || state === 'ERROR' || state === 'CANCELLED');
const isRunning = state === 'ENCODING'; const isRunning = state === 'ENCODING';
const isFinished = state === 'FINISHED';
const progress = Number.isFinite(Number(pipeline?.progress)) ? Math.max(0, Math.min(100, Number(pipeline.progress))) : 0; const progress = Number.isFinite(Number(pipeline?.progress)) ? Math.max(0, Math.min(100, Number(pipeline.progress))) : 0;
const outputPath = String(context?.outputPath || '').trim() || null; const outputPath = String(context?.outputPath || '').trim() || null;
const isSplitOutput = format === 'mp3' || format === 'flac';
const currentChapter = context?.currentChapter && typeof context.currentChapter === 'object' ? context.currentChapter : null;
const completedChapterCount = Number(context?.completedChapterCount ?? -1);
const chapterTotal = Number(currentChapter?.total || editableChapters.length || 0);
const statusLabel = getStatusLabel(state); const statusLabel = getStatusLabel(state);
const statusSeverity = getStatusSeverity(state); const statusSeverity = getStatusSeverity(state);
const description = String(metadata?.description || '').trim(); const description = String(metadata?.description || '').trim();
@@ -290,7 +295,29 @@ export default function AudiobookConfigPanel({
{isRunning ? ( {isRunning ? (
<div className="dashboard-job-row-progress" aria-label={`Audiobook Fortschritt ${Math.round(progress)}%`}> <div className="dashboard-job-row-progress" aria-label={`Audiobook Fortschritt ${Math.round(progress)}%`}>
<ProgressBar value={progress} showValue={false} /> <ProgressBar value={progress} showValue={false} />
<small>{Math.round(progress)}%</small> <small>{Math.round(progress)}%{currentChapter ? ` | Kapitel ${currentChapter.index}/${currentChapter.total}: ${currentChapter.title}` : ''}</small>
</div>
) : null}
{isSplitOutput && (isRunning || isFinished) && editableChapters.length > 0 ? (
<div className="audiobook-chapter-status">
<strong>Kapitel-Status ({completedChapterCount >= 0 ? completedChapterCount : (isFinished ? editableChapters.length : 0)}/{chapterTotal || editableChapters.length} fertig)</strong>
<div className="audiobook-chapter-status-list">
{editableChapters.map((chapter, idx) => {
const chIdx = chapter.index || idx + 1;
const isDone = isFinished || (completedChapterCount >= 0 && chIdx <= completedChapterCount);
const isActive = !isDone && currentChapter?.index === chIdx;
const statusIcon = isDone ? 'pi pi-check-circle' : isActive ? 'pi pi-spin pi-spinner' : 'pi pi-circle';
const statusClass = isDone ? 'chapter-status-done' : isActive ? 'chapter-status-active' : 'chapter-status-pending';
return (
<div key={chIdx} className={`audiobook-chapter-status-row ${statusClass}`}>
<i className={statusIcon} />
<span className="chapter-status-nr">#{String(chIdx).padStart(2, '0')}</span>
<span className="chapter-status-title">{chapter.title}</span>
</div>
);
})}
</div>
</div> </div>
) : null} ) : null}
+15 -9
View File
@@ -866,6 +866,7 @@ export default function DashboardPage({
const [insertWaitSeconds, setInsertWaitSeconds] = useState(30); const [insertWaitSeconds, setInsertWaitSeconds] = useState(30);
const toastRef = useRef(null); const toastRef = useRef(null);
const audiobookFileUploadRef = useRef(null); const audiobookFileUploadRef = useRef(null);
const [audiobookUploadStatusVisible, setAudiobookUploadStatusVisible] = useState(false);
const state = String(pipeline?.state || 'IDLE').trim().toUpperCase(); const state = String(pipeline?.state || 'IDLE').trim().toUpperCase();
const currentPipelineJobId = normalizeJobId(pipeline?.activeJobId || pipeline?.context?.jobId); const currentPipelineJobId = normalizeJobId(pipeline?.activeJobId || pipeline?.context?.jobId);
@@ -1123,6 +1124,19 @@ export default function DashboardPage({
setExpandedJobId(normalizeJobId(dashboardJobs[0]?.id)); setExpandedJobId(normalizeJobId(dashboardJobs[0]?.id));
}, [dashboardJobs, expandedJobId, currentPipelineJobId]); }, [dashboardJobs, expandedJobId, currentPipelineJobId]);
useEffect(() => {
if (audiobookUploadPhase === 'idle') {
setAudiobookUploadStatusVisible(false);
return undefined;
}
setAudiobookUploadStatusVisible(true);
if (audiobookUploadPhase === 'completed') {
const timer = setTimeout(() => setAudiobookUploadStatusVisible(false), 5000);
return () => clearTimeout(timer);
}
return undefined;
}, [audiobookUploadPhase]);
useEffect(() => { useEffect(() => {
if (!currentPipelineJobId || !isProcessing) { if (!currentPipelineJobId || !isProcessing) {
setLiveJobLog(''); setLiveJobLog('');
@@ -2531,14 +2545,6 @@ export default function DashboardPage({
<span className="aax-file-name" title={file.name}>{file.name}</span> <span className="aax-file-name" title={file.name}>{file.name}</span>
<small>{options.formatSize}</small> <small>{options.formatSize}</small>
</div> </div>
<Button
label="Hochladen"
icon="pi pi-upload"
size="small"
onClick={() => void handleAudiobookUpload()}
loading={audiobookUploadBusy}
disabled={audiobookUploadBusy}
/>
<Button <Button
icon="pi pi-times" icon="pi pi-times"
text text
@@ -2558,7 +2564,7 @@ export default function DashboardPage({
</div> </div>
)} )}
/> />
{audiobookUploadPhase !== 'idle' ? ( {audiobookUploadStatusVisible ? (
<div className={`audiobook-upload-status tone-${audiobookUploadStatusTone}`}> <div className={`audiobook-upload-status tone-${audiobookUploadStatusTone}`}>
<div className="audiobook-upload-status-head"> <div className="audiobook-upload-status-head">
<strong>{audiobookUploadStatusLabel}</strong> <strong>{audiobookUploadStatusLabel}</strong>
+50 -1
View File
@@ -3802,7 +3802,7 @@ body {
.audiobook-config-grid { .audiobook-config-grid {
display: grid; display: grid;
grid-template-columns: minmax(260px, 340px) minmax(0, 1fr); grid-template-columns: 1fr;
gap: 1rem; gap: 1rem;
} }
@@ -3859,6 +3859,55 @@ body {
gap: 0.55rem; gap: 0.55rem;
} }
.audiobook-chapter-status {
display: grid;
gap: 0.5rem;
margin-top: 0.25rem;
}
.audiobook-chapter-status-list {
display: grid;
gap: 0.25rem;
max-height: 16rem;
overflow: auto;
padding-right: 0.25rem;
}
.audiobook-chapter-status-row {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.3rem 0.5rem;
border-radius: 6px;
font-size: 0.85rem;
}
.chapter-status-done {
background: var(--green-50, #f0fdf4);
color: var(--green-700, #15803d);
}
.chapter-status-active {
background: var(--blue-50, #eff6ff);
color: var(--blue-700, #1d4ed8);
font-weight: 600;
}
.chapter-status-pending {
color: var(--rip-muted, #666);
}
.chapter-status-nr {
font-variant-numeric: tabular-nums;
flex-shrink: 0;
}
.chapter-status-title {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.audiobook-description-dialog p { .audiobook-description-dialog p {
margin: 0; margin: 0;
white-space: pre-wrap; white-space: pre-wrap;
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "ripster", "name": "ripster",
"version": "0.10.2-7", "version": "0.10.2-8",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "ripster", "name": "ripster",
"version": "0.10.2-7", "version": "0.10.2-8",
"devDependencies": { "devDependencies": {
"concurrently": "^9.1.2" "concurrently": "^9.1.2"
} }
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "ripster", "name": "ripster",
"private": true, "private": true,
"version": "0.10.2-7", "version": "0.10.2-8",
"scripts": { "scripts": {
"dev": "concurrently \"npm run dev --prefix backend\" \"npm run dev --prefix frontend\"", "dev": "concurrently \"npm run dev --prefix backend\" \"npm run dev --prefix frontend\"",
"dev:backend": "npm run dev --prefix backend", "dev:backend": "npm run dev --prefix backend",