0.11.0 Multi LW

This commit is contained in:
2026-03-17 19:58:32 +00:00
parent 16443c8a45
commit 16c783e1aa
25 changed files with 811 additions and 960 deletions
+97 -12
View File
@@ -1036,23 +1036,23 @@ export default function DashboardPage({
}
}, [pipeline?.state, metadataDialogVisible, metadataDialogContext?.jobId]);
// Auto-open CD metadata dialog when pipeline enters CD_METADATA_SELECTION
// Auto-open CD metadata dialog when any drive enters CD_METADATA_SELECTION
useEffect(() => {
const currentState = String(pipeline?.state || '').trim().toUpperCase();
if (currentState === 'CD_METADATA_SELECTION') {
const ctx = pipeline?.context && typeof pipeline.context === 'object' ? pipeline.context : null;
if (ctx?.jobId && !cdMetadataDialogVisible) {
const cdDrives = pipeline?.cdDrives;
if (!cdDrives || typeof cdDrives !== 'object') return;
for (const drive of Object.values(cdDrives)) {
const driveState = String(drive?.state || '').trim().toUpperCase();
const ctx = drive?.context && typeof drive.context === 'object' ? drive.context : null;
if (driveState === 'CD_METADATA_SELECTION' && ctx?.jobId && !cdMetadataDialogVisible) {
setCdMetadataDialogContext(ctx);
setCdMetadataDialogVisible(true);
break;
}
}
if (currentState === 'CD_READY_TO_RIP') {
const ctx = pipeline?.context && typeof pipeline.context === 'object' ? pipeline.context : null;
if (ctx?.jobId) {
if (driveState === 'CD_READY_TO_RIP' && ctx?.jobId) {
setCdRipPanelJobId(ctx.jobId);
}
}
}, [pipeline?.state, pipeline?.context?.jobId]);
}, [pipeline?.cdDrives]);
useEffect(() => {
setQueueState(normalizeQueue(pipeline?.queue));
@@ -1314,6 +1314,19 @@ export default function DashboardPage({
await handleAnalyze();
};
const handleAnalyzeForDrive = async (devicePath) => {
setBusy(true);
try {
await api.analyzeDisc(devicePath);
await refreshPipeline();
await loadDashboardJobs();
} catch (error) {
showError(error);
} finally {
setBusy(false);
}
};
const handleRescan = async () => {
setBusy(true);
try {
@@ -1969,7 +1982,11 @@ export default function DashboardPage({
}
};
const device = lastDiscEvent || pipeline?.context?.device;
// CD drives are tracked per-drive in pipeline.cdDrives — exclude them from the single-drive display
const lastNonCdDiscEvent = lastDiscEvent?.mediaProfile !== 'cd' ? lastDiscEvent : null;
const contextDevice = pipeline?.context?.device;
const device = lastNonCdDiscEvent || (contextDevice?.mediaProfile !== 'cd' ? contextDevice : null);
const cdDriveEntries = Object.entries(pipeline?.cdDrives || {});
const isDriveActive = driveActiveStates.includes(state);
const canRescan = !isDriveActive;
const canReanalyze = !isDriveActive && (state === 'ENCODING' ? Boolean(device) : !processingStates.includes(state));
@@ -2236,6 +2253,74 @@ export default function DashboardPage({
disabled={!canOpenMetadataModal}
/>
</div>
{/* Per-drive CD sections */}
{cdDriveEntries.length > 0 && (
<div className="cd-drives-section">
{cdDriveEntries.map(([drivePath, drive]) => {
const driveState = String(drive?.state || '').trim().toUpperCase();
const driveDevice = drive?.device || {};
const driveJobId = normalizeJobId(drive?.jobId);
const driveCtx = drive?.context && typeof drive.context === 'object' ? drive.context : {};
const cdStateSeverity = driveState === 'FINISHED' ? 'success'
: driveState === 'ERROR' || driveState === 'CANCELLED' ? 'danger'
: ['CD_RIPPING', 'CD_ENCODING'].includes(driveState) ? 'warning'
: 'info';
return (
<div key={drivePath} className="cd-drive-item">
<div className="cd-drive-header">
<strong>{driveDevice.model || 'CD-Laufwerk'}</strong>
<code className="cd-drive-path">{drivePath}</code>
<Tag value={driveState} severity={cdStateSeverity} />
</div>
{driveDevice.discLabel && (
<div className="cd-drive-disc-label">
<strong>Disc:</strong> {driveDevice.discLabel}
</div>
)}
<div className="cd-drive-actions">
{driveState === 'DISC_DETECTED' && (
<Button
label="CD analysieren"
icon="pi pi-search"
size="small"
onClick={() => handleAnalyzeForDrive(drivePath)}
loading={busy}
disabled={busy}
/>
)}
{driveState === 'CD_METADATA_SELECTION' && (
<Button
label="Metadaten auswählen"
icon="pi pi-list"
size="small"
severity="info"
onClick={() => {
setCdMetadataDialogContext({ ...driveCtx, jobId: driveJobId });
setCdMetadataDialogVisible(true);
}}
disabled={!driveJobId}
/>
)}
{(driveState === 'ERROR' || driveState === 'CANCELLED') && driveJobId && (
<Button
label="Erneut analysieren"
icon="pi pi-refresh"
size="small"
severity="warning"
onClick={() => handleAnalyzeForDrive(drivePath)}
loading={busy}
disabled={busy}
/>
)}
</div>
</div>
);
})}
</div>
)}
{/* Non-CD / legacy single-drive display */}
<div className="dashboard-drive-state">
{device
? <Tag value="Disk eingelegt" severity="success" icon="pi pi-circle-fill" />
@@ -2260,7 +2345,7 @@ export default function DashboardPage({
</div>
</div>
) : (
<p>Aktuell keine Disk erkannt.</p>
cdDriveEntries.length === 0 ? <p>Aktuell keine Disk erkannt.</p> : null
)}
</Card>