0.10.2-14 DVD title Select

This commit is contained in:
2026-03-16 14:30:54 +00:00
parent 2cbbbd9a86
commit ea096cb33c
7 changed files with 84 additions and 41 deletions
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "ripster-backend",
"version": "0.10.2-13",
"version": "0.10.2-14",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ripster-backend",
"version": "0.10.2-13",
"version": "0.10.2-14",
"dependencies": {
"archiver": "^7.0.1",
"cors": "^2.8.5",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "ripster-backend",
"version": "0.10.2-13",
"version": "0.10.2-14",
"private": true,
"type": "commonjs",
"scripts": {
+48 -5
View File
@@ -1449,6 +1449,28 @@ function pickScanTitleList(scanJson) {
return alt || [];
}
// Returns the HandBrake-selected main feature title ID (the "1 valid title" HandBrake reports).
// Present in the JSON as MainFeature at the top level or inside the Scan node.
function pickScanMainFeatureTitleId(scanJson) {
if (!scanJson || typeof scanJson !== 'object') {
return null;
}
const direct = scanJson.MainFeature ?? scanJson.mainFeature ?? null;
if (direct != null) {
const id = Number(direct);
return Number.isFinite(id) && id > 0 ? id : null;
}
const scanNode = scanJson.Scan && typeof scanJson.Scan === 'object' ? scanJson.Scan : null;
if (scanNode) {
const nested = scanNode.MainFeature ?? scanNode.mainFeature ?? null;
if (nested != null) {
const id = Number(nested);
return Number.isFinite(id) && id > 0 ? id : null;
}
}
return null;
}
function resolvePlaylistInfoFromAnalysis(playlistAnalysis, playlistIdRaw) {
const playlistId = normalizePlaylistId(playlistIdRaw);
if (!playlistId || !playlistAnalysis) {
@@ -2094,6 +2116,14 @@ function parseHandBrakeSelectedTitleInfo(scanJson, options = {}) {
.sort((a, b) => b.durationSeconds - a.durationSeconds || b.sizeBytes - a.sizeBytes || a.handBrakeTitleId - b.handBrakeTitleId);
selected = playlistMatches[0] || null;
}
// Use HandBrake's own MainFeature designation before falling back to heuristics.
// This is exactly the "1 valid title" HandBrake reports in text output.
if (!selected) {
const mainFeatureId = pickScanMainFeatureTitleId(scanJson);
if (mainFeatureId) {
selected = parsedTitles.find((title) => title.handBrakeTitleId === mainFeatureId) || null;
}
}
if (!selected) {
selected = parsedTitles
.slice()
@@ -8193,6 +8223,7 @@ class PipelineService extends EventEmitter {
|| rawMedia.source === 'single_extensionless';
let dvdHandBrakeScanJson = null;
let dvdHandBrakeScanInputPath = null;
let dvdSelectedTitleInfo = null; // set when HandBrake scan succeeds; used to skip re-selection
if (isDvdSource && mediaFiles.length > 0) {
// For 'dvd': scan the folder that contains VIDEO_TS (parent of the VIDEO_TS dir).
@@ -8234,6 +8265,7 @@ class PipelineService extends EventEmitter {
if (dvdHandBrakeScanJson) {
const titleInfo = parseHandBrakeSelectedTitleInfo(dvdHandBrakeScanJson);
if (titleInfo) {
dvdSelectedTitleInfo = titleInfo;
const syntheticMediaInfo = buildSyntheticMediaInfoFromMakeMkvTitle(titleInfo);
mediaInfoByPath[dvdHandBrakeScanInputPath] = syntheticMediaInfo;
effectiveMediaFiles = [{ path: dvdHandBrakeScanInputPath, size: 0 }];
@@ -8415,11 +8447,23 @@ class PipelineService extends EventEmitter {
'SYSTEM',
`HandBrake Titel-Auswahl (manuell) übernommen: -t ${preSelectedHandBrakeTitleId}`
);
} else if (dvdSelectedTitleInfo) {
// HandBrake already identified the valid title during the DVD analysis scan above.
// Use it directly — no need to re-parse all titles from TitleList, which would
// include short/invalid titles and incorrectly trigger manual title selection.
enrichedReview = {
...enrichedReview,
handBrakeTitleId: dvdSelectedTitleInfo.handBrakeTitleId,
encodeInputPath: dvdScanInputPath
};
await historyService.appendLog(
jobId,
'SYSTEM',
`HandBrake DVD Titel automatisch gewählt (aus Scan): -t ${dvdSelectedTitleInfo.handBrakeTitleId}`
);
} else {
// Reuse the HandBrake scan result from the DVD analysis step when available,
// otherwise run a fresh scan for title selection.
let dvdTitleScanJson = dvdHandBrakeScanJson || null;
if (!dvdTitleScanJson) {
// No pre-scanned result available — run a fresh HandBrake scan for title selection.
let dvdTitleScanJson = null;
await historyService.appendLog(
jobId,
'SYSTEM',
@@ -8447,7 +8491,6 @@ class PipelineService extends EventEmitter {
collectStderrLines: false
});
dvdTitleScanJson = parseMediainfoJsonOutput(dvdTitleScanLines.join('\n'));
}
if (dvdTitleScanJson) {
const allTitles = parseHandBrakeTitleList(dvdTitleScanJson);
const minLengthMinutes = Number(settings?.makemkv_min_length_minutes || 0);
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "ripster-frontend",
"version": "0.10.2-13",
"version": "0.10.2-14",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ripster-frontend",
"version": "0.10.2-13",
"version": "0.10.2-14",
"dependencies": {
"primeicons": "^7.0.0",
"primereact": "^10.9.2",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "ripster-frontend",
"version": "0.10.2-13",
"version": "0.10.2-14",
"private": true,
"type": "module",
"scripts": {
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "ripster",
"version": "0.10.2-13",
"version": "0.10.2-14",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ripster",
"version": "0.10.2-13",
"version": "0.10.2-14",
"devDependencies": {
"concurrently": "^9.1.2"
}
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "ripster",
"private": true,
"version": "0.10.2-13",
"version": "0.10.2-14",
"scripts": {
"dev": "concurrently \"npm run dev --prefix backend\" \"npm run dev --prefix frontend\"",
"dev:backend": "npm run dev --prefix backend",