final dev
This commit is contained in:
@@ -14,6 +14,7 @@ Ripster ist eine lokale Web-Anwendung für halbautomatisches Disc-Ripping mit Ma
|
||||
- Pre- und Post-Encode-Ausführungen (Skripte und/oder Skript-Ketten)
|
||||
- Pipeline-Queue mit Job- und Nicht-Job-Einträgen (`script`, `chain`, `wait`)
|
||||
- Cron-Jobs für Skripte/Ketten (inkl. Logs und manueller Auslösung)
|
||||
- **Aktivitäts-Tracking**: Laufende und abgeschlossene Aktionen (Skripte, Ketten, Cron, Tasks) in Echtzeit im Dashboard
|
||||
- Historie mit Re-Encode, Review-Neustart, File-/Job-Löschung und Orphan-Import
|
||||
- Hardware-Monitoring (CPU/RAM/GPU/Storage) im Dashboard
|
||||
|
||||
@@ -190,6 +191,12 @@ ripster/
|
||||
- `POST /api/crons/:id/run`
|
||||
- `POST /api/crons/validate-expression`
|
||||
|
||||
**Runtime-Aktivitäten**
|
||||
- `GET /api/activities`
|
||||
- `POST /api/activities/:id/cancel`
|
||||
- `POST /api/activities/:id/next-step`
|
||||
- `POST /api/activities/clear-recent`
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- WebSocket verbindet nicht:
|
||||
|
||||
@@ -2172,6 +2172,18 @@ function isPathInsideDirectory(parentPath, candidatePath) {
|
||||
return candidate.startsWith(parentWithSep);
|
||||
}
|
||||
|
||||
function isEncodeInputMismatchedWithRaw(rawPath, encodeInputPath) {
|
||||
const raw = normalizeComparablePath(rawPath);
|
||||
const input = normalizeComparablePath(encodeInputPath);
|
||||
if (!raw || !input) {
|
||||
return true;
|
||||
}
|
||||
if (raw === input) {
|
||||
return false;
|
||||
}
|
||||
return !isPathInsideDirectory(raw, input);
|
||||
}
|
||||
|
||||
function isJobFinished(jobLike = null) {
|
||||
const status = String(jobLike?.status || '').trim().toUpperCase();
|
||||
const lastState = String(jobLike?.last_state || '').trim().toUpperCase();
|
||||
@@ -9123,10 +9135,10 @@ class PipelineService extends EventEmitter {
|
||||
throw error;
|
||||
}
|
||||
|
||||
const hasRawInput = Boolean(
|
||||
hasBluRayBackupStructure(resolvedReviewRawPath)
|
||||
|| findPreferredRawInput(resolvedReviewRawPath)
|
||||
);
|
||||
const resolvedReviewInput = hasBluRayBackupStructure(resolvedReviewRawPath)
|
||||
? { path: resolvedReviewRawPath }
|
||||
: findPreferredRawInput(resolvedReviewRawPath);
|
||||
const hasRawInput = Boolean(resolvedReviewInput?.path);
|
||||
if (!hasRawInput) {
|
||||
let hasAnyRawEntries = false;
|
||||
try {
|
||||
@@ -9146,6 +9158,19 @@ class PipelineService extends EventEmitter {
|
||||
);
|
||||
}
|
||||
|
||||
const existingEncodeInputPath = String(sourceJob.encode_input_path || '').trim() || null;
|
||||
const shouldRealignEncodeInput = Boolean(
|
||||
resolvedReviewInput?.path
|
||||
&& (
|
||||
!existingEncodeInputPath
|
||||
|| !fs.existsSync(existingEncodeInputPath)
|
||||
|| isEncodeInputMismatchedWithRaw(resolvedReviewRawPath, existingEncodeInputPath)
|
||||
)
|
||||
);
|
||||
const normalizedReviewInputPath = shouldRealignEncodeInput
|
||||
? resolvedReviewInput.path
|
||||
: existingEncodeInputPath;
|
||||
|
||||
const currentStatus = String(sourceJob.status || '').trim().toUpperCase();
|
||||
if (['ANALYZING', 'RIPPING', 'MEDIAINFO_CHECK', 'ENCODING'].includes(currentStatus)) {
|
||||
const error = new Error(`Review-Neustart nicht möglich: Job ${jobId} ist noch aktiv (${currentStatus}).`);
|
||||
@@ -9194,7 +9219,7 @@ class PipelineService extends EventEmitter {
|
||||
handbrake_info_json: null,
|
||||
mediainfo_info_json: null,
|
||||
encode_plan_json: null,
|
||||
encode_input_path: null,
|
||||
encode_input_path: normalizedReviewInputPath || null,
|
||||
encode_review_confirmed: 0,
|
||||
makemkv_info_json: nextMakemkvInfoJson
|
||||
};
|
||||
@@ -9202,6 +9227,13 @@ class PipelineService extends EventEmitter {
|
||||
jobUpdatePayload.raw_path = resolvedReviewRawPath;
|
||||
}
|
||||
await historyService.updateJob(jobId, jobUpdatePayload);
|
||||
if (shouldRealignEncodeInput) {
|
||||
await historyService.appendLog(
|
||||
jobId,
|
||||
'SYSTEM',
|
||||
`Review-Neustart: Encode-Input auf aktuellen RAW-Pfad abgeglichen: ${existingEncodeInputPath || '-'} -> ${normalizedReviewInputPath}`
|
||||
);
|
||||
}
|
||||
await historyService.appendLog(
|
||||
jobId,
|
||||
'USER_ACTION',
|
||||
|
||||
@@ -204,6 +204,44 @@ Laufzeitstatus eines Cron-Jobs geändert.
|
||||
}
|
||||
```
|
||||
|
||||
### RUNTIME_ACTIVITY_CHANGED
|
||||
|
||||
Vollständiger Snapshot aller laufenden und kürzlich abgeschlossenen Aktivitäten.
|
||||
|
||||
Wird ausgelöst, wenn eine Aktivität gestartet, aktualisiert oder abgeschlossen wird sowie nach `clear-recent`.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "RUNTIME_ACTIVITY_CHANGED",
|
||||
"payload": {
|
||||
"active": [
|
||||
{
|
||||
"id": 7,
|
||||
"type": "chain",
|
||||
"name": "Post-Encode Aufräumen",
|
||||
"status": "running",
|
||||
"source": "cron",
|
||||
"message": "Schritt 2 von 3",
|
||||
"currentStep": "cleanup.sh",
|
||||
"currentStepType": "script",
|
||||
"stepIndex": 2,
|
||||
"stepTotal": 3,
|
||||
"canCancel": true,
|
||||
"canNextStep": false,
|
||||
"outcome": "running",
|
||||
"startedAt": "2026-03-10T10:00:00.000Z",
|
||||
"finishedAt": null,
|
||||
"durationMs": null
|
||||
}
|
||||
],
|
||||
"recent": [],
|
||||
"updatedAt": "2026-03-10T10:00:05.000Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Vollständige Feldbeschreibung: [Runtime Activities API](runtime-activities.md).
|
||||
|
||||
---
|
||||
|
||||
## Reconnect-Verhalten
|
||||
|
||||
@@ -91,6 +91,31 @@ Features:
|
||||
|
||||
---
|
||||
|
||||
## `runtimeActivityService.js`
|
||||
|
||||
In-Memory-Tracking aller laufenden und kürzlich abgeschlossenen Aktivitäten (Skripte, Ketten, Cron-Jobs, Tasks).
|
||||
|
||||
Features:
|
||||
|
||||
- `startActivity(type, payload)` → Aktivität registrieren, ID zurückgeben
|
||||
- `updateActivity(id, patch)` → Laufende Aktivität aktualisieren
|
||||
- `completeActivity(id, payload)` → Aktivität abschließen und in `recent` verschieben
|
||||
- `setControls(id, { cancel, nextStep })` → Steuer-Handler registrieren (für `canCancel`/`canNextStep`)
|
||||
- `requestCancel(id)` / `requestNextStep(id)` → Steuer-Handler aufrufen
|
||||
- `clearRecent()` → Abgeschlossene Aktivitäten löschen
|
||||
- `getSnapshot()` → Snapshot mit `active` + `recent` + `updatedAt`
|
||||
- Broadcasts `RUNTIME_ACTIVITY_CHANGED` über WebSocket bei jeder Änderung
|
||||
|
||||
Limits:
|
||||
|
||||
- `recent` max. 120 Einträge
|
||||
- `stdout`/`stderr`/`output` max. 12.000 Zeichen
|
||||
- `message`/`errorMessage` max. 2.000 Zeichen
|
||||
|
||||
Vollständige API-Dokumentation: [Runtime Activities API](../api/runtime-activities.md)
|
||||
|
||||
---
|
||||
|
||||
## Weitere Services
|
||||
|
||||
- `scriptService.js` (CRUD + Test + Wrapper-Ausführung)
|
||||
|
||||
@@ -69,6 +69,7 @@ nav:
|
||||
- Settings API: api/settings.md
|
||||
- History API: api/history.md
|
||||
- Cron API: api/crons.md
|
||||
- Runtime Activities API: api/runtime-activities.md
|
||||
- WebSocket Events: api/websocket.md
|
||||
- Konfiguration:
|
||||
- configuration/index.md
|
||||
|
||||
Reference in New Issue
Block a user