0.13.0-1 Series Flow
This commit is contained in:
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "ripster-backend",
|
"name": "ripster-backend",
|
||||||
"version": "0.13.0",
|
"version": "0.13.0-1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "ripster-backend",
|
"name": "ripster-backend",
|
||||||
"version": "0.13.0",
|
"version": "0.13.0-1",
|
||||||
"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.13.0",
|
"version": "0.13.0-1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "commonjs",
|
"type": "commonjs",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -905,11 +905,27 @@ function enrichJobRow(job, settings = null, options = {}) {
|
|||||||
const backupSuccess = ripSuccessful;
|
const backupSuccess = ripSuccessful;
|
||||||
const finishedWithOutput = normalizedJobStatus === 'FINISHED'
|
const finishedWithOutput = normalizedJobStatus === 'FINISHED'
|
||||||
&& (includeFsChecks ? Boolean(outputStatus?.exists) : true);
|
&& (includeFsChecks ? Boolean(outputStatus?.exists) : true);
|
||||||
|
const seriesBatchSummary = handbrakeInfo?.seriesBatch && typeof handbrakeInfo.seriesBatch === 'object'
|
||||||
|
? handbrakeInfo.seriesBatch
|
||||||
|
: null;
|
||||||
|
const seriesBatchMode = String(handbrakeInfo?.mode || '').trim().toLowerCase() === 'series_batch';
|
||||||
|
const seriesBatchTotal = Number(seriesBatchSummary?.totalCount || 0);
|
||||||
|
const seriesBatchFinished = Number(seriesBatchSummary?.finishedCount || 0);
|
||||||
|
const seriesBatchErrors = Number(seriesBatchSummary?.errorCount || 0);
|
||||||
|
const seriesBatchCancelled = Number(seriesBatchSummary?.cancelledCount || 0);
|
||||||
|
const seriesBatchEncodeSuccess = seriesBatchMode
|
||||||
|
&& seriesBatchTotal > 0
|
||||||
|
&& seriesBatchFinished >= seriesBatchTotal
|
||||||
|
&& seriesBatchErrors <= 0
|
||||||
|
&& seriesBatchCancelled <= 0;
|
||||||
const encodeSuccess = directoryLikeOutput
|
const encodeSuccess = directoryLikeOutput
|
||||||
? finishedWithOutput
|
? finishedWithOutput
|
||||||
: (mediaType === 'converter'
|
: (
|
||||||
? finishedWithOutput
|
seriesBatchEncodeSuccess
|
||||||
: String(handbrakeInfo?.status || '').trim().toUpperCase() === 'SUCCESS');
|
|| (mediaType === 'converter'
|
||||||
|
? finishedWithOutput
|
||||||
|
: String(handbrakeInfo?.status || '').trim().toUpperCase() === 'SUCCESS')
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...job,
|
...job,
|
||||||
@@ -3629,6 +3645,7 @@ class HistoryService {
|
|||||||
});
|
});
|
||||||
const seriesCandidateIds = seriesCandidates.map((job) => normalizeJobIdValue(job?.id)).filter(Boolean);
|
const seriesCandidateIds = seriesCandidates.map((job) => normalizeJobIdValue(job?.id)).filter(Boolean);
|
||||||
let childRows = [];
|
let childRows = [];
|
||||||
|
let nestedChildRows = [];
|
||||||
let outputFolders = [];
|
let outputFolders = [];
|
||||||
if (containerIds.length > 0) {
|
if (containerIds.length > 0) {
|
||||||
const placeholders = containerIds.map(() => '?').join(', ');
|
const placeholders = containerIds.map(() => '?').join(', ');
|
||||||
@@ -3639,15 +3656,46 @@ class HistoryService {
|
|||||||
const childIds = childRows.map((row) => normalizeJobIdValue(row?.id)).filter(Boolean);
|
const childIds = childRows.map((row) => normalizeJobIdValue(row?.id)).filter(Boolean);
|
||||||
if (childIds.length > 0) {
|
if (childIds.length > 0) {
|
||||||
const childPlaceholders = childIds.map(() => '?').join(', ');
|
const childPlaceholders = childIds.map(() => '?').join(', ');
|
||||||
|
nestedChildRows = await db.all(
|
||||||
|
`SELECT id, parent_job_id, output_path, raw_path, handbrake_info_json, status, encode_plan_json, makemkv_info_json, rip_successful FROM jobs WHERE parent_job_id IN (${childPlaceholders})`,
|
||||||
|
childIds
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const summaryJobIds = Array.from(new Set([
|
||||||
|
...childRows.map((row) => normalizeJobIdValue(row?.id)).filter(Boolean),
|
||||||
|
...nestedChildRows.map((row) => normalizeJobIdValue(row?.id)).filter(Boolean)
|
||||||
|
]));
|
||||||
|
if (summaryJobIds.length > 0) {
|
||||||
|
const childPlaceholders = summaryJobIds.map(() => '?').join(', ');
|
||||||
outputFolders = await db.all(
|
outputFolders = await db.all(
|
||||||
`SELECT job_id, output_path FROM job_output_folders WHERE job_id IN (${childPlaceholders})`,
|
`SELECT job_id, output_path FROM job_output_folders WHERE job_id IN (${childPlaceholders})`,
|
||||||
childIds
|
summaryJobIds
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const allContainerChildRows = [...childRows, ...nestedChildRows];
|
||||||
|
const directChildRowById = new Map();
|
||||||
|
for (const row of childRows) {
|
||||||
|
const childId = normalizeJobIdValue(row?.id);
|
||||||
|
if (childId) {
|
||||||
|
directChildRowById.set(childId, row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const nestedRowsByParentId = new Map();
|
||||||
|
for (const row of nestedChildRows) {
|
||||||
|
const parentId = normalizeJobIdValue(row?.parent_job_id);
|
||||||
|
if (!parentId) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!nestedRowsByParentId.has(parentId)) {
|
||||||
|
nestedRowsByParentId.set(parentId, []);
|
||||||
|
}
|
||||||
|
nestedRowsByParentId.get(parentId).push(row);
|
||||||
|
}
|
||||||
|
|
||||||
const childOutputMap = new Map();
|
const childOutputMap = new Map();
|
||||||
for (const child of childRows) {
|
for (const child of allContainerChildRows) {
|
||||||
const childId = normalizeJobIdValue(child?.id);
|
const childId = normalizeJobIdValue(child?.id);
|
||||||
if (!childId) {
|
if (!childId) {
|
||||||
continue;
|
continue;
|
||||||
@@ -3701,9 +3749,12 @@ class HistoryService {
|
|||||||
let expectedFromPlans = 0;
|
let expectedFromPlans = 0;
|
||||||
const seenOutputs = new Set();
|
const seenOutputs = new Set();
|
||||||
let existingCount = 0;
|
let existingCount = 0;
|
||||||
|
let existingCountFromSeriesBatch = 0;
|
||||||
const totalChildren = childIds.length;
|
const totalChildren = childIds.length;
|
||||||
for (const childId of childIds) {
|
for (const childId of childIds) {
|
||||||
const childRow = childRows.find((row) => normalizeJobIdValue(row?.id) === childId) || null;
|
const childRow = directChildRowById.get(childId) || null;
|
||||||
|
const descendantRows = nestedRowsByParentId.get(childId) || [];
|
||||||
|
const relatedRows = [childRow, ...descendantRows].filter(Boolean);
|
||||||
if (childRow) {
|
if (childRow) {
|
||||||
const rawPath = String(childRow?.raw_path || '').trim();
|
const rawPath = String(childRow?.raw_path || '').trim();
|
||||||
if (rawPath) {
|
if (rawPath) {
|
||||||
@@ -3722,32 +3773,87 @@ class HistoryService {
|
|||||||
if (ripSuccessful) {
|
if (ripSuccessful) {
|
||||||
backupSuccessCount += 1;
|
backupSuccessCount += 1;
|
||||||
}
|
}
|
||||||
|
let selectedCount = 0;
|
||||||
const childPlan = parseJsonSafe(childRow?.encode_plan_json, null);
|
const childPlan = parseJsonSafe(childRow?.encode_plan_json, null);
|
||||||
if (childPlan && typeof childPlan === 'object') {
|
if (childPlan && typeof childPlan === 'object') {
|
||||||
const titles = Array.isArray(childPlan?.titles) ? childPlan.titles : [];
|
const titles = Array.isArray(childPlan?.titles) ? childPlan.titles : [];
|
||||||
const selectedCount = titles.filter((title) => title?.selectedForEncode || title?.encodeInput).length;
|
selectedCount = titles.filter((title) => title?.selectedForEncode || title?.encodeInput).length;
|
||||||
if (selectedCount > 0) {
|
|
||||||
expectedFromPlans += selectedCount;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const handbrakeInfo = parseJsonSafe(childRow?.handbrake_info_json, null);
|
const handbrakeInfo = parseJsonSafe(childRow?.handbrake_info_json, null);
|
||||||
const hbStatus = String(handbrakeInfo?.status || '').trim().toUpperCase();
|
const hbStatus = String(handbrakeInfo?.status || '').trim().toUpperCase();
|
||||||
if (hbStatus === 'SUCCESS') {
|
const seriesBatchSummary = handbrakeInfo?.seriesBatch && typeof handbrakeInfo.seriesBatch === 'object'
|
||||||
encodeSuccessAny = true;
|
? handbrakeInfo.seriesBatch
|
||||||
encodeSuccessCount += 1;
|
: null;
|
||||||
|
const seriesBatchTotal = Number(seriesBatchSummary?.totalCount || 0);
|
||||||
|
const seriesBatchFinished = Number(seriesBatchSummary?.finishedCount || 0);
|
||||||
|
const seriesBatchErrors = Number(seriesBatchSummary?.errorCount || 0);
|
||||||
|
const seriesBatchCancelled = Number(seriesBatchSummary?.cancelledCount || 0);
|
||||||
|
const expectedFromNested = descendantRows.length > 0 ? descendantRows.length : 0;
|
||||||
|
const expectedFromChild = seriesBatchTotal > 0
|
||||||
|
? seriesBatchTotal
|
||||||
|
: (selectedCount > 0 ? selectedCount : expectedFromNested);
|
||||||
|
if (expectedFromChild > 0) {
|
||||||
|
expectedFromPlans += expectedFromChild;
|
||||||
}
|
}
|
||||||
|
if (seriesBatchFinished > 0) {
|
||||||
|
existingCountFromSeriesBatch += seriesBatchFinished;
|
||||||
|
}
|
||||||
|
|
||||||
|
let childEncodeSuccess = hbStatus === 'SUCCESS';
|
||||||
|
if (
|
||||||
|
!childEncodeSuccess
|
||||||
|
&& seriesBatchTotal > 0
|
||||||
|
&& seriesBatchFinished >= seriesBatchTotal
|
||||||
|
&& seriesBatchErrors <= 0
|
||||||
|
&& seriesBatchCancelled <= 0
|
||||||
|
) {
|
||||||
|
childEncodeSuccess = true;
|
||||||
|
}
|
||||||
|
if (!childEncodeSuccess && descendantRows.length > 0) {
|
||||||
|
let finishedDescendants = 0;
|
||||||
|
let errorDescendants = 0;
|
||||||
|
let cancelledDescendants = 0;
|
||||||
|
for (const row of descendantRows) {
|
||||||
|
const rowState = String(row?.status || row?.last_state || '').trim().toUpperCase();
|
||||||
|
if (rowState === 'FINISHED') {
|
||||||
|
finishedDescendants += 1;
|
||||||
|
} else if (rowState === 'ERROR') {
|
||||||
|
errorDescendants += 1;
|
||||||
|
} else if (rowState === 'CANCELLED') {
|
||||||
|
cancelledDescendants += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
childEncodeSuccess = finishedDescendants >= descendantRows.length
|
||||||
|
&& errorDescendants === 0
|
||||||
|
&& cancelledDescendants === 0;
|
||||||
|
}
|
||||||
|
if (childEncodeSuccess) {
|
||||||
|
encodeSuccessAny = true;
|
||||||
|
encodeSuccessCount += 1;
|
||||||
|
}
|
||||||
|
} else if (descendantRows.length > 0) {
|
||||||
|
expectedFromPlans += descendantRows.length;
|
||||||
}
|
}
|
||||||
const outputs = Array.from(childOutputMap.get(childId) || []);
|
for (const relatedRow of relatedRows) {
|
||||||
for (const outputPath of outputs) {
|
const relatedRowId = normalizeJobIdValue(relatedRow?.id);
|
||||||
if (!outputPath || seenOutputs.has(outputPath)) {
|
if (!relatedRowId) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
seenOutputs.add(outputPath);
|
const outputs = Array.from(childOutputMap.get(relatedRowId) || []);
|
||||||
if (!includeFsChecks || fs.existsSync(outputPath)) {
|
for (const outputPath of outputs) {
|
||||||
existingCount += 1;
|
if (!outputPath || seenOutputs.has(outputPath)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
seenOutputs.add(outputPath);
|
||||||
|
if (!includeFsChecks || fs.existsSync(outputPath)) {
|
||||||
|
existingCount += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (existingCountFromSeriesBatch > existingCount) {
|
||||||
|
existingCount = existingCountFromSeriesBatch;
|
||||||
|
}
|
||||||
if (!encodeSuccessAny && existingCount > 0) {
|
if (!encodeSuccessAny && existingCount > 0) {
|
||||||
encodeSuccessAny = true;
|
encodeSuccessAny = true;
|
||||||
if (encodeSuccessCount === 0 && totalChildren > 0) {
|
if (encodeSuccessCount === 0 && totalChildren > 0) {
|
||||||
@@ -5102,6 +5208,18 @@ class HistoryService {
|
|||||||
?? analyzeContext?.seriesLookupHint?.discNumber
|
?? analyzeContext?.seriesLookupHint?.discNumber
|
||||||
?? null
|
?? null
|
||||||
);
|
);
|
||||||
|
const resolvedJobMediaType = inferMediaType(
|
||||||
|
job,
|
||||||
|
makemkvInfo,
|
||||||
|
job?.mediainfo_info_json,
|
||||||
|
job?.encode_plan_json,
|
||||||
|
job?.handbrake_info_json
|
||||||
|
);
|
||||||
|
if (resolvedJobMediaType === 'dvd' && discNumber === null) {
|
||||||
|
const error = new Error('Serien-DVD erkannt: Disk-Nummer ist Pflicht (Start bei 1).');
|
||||||
|
error.statusCode = 400;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
let metadataKind = manualMetadataKind
|
let metadataKind = manualMetadataKind
|
||||||
|| String(
|
|| String(
|
||||||
existingSelectedMetadata?.metadataKind
|
existingSelectedMetadata?.metadataKind
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "ripster-frontend",
|
"name": "ripster-frontend",
|
||||||
"version": "0.13.0",
|
"version": "0.13.0-1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "ripster-frontend",
|
"name": "ripster-frontend",
|
||||||
"version": "0.13.0",
|
"version": "0.13.0-1",
|
||||||
"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.13.0",
|
"version": "0.13.0-1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -739,6 +739,21 @@ function resolveSeriesDiscNumber(job) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function compareSeriesChildJobsByDisc(leftJob, rightJob) {
|
||||||
|
const leftDisc = resolveSeriesDiscNumber(leftJob);
|
||||||
|
const rightDisc = resolveSeriesDiscNumber(rightJob);
|
||||||
|
if (leftDisc !== null && rightDisc !== null && leftDisc !== rightDisc) {
|
||||||
|
return leftDisc - rightDisc;
|
||||||
|
}
|
||||||
|
if (leftDisc !== null && rightDisc === null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (leftDisc === null && rightDisc !== null) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return Number(leftJob?.id || 0) - Number(rightJob?.id || 0);
|
||||||
|
}
|
||||||
|
|
||||||
function BoolState({ value }) {
|
function BoolState({ value }) {
|
||||||
const isTrue = Boolean(value);
|
const isTrue = Boolean(value);
|
||||||
return isTrue ? (
|
return isTrue ? (
|
||||||
@@ -1113,7 +1128,9 @@ export default function JobDetailDialog({
|
|||||||
const metadataDetails = resolveMetadataDetailsForDisplay(job, omdbInfo);
|
const metadataDetails = resolveMetadataDetailsForDisplay(job, omdbInfo);
|
||||||
const seriesDiscNumber = isDvdSeries ? resolveSeriesDiscNumber(job) : null;
|
const seriesDiscNumber = isDvdSeries ? resolveSeriesDiscNumber(job) : null;
|
||||||
const seriesDiscLabel = seriesDiscNumber ? `Disk ${seriesDiscNumber}` : 'Disk unbekannt';
|
const seriesDiscLabel = seriesDiscNumber ? `Disk ${seriesDiscNumber}` : 'Disk unbekannt';
|
||||||
const childJobs = Array.isArray(job?.children) ? job.children : [];
|
const childJobs = Array.isArray(job?.children)
|
||||||
|
? [...job.children].sort(compareSeriesChildJobsByDisc)
|
||||||
|
: [];
|
||||||
const isSeriesContainer = isDvdSeries && String(job?.job_kind || '').trim().toLowerCase() === 'dvd_series_container';
|
const isSeriesContainer = isDvdSeries && String(job?.job_kind || '').trim().toLowerCase() === 'dvd_series_container';
|
||||||
const seriesChildSummary = job?.seriesChildSummary || null;
|
const seriesChildSummary = job?.seriesChildSummary || null;
|
||||||
const seriesBackupSummary = isSeriesContainer ? seriesChildSummary?.backup : null;
|
const seriesBackupSummary = isSeriesContainer ? seriesChildSummary?.backup : null;
|
||||||
|
|||||||
@@ -13,6 +13,17 @@ export default function MetadataSelectionDialog({
|
|||||||
onSearch,
|
onSearch,
|
||||||
busy
|
busy
|
||||||
}) {
|
}) {
|
||||||
|
const parseDiscNumber = (rawValue) => {
|
||||||
|
const text = String(rawValue ?? '').trim();
|
||||||
|
if (!/^\d+$/.test(text)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const parsed = Number(text);
|
||||||
|
if (!Number.isFinite(parsed) || parsed <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return Math.trunc(parsed);
|
||||||
|
};
|
||||||
const [selected, setSelected] = useState(null);
|
const [selected, setSelected] = useState(null);
|
||||||
const [query, setQuery] = useState('');
|
const [query, setQuery] = useState('');
|
||||||
const [manualTitle, setManualTitle] = useState('');
|
const [manualTitle, setManualTitle] = useState('');
|
||||||
@@ -23,9 +34,18 @@ export default function MetadataSelectionDialog({
|
|||||||
const [searchBusy, setSearchBusy] = useState(false);
|
const [searchBusy, setSearchBusy] = useState(false);
|
||||||
const [searchError, setSearchError] = useState('');
|
const [searchError, setSearchError] = useState('');
|
||||||
const [seasonFilter, setSeasonFilter] = useState('');
|
const [seasonFilter, setSeasonFilter] = useState('');
|
||||||
|
const [discValidationError, setDiscValidationError] = useState('');
|
||||||
const metadataProvider = String(context?.metadataProvider || 'omdb').trim().toLowerCase() || 'omdb';
|
const metadataProvider = String(context?.metadataProvider || 'omdb').trim().toLowerCase() || 'omdb';
|
||||||
|
const mediaProfile = String(
|
||||||
|
context?.mediaProfile
|
||||||
|
|| context?.selectedMetadata?.mediaProfile
|
||||||
|
|| ''
|
||||||
|
).trim().toLowerCase();
|
||||||
const providerLabel = metadataProvider === 'tmdb' ? 'TMDb' : 'OMDb';
|
const providerLabel = metadataProvider === 'tmdb' ? 'TMDb' : 'OMDb';
|
||||||
const supportsExternalIdInput = metadataProvider === 'omdb';
|
const supportsExternalIdInput = metadataProvider === 'omdb';
|
||||||
|
const requiresDiscNumber = metadataProvider === 'tmdb' && (mediaProfile === 'dvd' || !mediaProfile);
|
||||||
|
const effectiveDiscNumber = parseDiscNumber(manualDiscNumber);
|
||||||
|
const hasValidDiscNumber = effectiveDiscNumber !== null;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!visible) {
|
if (!visible) {
|
||||||
@@ -51,8 +71,18 @@ export default function MetadataSelectionDialog({
|
|||||||
setSearchBusy(false);
|
setSearchBusy(false);
|
||||||
setSearchError('');
|
setSearchError('');
|
||||||
setSeasonFilter('');
|
setSeasonFilter('');
|
||||||
|
setDiscValidationError('');
|
||||||
}, [visible, context]);
|
}, [visible, context]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!discValidationError) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!requiresDiscNumber || hasValidDiscNumber) {
|
||||||
|
setDiscValidationError('');
|
||||||
|
}
|
||||||
|
}, [discValidationError, requiresDiscNumber, hasValidDiscNumber]);
|
||||||
|
|
||||||
const rows = useMemo(() => {
|
const rows = useMemo(() => {
|
||||||
const base = Array.isArray(context?.metadataCandidates)
|
const base = Array.isArray(context?.metadataCandidates)
|
||||||
? context.metadataCandidates
|
? context.metadataCandidates
|
||||||
@@ -136,10 +166,10 @@ export default function MetadataSelectionDialog({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
const parsedDiscNumber = Number(manualDiscNumber);
|
if (requiresDiscNumber && !hasValidDiscNumber) {
|
||||||
const effectiveDiscNumber = Number.isFinite(parsedDiscNumber) && parsedDiscNumber > 0
|
setDiscValidationError('Disk-Nummer ist Pflicht und muss eine ganze Zahl >= 1 sein.');
|
||||||
? Math.trunc(parsedDiscNumber)
|
return;
|
||||||
: null;
|
}
|
||||||
|
|
||||||
const payload = selected
|
const payload = selected
|
||||||
? {
|
? {
|
||||||
@@ -174,11 +204,19 @@ export default function MetadataSelectionDialog({
|
|||||||
await onSubmit(payload);
|
await onSubmit(payload);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleHideRequest = () => {
|
||||||
|
if (requiresDiscNumber && !hasValidDiscNumber) {
|
||||||
|
setDiscValidationError('Disk-Nummer ist Pflicht und muss eine ganze Zahl >= 1 sein.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onHide?.();
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
header="Metadaten auswählen"
|
header="Metadaten auswählen"
|
||||||
visible={visible}
|
visible={visible}
|
||||||
onHide={onHide}
|
onHide={handleHideRequest}
|
||||||
style={{ width: '52rem', maxWidth: '95vw' }}
|
style={{ width: '52rem', maxWidth: '95vw' }}
|
||||||
className="metadata-selection-dialog"
|
className="metadata-selection-dialog"
|
||||||
breakpoints={{ '1200px': '92vw', '768px': '96vw', '560px': '98vw' }}
|
breakpoints={{ '1200px': '92vw', '768px': '96vw', '560px': '98vw' }}
|
||||||
@@ -211,16 +249,19 @@ export default function MetadataSelectionDialog({
|
|||||||
<InputText
|
<InputText
|
||||||
value={manualDiscNumber}
|
value={manualDiscNumber}
|
||||||
onChange={(event) => setManualDiscNumber(event.target.value)}
|
onChange={(event) => setManualDiscNumber(event.target.value)}
|
||||||
placeholder="Disc-Nr (optional)"
|
placeholder={requiresDiscNumber ? 'Disc-Nr (Pflicht)' : 'Disc-Nr (optional)'}
|
||||||
inputMode="numeric"
|
inputMode="numeric"
|
||||||
disabled={searchBusy || busy}
|
disabled={searchBusy || busy}
|
||||||
/>
|
/>
|
||||||
<small className="metadata-filter-subtitle">
|
<small className="metadata-filter-subtitle">
|
||||||
Optionale Disc-Nummer fuer internes Mapping und Output-Templates. Nur Werte groesser 0 werden gespeichert.
|
{requiresDiscNumber
|
||||||
|
? 'Pflichtfeld fuer Serien-DVDs. Ohne Disk-Nummer koennen die Metadaten nicht verlassen werden.'
|
||||||
|
: 'Optionale Disc-Nummer fuer internes Mapping und Output-Templates. Nur Werte groesser 0 werden gespeichert.'}
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{searchError ? <small className="error-text">{searchError}</small> : null}
|
{searchError ? <small className="error-text">{searchError}</small> : null}
|
||||||
|
{discValidationError ? <small className="error-text">{discValidationError}</small> : null}
|
||||||
|
|
||||||
<div className="table-scroll-wrap table-scroll-medium">
|
<div className="table-scroll-wrap table-scroll-medium">
|
||||||
<DataTable
|
<DataTable
|
||||||
@@ -271,13 +312,22 @@ export default function MetadataSelectionDialog({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="dialog-actions">
|
<div className="dialog-actions">
|
||||||
<Button label="Abbrechen" severity="secondary" text onClick={onHide} />
|
<Button
|
||||||
|
label="Abbrechen"
|
||||||
|
severity="secondary"
|
||||||
|
text
|
||||||
|
onClick={handleHideRequest}
|
||||||
|
disabled={requiresDiscNumber && !hasValidDiscNumber}
|
||||||
|
/>
|
||||||
<Button
|
<Button
|
||||||
label="Auswahl übernehmen"
|
label="Auswahl übernehmen"
|
||||||
icon="pi pi-play"
|
icon="pi pi-play"
|
||||||
onClick={handleSubmit}
|
onClick={handleSubmit}
|
||||||
loading={busy}
|
loading={busy}
|
||||||
disabled={!selected && !manualTitle.trim() && !manualImdb.trim()}
|
disabled={
|
||||||
|
(!selected && !manualTitle.trim() && !manualImdb.trim())
|
||||||
|
|| (requiresDiscNumber && !hasValidDiscNumber)
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "ripster",
|
"name": "ripster",
|
||||||
"version": "0.13.0",
|
"version": "0.13.0-1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "ripster",
|
"name": "ripster",
|
||||||
"version": "0.13.0",
|
"version": "0.13.0-1",
|
||||||
"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.13.0",
|
"version": "0.13.0-1",
|
||||||
"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