0.12.0-11 Drive Fix
This commit is contained in:
@@ -23,7 +23,8 @@
|
|||||||
"Bash(python3 -c \"import sys,json; d=json.load\\(sys.stdin\\); [print\\(e\\) for e in d.get\\(''''blockdevices'''',[]\\)]\")",
|
"Bash(python3 -c \"import sys,json; d=json.load\\(sys.stdin\\); [print\\(e\\) for e in d.get\\(''''blockdevices'''',[]\\)]\")",
|
||||||
"Bash(lsblk -o NAME,TYPE,MODEL)",
|
"Bash(lsblk -o NAME,TYPE,MODEL)",
|
||||||
"Bash(node --check /home/michael/ripster/backend/src/routes/pipelineRoutes.js)",
|
"Bash(node --check /home/michael/ripster/backend/src/routes/pipelineRoutes.js)",
|
||||||
"Bash(grep -n \"inferMediaProfile\" /home/michael/ripster/backend/src/services/*.js)"
|
"Bash(grep -n \"inferMediaProfile\" /home/michael/ripster/backend/src/services/*.js)",
|
||||||
|
"Bash(grep -rn \"PROCESS_LOG\\\\|LOG_UPDATE\\\\|processLog\" /home/michael/ripster/backend/src --include=*.js)"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "ripster-backend",
|
"name": "ripster-backend",
|
||||||
"version": "0.12.0-10",
|
"version": "0.12.0-11",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "ripster-backend",
|
"name": "ripster-backend",
|
||||||
"version": "0.12.0-10",
|
"version": "0.12.0-11",
|
||||||
"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.12.0-10",
|
"version": "0.12.0-11",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "commonjs",
|
"type": "commonjs",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "ripster-frontend",
|
"name": "ripster-frontend",
|
||||||
"version": "0.12.0-10",
|
"version": "0.12.0-11",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "ripster-frontend",
|
"name": "ripster-frontend",
|
||||||
"version": "0.12.0-10",
|
"version": "0.12.0-11",
|
||||||
"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.12.0-10",
|
"version": "0.12.0-11",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -455,6 +455,7 @@ function App() {
|
|||||||
|
|
||||||
if (message.type === 'DISC_DETECTED') {
|
if (message.type === 'DISC_DETECTED') {
|
||||||
setLastDiscEvent(message.payload?.device || null);
|
setLastDiscEvent(message.payload?.device || null);
|
||||||
|
refreshPipeline().catch(() => null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.type === 'DISC_REMOVED') {
|
if (message.type === 'DISC_REMOVED') {
|
||||||
|
|||||||
@@ -605,6 +605,10 @@ export default function JobDetailDialog({
|
|||||||
downloadFolderBusyPath = null
|
downloadFolderBusyPath = null
|
||||||
}) {
|
}) {
|
||||||
const mkDone = Boolean(job?.ripSuccessful) || !job?.makemkvInfo || job?.makemkvInfo?.status === 'SUCCESS';
|
const mkDone = Boolean(job?.ripSuccessful) || !job?.makemkvInfo || job?.makemkvInfo?.status === 'SUCCESS';
|
||||||
|
// RAW-Import aus Datenbank, der noch nie encodiert wurde → nur "Neu einlesen" erlaubt
|
||||||
|
const isUnencodedOrphanImport = Boolean(
|
||||||
|
job?.makemkvInfo?.source === 'orphan_raw_import' && !job?.handbrakeInfo
|
||||||
|
);
|
||||||
const running = ['ANALYZING', 'RIPPING', 'MEDIAINFO_CHECK', 'ENCODING', 'CD_RIPPING', 'CD_ENCODING'].includes(job?.status);
|
const running = ['ANALYZING', 'RIPPING', 'MEDIAINFO_CHECK', 'ENCODING', 'CD_RIPPING', 'CD_ENCODING'].includes(job?.status);
|
||||||
const showFinalLog = !running;
|
const showFinalLog = !running;
|
||||||
const mediaType = resolveMediaType(job);
|
const mediaType = resolveMediaType(job);
|
||||||
@@ -1340,7 +1344,7 @@ export default function JobDetailDialog({
|
|||||||
size="small"
|
size="small"
|
||||||
onClick={() => onRestartReview?.(job)}
|
onClick={() => onRestartReview?.(job)}
|
||||||
loading={actionBusy}
|
loading={actionBusy}
|
||||||
disabled={!canRestartReview}
|
disabled={!canRestartReview || isUnencodedOrphanImport}
|
||||||
/>
|
/>
|
||||||
<span className="action-desc">{isAudiobook
|
<span className="action-desc">{isAudiobook
|
||||||
? 'Öffnet die Kapitel-Auswahl erneut, um Kapitel anzupassen bevor der Encode startet.'
|
? 'Öffnet die Kapitel-Auswahl erneut, um Kapitel anzupassen bevor der Encode startet.'
|
||||||
@@ -1354,9 +1358,9 @@ export default function JobDetailDialog({
|
|||||||
icon="pi pi-sync"
|
icon="pi pi-sync"
|
||||||
severity="info"
|
severity="info"
|
||||||
size="small"
|
size="small"
|
||||||
onClick={() => onReencode?.(job)}
|
onClick={() => isUnencodedOrphanImport ? onRestartReview?.(job) : onReencode?.(job)}
|
||||||
loading={reencodeBusy}
|
loading={isUnencodedOrphanImport ? actionBusy : reencodeBusy}
|
||||||
disabled={!canReencode || typeof onReencode !== 'function'}
|
disabled={isUnencodedOrphanImport ? !canRestartReview : (!canReencode || typeof onReencode !== 'function')}
|
||||||
/>
|
/>
|
||||||
<span className="action-desc">{isAudiobook
|
<span className="action-desc">{isAudiobook
|
||||||
? 'Liest die AAX-Datei neu ein und öffnet danach die Kapitel-Auswahl. Sinnvoll wenn sich die Quelldatei geändert hat.'
|
? 'Liest die AAX-Datei neu ein und öffnet danach die Kapitel-Auswahl. Sinnvoll wenn sich die Quelldatei geändert hat.'
|
||||||
|
|||||||
@@ -960,6 +960,19 @@ export default function DashboardPage({
|
|||||||
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);
|
||||||
const isProcessing = processingStates.includes(state);
|
const isProcessing = processingStates.includes(state);
|
||||||
|
// When the pipeline transitions to METADATA_SELECTION for a new job while another job
|
||||||
|
// is still encoding in the background, isProcessing becomes false and the log clears.
|
||||||
|
// Track the background encoding job so its live log keeps polling.
|
||||||
|
const backgroundProcessingJobId = useMemo(() => {
|
||||||
|
if (isProcessing) return null;
|
||||||
|
const bg = dashboardJobs.find((job) => {
|
||||||
|
const s = normalizeStatus(job?.status);
|
||||||
|
return processingStates.includes(s);
|
||||||
|
});
|
||||||
|
return bg ? normalizeJobId(bg.id) : null;
|
||||||
|
}, [isProcessing, dashboardJobs]);
|
||||||
|
const liveLogJobId = backgroundProcessingJobId || currentPipelineJobId;
|
||||||
|
const liveLogActive = isProcessing || Boolean(backgroundProcessingJobId);
|
||||||
const monitoringState = useMemo(
|
const monitoringState = useMemo(
|
||||||
() => normalizeHardwareMonitoringPayload(hardwareMonitoring),
|
() => normalizeHardwareMonitoringPayload(hardwareMonitoring),
|
||||||
[hardwareMonitoring]
|
[hardwareMonitoring]
|
||||||
@@ -1295,7 +1308,7 @@ export default function DashboardPage({
|
|||||||
}, [audiobookUploadPhase]);
|
}, [audiobookUploadPhase]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!currentPipelineJobId || !isProcessing) {
|
if (!liveLogJobId || !liveLogActive) {
|
||||||
setLiveJobLog('');
|
setLiveJobLog('');
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@@ -1303,7 +1316,7 @@ export default function DashboardPage({
|
|||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
const refreshLiveLog = async () => {
|
const refreshLiveLog = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await api.getJob(currentPipelineJobId, { includeLiveLog: true, lite: true });
|
const response = await api.getJob(liveLogJobId, { includeLiveLog: true, lite: true });
|
||||||
if (!cancelled) {
|
if (!cancelled) {
|
||||||
setLiveJobLog(response?.job?.log || '');
|
setLiveJobLog(response?.job?.log || '');
|
||||||
}
|
}
|
||||||
@@ -1318,7 +1331,7 @@ export default function DashboardPage({
|
|||||||
cancelled = true;
|
cancelled = true;
|
||||||
clearInterval(interval);
|
clearInterval(interval);
|
||||||
};
|
};
|
||||||
}, [currentPipelineJobId, isProcessing]);
|
}, [liveLogJobId, liveLogActive]);
|
||||||
|
|
||||||
const pipelineByJobId = useMemo(() => {
|
const pipelineByJobId = useMemo(() => {
|
||||||
const map = new Map();
|
const map = new Map();
|
||||||
@@ -1435,7 +1448,7 @@ export default function DashboardPage({
|
|||||||
await refreshPipeline();
|
await refreshPipeline();
|
||||||
await loadDashboardJobs();
|
await loadDashboardJobs();
|
||||||
const analyzedJobId = normalizeJobId(response?.result?.jobId);
|
const analyzedJobId = normalizeJobId(response?.result?.jobId);
|
||||||
if (analyzedJobId && state === 'ENCODING') {
|
if (analyzedJobId) {
|
||||||
setMetadataDialogContext({
|
setMetadataDialogContext({
|
||||||
jobId: analyzedJobId,
|
jobId: analyzedJobId,
|
||||||
detectedTitle: response?.result?.detectedTitle || '',
|
detectedTitle: response?.result?.detectedTitle || '',
|
||||||
@@ -1465,9 +1478,26 @@ export default function DashboardPage({
|
|||||||
const handleAnalyzeForDrive = async (devicePath) => {
|
const handleAnalyzeForDrive = async (devicePath) => {
|
||||||
setDriveAnalyzeBusy((prev) => new Set([...prev, devicePath]));
|
setDriveAnalyzeBusy((prev) => new Set([...prev, devicePath]));
|
||||||
try {
|
try {
|
||||||
await api.analyzeDisc(devicePath);
|
const response = await api.analyzeDisc(devicePath);
|
||||||
await refreshPipeline();
|
await refreshPipeline();
|
||||||
await loadDashboardJobs();
|
await loadDashboardJobs();
|
||||||
|
const analyzedJobId = normalizeJobId(response?.result?.jobId);
|
||||||
|
if (analyzedJobId) {
|
||||||
|
setMetadataDialogContext({
|
||||||
|
jobId: analyzedJobId,
|
||||||
|
detectedTitle: response?.result?.detectedTitle || '',
|
||||||
|
selectedMetadata: {
|
||||||
|
title: response?.result?.detectedTitle || '',
|
||||||
|
year: null,
|
||||||
|
imdbId: null,
|
||||||
|
poster: null
|
||||||
|
},
|
||||||
|
omdbCandidates: Array.isArray(response?.result?.omdbCandidates)
|
||||||
|
? response.result.omdbCandidates
|
||||||
|
: []
|
||||||
|
});
|
||||||
|
setMetadataDialogVisible(true);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showError(error);
|
showError(error);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -2284,7 +2314,10 @@ export default function DashboardPage({
|
|||||||
}
|
}
|
||||||
if (device?.path) {
|
if (device?.path) {
|
||||||
const base = driveMap.get(device.path) || { path: device.path, model: device.model };
|
const base = driveMap.get(device.path) || { path: device.path, model: device.model };
|
||||||
driveMap.set(device.path, { ...base, pipelineDevice: device, pipelineState: state });
|
// When device comes from a recent DISC_DETECTED event, use 'DISC_DETECTED' as the
|
||||||
|
// effective state for that drive even if the global pipeline state is ENCODING etc.
|
||||||
|
const effectivePipelineState = lastNonCdDiscEvent ? 'DISC_DETECTED' : state;
|
||||||
|
driveMap.set(device.path, { ...base, pipelineDevice: device, pipelineState: effectivePipelineState });
|
||||||
}
|
}
|
||||||
// For drives that the detection service found but aren't tracked by cdDrives or the global
|
// For drives that the detection service found but aren't tracked by cdDrives or the global
|
||||||
// state machine (e.g. a second non-CD disc), show DISC_DETECTED using detectedDiscs.
|
// state machine (e.g. a second non-CD disc), show DISC_DETECTED using detectedDiscs.
|
||||||
@@ -2297,7 +2330,7 @@ export default function DashboardPage({
|
|||||||
return Array.from(driveMap.values())
|
return Array.from(driveMap.values())
|
||||||
.filter((drv) => !String(drv.path || '').startsWith('__virtual__'))
|
.filter((drv) => !String(drv.path || '').startsWith('__virtual__'))
|
||||||
.sort((a, b) => (a.path || '').localeCompare(b.path || ''));
|
.sort((a, b) => (a.path || '').localeCompare(b.path || ''));
|
||||||
}, [knownDrives, pipeline?.cdDrives, pipeline?.detectedDiscs, device, state]);
|
}, [knownDrives, pipeline?.cdDrives, pipeline?.detectedDiscs, device, state, lastNonCdDiscEvent]);
|
||||||
const isDriveActive = driveActiveStates.includes(state);
|
const isDriveActive = driveActiveStates.includes(state);
|
||||||
const canRescan = !isDriveActive;
|
const canRescan = !isDriveActive;
|
||||||
const canOpenMetadataModal = Boolean(defaultMetadataDialogContext?.jobId);
|
const canOpenMetadataModal = Boolean(defaultMetadataDialogContext?.jobId);
|
||||||
@@ -2801,7 +2834,7 @@ export default function DashboardPage({
|
|||||||
const statusBadgeValue = getStatusLabel(job?.status, { queued: isQueued });
|
const statusBadgeValue = getStatusLabel(job?.status, { queued: isQueued });
|
||||||
const statusBadgeSeverity = getStatusSeverity(normalizedStatus, { queued: isQueued });
|
const statusBadgeSeverity = getStatusSeverity(normalizedStatus, { queued: isQueued });
|
||||||
const isExpanded = normalizeJobId(expandedJobId) === jobId;
|
const isExpanded = normalizeJobId(expandedJobId) === jobId;
|
||||||
const isCurrentSession = currentPipelineJobId === jobId && state !== 'IDLE';
|
const isCurrentSession = liveLogJobId === jobId && (state !== 'IDLE' || liveLogActive);
|
||||||
const reviewConfirmed = Boolean(Number(job?.encode_review_confirmed || 0));
|
const reviewConfirmed = Boolean(Number(job?.encode_review_confirmed || 0));
|
||||||
const pipelineForJob = pipelineByJobId.get(jobId) || pipeline;
|
const pipelineForJob = pipelineByJobId.get(jobId) || pipeline;
|
||||||
const jobTitle = job?.title || job?.detected_title || `Job #${jobId}`;
|
const jobTitle = job?.title || job?.detected_title || `Job #${jobId}`;
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "ripster",
|
"name": "ripster",
|
||||||
"version": "0.12.0-10",
|
"version": "0.12.0-11",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "ripster",
|
"name": "ripster",
|
||||||
"version": "0.12.0-10",
|
"version": "0.12.0-11",
|
||||||
"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.12.0-10",
|
"version": "0.12.0-11",
|
||||||
"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