0.12.0-16 misc fixes
This commit is contained in:
+519
-15
@@ -3,6 +3,16 @@ const { splitArgs } = require('./commandLine');
|
||||
|
||||
const DEFAULT_AUDIO_COPY_MASK = ['aac', 'ac3', 'eac3', 'truehd', 'dts', 'dtshd', 'mp3', 'flac'];
|
||||
const DEFAULT_AUDIO_FALLBACK = 'av_aac';
|
||||
const SUBTITLE_CONFIDENCE_SCORES = Object.freeze({
|
||||
low: 1,
|
||||
medium: 2,
|
||||
high: 3
|
||||
});
|
||||
const FORCED_SUBTITLE_EVENT_RATIO_THRESHOLD = 0.35;
|
||||
const FORCED_SUBTITLE_SIZE_RATIO_THRESHOLD = 0.35;
|
||||
const FORCED_SUBTITLE_MAX_EVENT_COUNT = 220;
|
||||
const FORCED_SUBTITLE_MIN_EVENT_GAP = 12;
|
||||
const FORCED_SUBTITLE_MIN_SIZE_GAP_BYTES = 64 * 1024;
|
||||
const ISO2_TO_3_LANGUAGE = {
|
||||
de: 'deu',
|
||||
en: 'eng',
|
||||
@@ -271,7 +281,7 @@ function parseMediaInfoFile(mediaInfoJson, fileInfo, index) {
|
||||
channels: item?.Channels || item?.Channel_s_ || null
|
||||
}));
|
||||
|
||||
const subtitleTracks = tracks
|
||||
const subtitleTracksRaw = tracks
|
||||
.filter((item) => {
|
||||
const type = String(item?.['@type'] || '').toLowerCase();
|
||||
return type === 'text' || type === 'subtitle';
|
||||
@@ -282,8 +292,14 @@ function parseMediaInfoFile(mediaInfoJson, fileInfo, index) {
|
||||
language: normalizeLanguage(item?.Language || item?.Language_String3 || item?.Language_String || 'und'),
|
||||
languageLabel: item?.Language_String3 || item?.Language || item?.Language_String || 'und',
|
||||
title: item?.Title || null,
|
||||
format: item?.Format || null
|
||||
format: item?.Format || null,
|
||||
defaultFlag: parseBooleanFlag(item?.Default ?? item?.Default_String ?? item?.IsDefault ?? item?.isDefault),
|
||||
forcedFlag: parseBooleanFlagNullable(item?.Forced ?? item?.Forced_String ?? item?.IsForced ?? item?.isForced),
|
||||
sdhFlag: parseSubtitleSdhFlag(item),
|
||||
eventCount: parseSubtitleEventCount(item),
|
||||
streamSizeBytes: parseSubtitleStreamSizeBytes(item)
|
||||
}));
|
||||
const subtitleTracks = annotateSubtitleTracks(subtitleTracksRaw);
|
||||
|
||||
const videoTracks = tracks
|
||||
.filter((item) => String(item?.['@type'] || '').toLowerCase() === 'video')
|
||||
@@ -350,6 +366,467 @@ function parseTrackIdList(raw) {
|
||||
.filter((item) => Number.isFinite(item));
|
||||
}
|
||||
|
||||
function parseBooleanFlag(value) {
|
||||
return parseBooleanFlagNullable(value) === true;
|
||||
}
|
||||
|
||||
function parseBooleanFlagNullable(value) {
|
||||
if (typeof value === 'boolean') {
|
||||
return value;
|
||||
}
|
||||
if (typeof value === 'number') {
|
||||
return value === 1;
|
||||
}
|
||||
const raw = String(value || '').trim().toLowerCase();
|
||||
if (!raw) {
|
||||
return null;
|
||||
}
|
||||
if (raw === 'yes' || raw === 'true' || raw === '1') {
|
||||
return true;
|
||||
}
|
||||
if (raw === 'no' || raw === 'false' || raw === '0') {
|
||||
return false;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function parseSubtitleForcedFlag(track) {
|
||||
if (!track || typeof track !== 'object') {
|
||||
return null;
|
||||
}
|
||||
const candidates = [
|
||||
track?.forcedFlag,
|
||||
track?.forced,
|
||||
track?.Forced,
|
||||
track?.isForced,
|
||||
track?.IsForced,
|
||||
track?.forced_only,
|
||||
track?.forcedOnly,
|
||||
track?.Attributes?.Forced
|
||||
];
|
||||
for (const candidate of candidates) {
|
||||
const parsed = parseBooleanFlagNullable(candidate);
|
||||
if (parsed === true || parsed === false) {
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function parseSubtitleSdhFlag(track) {
|
||||
if (!track || typeof track !== 'object') {
|
||||
return null;
|
||||
}
|
||||
const candidates = [
|
||||
track?.sdhFlag,
|
||||
track?.hearingImpaired,
|
||||
track?.HearingImpaired,
|
||||
track?.isHearingImpaired,
|
||||
track?.IsHearingImpaired,
|
||||
track?.Hearing_Impaired,
|
||||
track?.closedCaptions,
|
||||
track?.ClosedCaptions,
|
||||
track?.Attributes?.HearingImpaired,
|
||||
track?.Attributes?.ClosedCaptions
|
||||
];
|
||||
for (const candidate of candidates) {
|
||||
const parsed = parseBooleanFlagNullable(candidate);
|
||||
if (parsed === true || parsed === false) {
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
|
||||
const serviceKind = String(track?.serviceKind || track?.ServiceKind || '').trim().toLowerCase();
|
||||
if (!serviceKind) {
|
||||
return null;
|
||||
}
|
||||
if (serviceKind.includes('sdh') || serviceKind.includes('cc') || serviceKind.includes('hearing')) {
|
||||
return true;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function parseSubtitleEventCount(track) {
|
||||
const candidates = [
|
||||
track?.CountOfEvents,
|
||||
track?.countOfEvents,
|
||||
track?.EventCount,
|
||||
track?.eventCount,
|
||||
track?.ElementCount,
|
||||
track?.elementCount
|
||||
];
|
||||
for (const candidate of candidates) {
|
||||
const numeric = Number(candidate);
|
||||
if (Number.isFinite(numeric) && numeric >= 0) {
|
||||
return Math.trunc(numeric);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function parseSubtitleStreamSizeBytes(track) {
|
||||
const numericCandidates = [
|
||||
track?.StreamSize,
|
||||
track?.streamSize,
|
||||
track?.StreamSize_Original,
|
||||
track?.streamSizeOriginal,
|
||||
track?.Bytes,
|
||||
track?.bytes
|
||||
];
|
||||
for (const candidate of numericCandidates) {
|
||||
const numeric = Number(candidate);
|
||||
if (Number.isFinite(numeric) && numeric > 0) {
|
||||
return Math.trunc(numeric);
|
||||
}
|
||||
}
|
||||
|
||||
const textCandidates = [
|
||||
track?.StreamSize_String,
|
||||
track?.streamSizeString,
|
||||
track?.StreamSize_Original_String,
|
||||
track?.streamSizeOriginalString,
|
||||
track?.Size_String,
|
||||
track?.sizeString
|
||||
];
|
||||
for (const candidate of textCandidates) {
|
||||
const text = String(candidate || '').trim();
|
||||
if (!text) {
|
||||
continue;
|
||||
}
|
||||
const match = text.match(/([0-9]+(?:[.,][0-9]+)?)\s*([kmgt]?b)/i);
|
||||
if (!match) {
|
||||
continue;
|
||||
}
|
||||
const value = Number(String(match[1]).replace(',', '.'));
|
||||
if (!Number.isFinite(value) || value <= 0) {
|
||||
continue;
|
||||
}
|
||||
const unit = String(match[2] || 'b').toLowerCase();
|
||||
const factorByUnit = {
|
||||
b: 1,
|
||||
kb: 1024,
|
||||
mb: 1024 ** 2,
|
||||
gb: 1024 ** 3,
|
||||
tb: 1024 ** 4
|
||||
};
|
||||
const factor = factorByUnit[unit] || 1;
|
||||
return Math.max(0, Math.trunc(value * factor));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function normalizeSubtitleConfidence(raw) {
|
||||
const value = String(raw || '').trim().toLowerCase();
|
||||
if (value === 'high' || value === 'medium' || value === 'low') {
|
||||
return value;
|
||||
}
|
||||
return 'low';
|
||||
}
|
||||
|
||||
function subtitleConfidenceScore(raw) {
|
||||
return SUBTITLE_CONFIDENCE_SCORES[normalizeSubtitleConfidence(raw)] || 0;
|
||||
}
|
||||
|
||||
function collectSubtitleText(track) {
|
||||
return [
|
||||
track?.title,
|
||||
track?.description,
|
||||
track?.name,
|
||||
track?.format,
|
||||
track?.label
|
||||
]
|
||||
.map((value) => String(value || '').trim().toLowerCase())
|
||||
.filter(Boolean)
|
||||
.join(' ');
|
||||
}
|
||||
|
||||
function isLikelyBitmapSubtitleFormat(track) {
|
||||
const text = [
|
||||
track?.format,
|
||||
track?.codec,
|
||||
track?.codecName,
|
||||
track?.title,
|
||||
track?.description,
|
||||
track?.name,
|
||||
track?.label
|
||||
]
|
||||
.map((value) => String(value || '').trim().toLowerCase())
|
||||
.filter(Boolean)
|
||||
.join(' ');
|
||||
if (!text) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
/\bpgs\b/.test(text)
|
||||
|| /\bhdmv\b/.test(text)
|
||||
|| /\bsup\b/.test(text)
|
||||
|| /\bvobsub\b/.test(text)
|
||||
|| /\bdvd[-_\s]?sub/.test(text)
|
||||
|| /\bdvb[-_\s]?sub/.test(text)
|
||||
);
|
||||
}
|
||||
|
||||
function isLikelySdhSubtitleTrack(track) {
|
||||
const text = collectSubtitleText(track);
|
||||
if (!text) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
/\bsdh\b/.test(text)
|
||||
|| /\bcc\b/.test(text)
|
||||
|| /\bhoh\b/.test(text)
|
||||
|| /\bcaptions?\b/.test(text)
|
||||
|| /hard[-\s]?of[-\s]?hearing/.test(text)
|
||||
|| /hearing\s+impaired/.test(text)
|
||||
|| /(?:gehörlos|hoergeschaedigt|hörgeschädigt)/.test(text)
|
||||
);
|
||||
}
|
||||
|
||||
function isLikelyForcedSubtitleTrack(track) {
|
||||
const text = collectSubtitleText(track);
|
||||
if (!text) {
|
||||
return false;
|
||||
}
|
||||
if (isLikelySdhSubtitleTrack(track) || /\bnot forced\b/.test(text)) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
/\bforced(?:\s+only)?\b/.test(text)
|
||||
|| /nur\s+erzwungen/.test(text)
|
||||
|| /\berzwungen\b/.test(text)
|
||||
);
|
||||
}
|
||||
|
||||
function compareSubtitleTracksByForcedHeuristic(a, b) {
|
||||
const aSdh = Number(Boolean(a?.sdhLikely));
|
||||
const bSdh = Number(Boolean(b?.sdhLikely));
|
||||
if (aSdh !== bSdh) {
|
||||
return aSdh - bSdh;
|
||||
}
|
||||
|
||||
const aDefault = Number(Boolean(a?.defaultFlag));
|
||||
const bDefault = Number(Boolean(b?.defaultFlag));
|
||||
if (aDefault !== bDefault) {
|
||||
return aDefault - bDefault;
|
||||
}
|
||||
|
||||
const aEventKnown = Number.isFinite(a?.eventCount) ? 0 : 1;
|
||||
const bEventKnown = Number.isFinite(b?.eventCount) ? 0 : 1;
|
||||
if (aEventKnown !== bEventKnown) {
|
||||
return aEventKnown - bEventKnown;
|
||||
}
|
||||
if (aEventKnown === 0 && a.eventCount !== b.eventCount) {
|
||||
return a.eventCount - b.eventCount;
|
||||
}
|
||||
|
||||
const aSizeKnown = Number.isFinite(a?.streamSizeBytes) ? 0 : 1;
|
||||
const bSizeKnown = Number.isFinite(b?.streamSizeBytes) ? 0 : 1;
|
||||
if (aSizeKnown !== bSizeKnown) {
|
||||
return aSizeKnown - bSizeKnown;
|
||||
}
|
||||
if (aSizeKnown === 0 && a.streamSizeBytes !== b.streamSizeBytes) {
|
||||
return a.streamSizeBytes - b.streamSizeBytes;
|
||||
}
|
||||
|
||||
const aTrackId = Number.isFinite(a?.id) && a.id > 0 ? a.id : Number.MAX_SAFE_INTEGER;
|
||||
const bTrackId = Number.isFinite(b?.id) && b.id > 0 ? b.id : Number.MAX_SAFE_INTEGER;
|
||||
if (aTrackId !== bTrackId) {
|
||||
return aTrackId - bTrackId;
|
||||
}
|
||||
return a.originalIndex - b.originalIndex;
|
||||
}
|
||||
|
||||
function compareSubtitleTracksForDedup(a, b) {
|
||||
const aSdh = Number(Boolean(a?.sdhLikely));
|
||||
const bSdh = Number(Boolean(b?.sdhLikely));
|
||||
if (aSdh !== bSdh) {
|
||||
return aSdh - bSdh;
|
||||
}
|
||||
|
||||
const defaultDiff = Number(Boolean(b?.defaultFlag)) - Number(Boolean(a?.defaultFlag));
|
||||
if (defaultDiff !== 0) {
|
||||
return defaultDiff;
|
||||
}
|
||||
const confidenceDiff = subtitleConfidenceScore(b?.sourceConfidence) - subtitleConfidenceScore(a?.sourceConfidence);
|
||||
if (confidenceDiff !== 0) {
|
||||
return confidenceDiff;
|
||||
}
|
||||
const aTrackId = Number.isFinite(a?.id) && a.id > 0 ? a.id : Number.MAX_SAFE_INTEGER;
|
||||
const bTrackId = Number.isFinite(b?.id) && b.id > 0 ? b.id : Number.MAX_SAFE_INTEGER;
|
||||
if (aTrackId !== bTrackId) {
|
||||
return aTrackId - bTrackId;
|
||||
}
|
||||
return a.originalIndex - b.originalIndex;
|
||||
}
|
||||
|
||||
function isHeuristicForcedSubtitleCandidate(languageEntries, candidate) {
|
||||
if (!candidate) {
|
||||
return false;
|
||||
}
|
||||
if (!isLikelyBitmapSubtitleFormat(candidate)) {
|
||||
return false;
|
||||
}
|
||||
const comparable = (Array.isArray(languageEntries) ? languageEntries : [])
|
||||
.filter((entry) => entry !== candidate && !entry.sdhLikely && isLikelyBitmapSubtitleFormat(entry));
|
||||
if (comparable.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const candidateEventCount = Number(candidate?.eventCount);
|
||||
const comparableEventCounts = comparable
|
||||
.map((entry) => Number(entry?.eventCount))
|
||||
.filter((value) => Number.isFinite(value) && value > 0);
|
||||
const maxComparableEventCount = comparableEventCounts.length > 0
|
||||
? Math.max(...comparableEventCounts)
|
||||
: null;
|
||||
const hasEventSignal = Number.isFinite(candidateEventCount)
|
||||
&& candidateEventCount >= 0
|
||||
&& Number.isFinite(maxComparableEventCount)
|
||||
&& maxComparableEventCount > 0
|
||||
&& candidateEventCount <= FORCED_SUBTITLE_MAX_EVENT_COUNT
|
||||
&& (candidateEventCount / maxComparableEventCount) <= FORCED_SUBTITLE_EVENT_RATIO_THRESHOLD
|
||||
&& (maxComparableEventCount - candidateEventCount) >= FORCED_SUBTITLE_MIN_EVENT_GAP;
|
||||
|
||||
const candidateStreamSize = Number(candidate?.streamSizeBytes);
|
||||
const comparableSizes = comparable
|
||||
.map((entry) => Number(entry?.streamSizeBytes))
|
||||
.filter((value) => Number.isFinite(value) && value > 0);
|
||||
const maxComparableSize = comparableSizes.length > 0
|
||||
? Math.max(...comparableSizes)
|
||||
: null;
|
||||
const hasSizeSignal = Number.isFinite(candidateStreamSize)
|
||||
&& candidateStreamSize > 0
|
||||
&& Number.isFinite(maxComparableSize)
|
||||
&& maxComparableSize > 0
|
||||
&& (candidateStreamSize / maxComparableSize) <= FORCED_SUBTITLE_SIZE_RATIO_THRESHOLD
|
||||
&& (maxComparableSize - candidateStreamSize) >= FORCED_SUBTITLE_MIN_SIZE_GAP_BYTES;
|
||||
|
||||
const signalCount = Number(hasEventSignal) + Number(hasSizeSignal);
|
||||
if (signalCount === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (candidate.defaultFlag && signalCount < 2) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function annotateSubtitleTracks(subtitleTracks) {
|
||||
const tracks = Array.isArray(subtitleTracks) ? subtitleTracks : [];
|
||||
if (tracks.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const entries = tracks.map((track, index) => ({
|
||||
...track,
|
||||
language: normalizeLanguage(track?.language || track?.languageLabel || 'und'),
|
||||
defaultFlag: Boolean(track?.defaultFlag),
|
||||
forcedFlag: parseSubtitleForcedFlag(track),
|
||||
forcedTrack: false,
|
||||
sourceConfidence: null,
|
||||
confidenceSource: 'heuristic',
|
||||
duplicate: false,
|
||||
selected: false,
|
||||
subtitleType: 'full',
|
||||
forcedAvailable: false,
|
||||
forcedSourceTrackIds: [],
|
||||
sdhLikely: (parseSubtitleSdhFlag(track) === true) || Boolean(track?.sdhLikely) || isLikelySdhSubtitleTrack(track),
|
||||
originalIndex: index
|
||||
}));
|
||||
|
||||
const byLanguage = new Map();
|
||||
for (const entry of entries) {
|
||||
if (!byLanguage.has(entry.language)) {
|
||||
byLanguage.set(entry.language, []);
|
||||
}
|
||||
byLanguage.get(entry.language).push(entry);
|
||||
}
|
||||
|
||||
for (const languageEntries of byLanguage.values()) {
|
||||
for (const entry of languageEntries) {
|
||||
const forcedByFlag = entry.forcedFlag === true;
|
||||
const forcedBlockedByFlag = entry.forcedFlag === false;
|
||||
const forcedByTitle = !entry.sdhLikely && !forcedBlockedByFlag && isLikelyForcedSubtitleTrack(entry);
|
||||
|
||||
if (forcedByFlag) {
|
||||
entry.forcedTrack = true;
|
||||
entry.sourceConfidence = 'high';
|
||||
entry.confidenceSource = 'explicit_flag';
|
||||
} else if (forcedByTitle) {
|
||||
entry.forcedTrack = true;
|
||||
entry.sourceConfidence = 'medium';
|
||||
entry.confidenceSource = 'title';
|
||||
}
|
||||
}
|
||||
|
||||
if (!languageEntries.some((entry) => entry.forcedTrack)) {
|
||||
const candidates = languageEntries
|
||||
.filter((entry) => !entry.sdhLikely && entry.forcedFlag !== false)
|
||||
.sort(compareSubtitleTracksByForcedHeuristic);
|
||||
const forcedCandidate = candidates[0] || null;
|
||||
if (forcedCandidate && isHeuristicForcedSubtitleCandidate(languageEntries, forcedCandidate)) {
|
||||
forcedCandidate.forcedTrack = true;
|
||||
forcedCandidate.sourceConfidence = 'low';
|
||||
forcedCandidate.confidenceSource = 'heuristic';
|
||||
}
|
||||
}
|
||||
|
||||
for (const entry of languageEntries) {
|
||||
if (!entry.forcedTrack) {
|
||||
continue;
|
||||
}
|
||||
entry.sourceConfidence = normalizeSubtitleConfidence(entry.sourceConfidence || 'low');
|
||||
}
|
||||
|
||||
const forcedEntries = languageEntries.filter((entry) => entry.forcedTrack);
|
||||
const fullEntries = languageEntries.filter((entry) => !entry.forcedTrack && !entry.sdhLikely);
|
||||
const sdhEntries = languageEntries.filter((entry) => !entry.forcedTrack && entry.sdhLikely);
|
||||
const forcedWinner = forcedEntries.length > 0 ? [...forcedEntries].sort(compareSubtitleTracksForDedup)[0] : null;
|
||||
const fullWinner = fullEntries.length > 0 ? [...fullEntries].sort(compareSubtitleTracksForDedup)[0] : null;
|
||||
const sdhWinner = sdhEntries.length > 0 ? [...sdhEntries].sort(compareSubtitleTracksForDedup)[0] : null;
|
||||
const forcedSourceTrackIds = forcedEntries
|
||||
.map((entry) => Number(entry?.sourceTrackId ?? entry?.id))
|
||||
.filter((value) => Number.isFinite(value) && value > 0)
|
||||
.map((value) => Math.trunc(value));
|
||||
const forcedAvailable = Boolean(forcedWinner);
|
||||
|
||||
for (const entry of languageEntries) {
|
||||
const typeWinner = entry.forcedTrack
|
||||
? forcedWinner
|
||||
: (entry.sdhLikely ? sdhWinner : fullWinner);
|
||||
entry.duplicate = Boolean(typeWinner && typeWinner !== entry);
|
||||
if (entry.forcedTrack) {
|
||||
entry.selected = Boolean(typeWinner && typeWinner === entry && !entry.duplicate);
|
||||
} else if (entry.sdhLikely) {
|
||||
entry.selected = Boolean(!fullWinner && typeWinner && typeWinner === entry && !entry.duplicate);
|
||||
} else {
|
||||
entry.selected = Boolean(typeWinner && typeWinner === entry && !entry.duplicate);
|
||||
}
|
||||
entry.subtitleType = entry.forcedTrack ? 'forced' : 'full';
|
||||
entry.forcedAvailable = forcedAvailable;
|
||||
entry.forcedSourceTrackIds = forcedSourceTrackIds;
|
||||
const fullHasForced = !entry.forcedTrack && Boolean(
|
||||
entry.fullHasForced
|
||||
?? entry.subtitleFullHasForced
|
||||
?? entry.hasForcedVariant
|
||||
);
|
||||
const explicitForcedOnly = entry.isForcedOnly ?? entry.forcedOnly ?? entry.subtitlePreviewForcedOnly;
|
||||
entry.isForcedOnly = typeof explicitForcedOnly === 'boolean'
|
||||
? explicitForcedOnly
|
||||
: (entry.forcedTrack && !fullHasForced);
|
||||
entry.fullHasForced = fullHasForced;
|
||||
}
|
||||
}
|
||||
|
||||
return entries
|
||||
.sort((a, b) => a.originalIndex - b.originalIndex)
|
||||
.map((entry) => {
|
||||
const { originalIndex, ...rest } = entry;
|
||||
return rest;
|
||||
});
|
||||
}
|
||||
|
||||
function parseEncoderList(raw) {
|
||||
return String(raw || '')
|
||||
.split(',')
|
||||
@@ -638,7 +1115,10 @@ function buildTrackSelectors(settings, presetProfile) {
|
||||
|
||||
function selectTrackIds(tracks, selector, trackType) {
|
||||
const available = Array.isArray(tracks) ? tracks : [];
|
||||
if (available.length === 0) {
|
||||
const selectable = trackType === 'subtitle'
|
||||
? available.filter((track) => !Boolean(track?.duplicate))
|
||||
: available;
|
||||
if (selectable.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -648,13 +1128,13 @@ function selectTrackIds(tracks, selector, trackType) {
|
||||
|
||||
if (selector.mode === 'all') {
|
||||
if (selector.firstOnly) {
|
||||
return [available[0].id];
|
||||
return [selectable[0].id];
|
||||
}
|
||||
return available.map((track) => track.id);
|
||||
return selectable.map((track) => track.id);
|
||||
}
|
||||
|
||||
if (selector.mode === 'explicit') {
|
||||
const explicit = available
|
||||
const explicit = selectable
|
||||
.filter((track) => selector.explicitIds.includes(track.id))
|
||||
.map((track) => track.id);
|
||||
if (selector.firstOnly) {
|
||||
@@ -664,7 +1144,7 @@ function selectTrackIds(tracks, selector, trackType) {
|
||||
}
|
||||
|
||||
if (selector.mode === 'language') {
|
||||
const matches = available.filter((track) => selector.languages.includes(track.language));
|
||||
const matches = selectable.filter((track) => selector.languages.includes(track.language));
|
||||
if (selector.firstOnly) {
|
||||
return matches.length > 0 ? [matches[0].id] : [];
|
||||
}
|
||||
@@ -672,11 +1152,11 @@ function selectTrackIds(tracks, selector, trackType) {
|
||||
}
|
||||
|
||||
if (selector.mode === 'first') {
|
||||
return [available[0].id];
|
||||
return [selectable[0].id];
|
||||
}
|
||||
|
||||
if (trackType === 'audio') {
|
||||
return [available[0].id];
|
||||
return [selectable[0].id];
|
||||
}
|
||||
|
||||
return [];
|
||||
@@ -916,21 +1396,45 @@ function buildMediainfoReview({
|
||||
const normalizedSubtitle = title.subtitleTracks.map((track) => {
|
||||
const selectedByRule = selectedSubtitleIds.includes(track.id);
|
||||
const subtitleFlags = computeSubtitleFlags(track.id, selectedSubtitleIds, trackSelectors.subtitle);
|
||||
const inferredForced = Boolean(
|
||||
track?.forcedTrack
|
||||
|| String(track?.subtitleType || '').trim().toLowerCase() === 'forced'
|
||||
);
|
||||
const inferredForcedOnly = Boolean(
|
||||
track?.isForcedOnly
|
||||
?? track?.forcedOnly
|
||||
?? track?.subtitlePreviewForcedOnly
|
||||
?? inferredForced
|
||||
);
|
||||
const inferredDefault = Boolean(track?.defaultFlag);
|
||||
const subtitlePreviewFlags = [];
|
||||
if (subtitleFlags.burned) {
|
||||
subtitlePreviewFlags.push('burned');
|
||||
}
|
||||
if (subtitleFlags.forced || inferredForced) {
|
||||
subtitlePreviewFlags.push('forced');
|
||||
}
|
||||
if (subtitleFlags.forcedOnly || inferredForcedOnly) {
|
||||
subtitlePreviewFlags.push('forced-only');
|
||||
}
|
||||
if (subtitleFlags.default || inferredDefault) {
|
||||
subtitlePreviewFlags.push('default');
|
||||
}
|
||||
const subtitlePreviewSummary = !selectedByRule
|
||||
? 'Nicht übernommen'
|
||||
: (subtitleFlags.flags.length > 0
|
||||
? `Übernehmen (${subtitleFlags.flags.join(', ')})`
|
||||
: (subtitlePreviewFlags.length > 0
|
||||
? `Übernehmen (${subtitlePreviewFlags.join(', ')})`
|
||||
: 'Übernehmen');
|
||||
|
||||
return {
|
||||
...track,
|
||||
selectedByRule,
|
||||
subtitlePreviewSummary,
|
||||
subtitlePreviewFlags: subtitleFlags.flags,
|
||||
subtitlePreviewFlags: selectedByRule ? subtitlePreviewFlags : [],
|
||||
subtitlePreviewBurnIn: subtitleFlags.burned,
|
||||
subtitlePreviewForced: subtitleFlags.forced,
|
||||
subtitlePreviewForcedOnly: subtitleFlags.forcedOnly,
|
||||
subtitlePreviewDefaultTrack: subtitleFlags.default
|
||||
subtitlePreviewForced: selectedByRule ? (subtitleFlags.forced || inferredForced) : false,
|
||||
subtitlePreviewForcedOnly: selectedByRule ? (subtitleFlags.forcedOnly || inferredForcedOnly) : false,
|
||||
subtitlePreviewDefaultTrack: selectedByRule ? (subtitleFlags.default || inferredDefault) : false
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user