0.10.2-8 Layout
This commit is contained in:
Generated
+2
-2
@@ -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,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": {
|
||||||
|
|||||||
@@ -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 = {
|
||||||
|
|||||||
Generated
+2
-2
@@ -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,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}
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Generated
+2
-2
@@ -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
@@ -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",
|
||||||
|
|||||||
Reference in New Issue
Block a user