Fix Sonarr season-pack imports after container updates #9

Merged
sascha merged 3 commits from fix/sonarr-pack-queue-resolution-20260916 into main 2026-09-16 07:13:35 +02:00
Showing only changes of commit 7abfb9e836 - Show all commits

View file

@ -2,7 +2,7 @@
* @name Sonarr - Trigger Manual Import * @name Sonarr - Trigger Manual Import
* @description Safely trigger Sonarr ManualImport for exactly one processed episode file. * @description Safely trigger Sonarr ManualImport for exactly one processed episode file.
* @author FileFlows + Sascha * @author FileFlows + Sascha
* @revision 11 * @revision 12
* @output Import erfolgreich * @output Import erfolgreich
* @output Fehler * @output Fehler
* @param {string} URL Sonarr root URL and port * @param {string} URL Sonarr root URL and port
@ -57,8 +57,8 @@ function Script(URL, ApiKey, ImportPath, UseUnmappedPath, MoveMode, TimeOut) {
if (!(seriesId && episodeIds)) { if (!(seriesId && episodeIds)) {
let trackedEpisode = null; let trackedEpisode = null;
for (const pattern of searchPatterns) { for (const pattern of searchPatterns) {
trackedEpisode = findEpisodeFromQueue(pattern, sonarr) trackedEpisode = findEpisodeFromQueue(pattern, sonarr, filePattern)
?? findEpisodeFromGrabHistory(pattern, sonarr); ?? findEpisodeFromGrabHistory(pattern, sonarr, filePattern);
if (trackedEpisode) break; if (trackedEpisode) break;
} }
if (!trackedEpisode) { if (!trackedEpisode) {
@ -226,7 +226,7 @@ function sendManualImportCommand(sonarr, cmdBody) {
/*────────────────────────────── helpers ──────────────────────────────────*/ /*────────────────────────────── helpers ──────────────────────────────────*/
function findEpisodeFromQueue(searchPattern, sonarr) { function findEpisodeFromQueue(searchPattern, sonarr, episodePattern) {
if (!searchPattern) return null; if (!searchPattern) return null;
try { try {
const query = buildQueryParams({ page: 1, pageSize: 200, includeUnknownSeriesItems: true }); const query = buildQueryParams({ page: 1, pageSize: 200, includeUnknownSeriesItems: true });
@ -240,7 +240,11 @@ function findEpisodeFromQueue(searchPattern, sonarr) {
Logger.ILog(`Matched Sonarr queue seriesId ${matches[0].seriesId}, episodeId ${matches[0].episodeId}.`); Logger.ILog(`Matched Sonarr queue seriesId ${matches[0].seriesId}, episodeId ${matches[0].episodeId}.`);
return { id: matches[0].seriesId, episodeIds: [matches[0].episodeId] }; return { id: matches[0].seriesId, episodeIds: [matches[0].episodeId] };
} }
if (matches.length > 1) Logger.WLog(`Ambiguous Sonarr queue match (${matches.length} records).`); if (matches.length > 1) {
const resolved = resolvePackEpisode(matches, episodePattern, sonarr);
if (resolved) return resolved;
Logger.WLog(`Ambiguous Sonarr queue match (${matches.length} records).`);
}
return null; return null;
} catch (e) { } catch (e) {
Logger.ELog(`Error fetching Sonarr queue: ${e.message}`); Logger.ELog(`Error fetching Sonarr queue: ${e.message}`);
@ -248,7 +252,7 @@ function findEpisodeFromQueue(searchPattern, sonarr) {
} }
} }
function findEpisodeFromGrabHistory(searchPattern, sonarr) { function findEpisodeFromGrabHistory(searchPattern, sonarr, episodePattern) {
if (!searchPattern) return null; if (!searchPattern) return null;
try { try {
const query = buildQueryParams({ const query = buildQueryParams({
@ -269,7 +273,11 @@ function findEpisodeFromGrabHistory(searchPattern, sonarr) {
Logger.ILog(`Matched Sonarr grab history seriesId ${seriesId}, episodeId ${episodeId}.`); Logger.ILog(`Matched Sonarr grab history seriesId ${seriesId}, episodeId ${episodeId}.`);
return { id: seriesId, episodeIds: [episodeId] }; return { id: seriesId, episodeIds: [episodeId] };
} }
if (keys.length > 1) Logger.WLog(`Ambiguous Sonarr grab history match (${keys.length} episodes).`); if (keys.length > 1) {
const resolved = resolvePackEpisode(matches, episodePattern, sonarr);
if (resolved) return resolved;
Logger.WLog(`Ambiguous Sonarr grab history match (${keys.length} episodes).`);
}
return null; return null;
} catch (e) { } catch (e) {
Logger.ELog(`Error fetching Sonarr history: ${e.message}`); Logger.ELog(`Error fetching Sonarr history: ${e.message}`);
@ -277,6 +285,47 @@ function findEpisodeFromGrabHistory(searchPattern, sonarr) {
} }
} }
function resolvePackEpisode(matches, episodePattern, sonarr) {
const coordinates = extractEpisodeCoordinates(episodePattern);
if (!coordinates) return null;
const seriesIds = matches
.map(item => Number(item?.seriesId))
.filter(Boolean)
.filter((value, index, values) => values.indexOf(value) === index);
if (seriesIds.length !== 1) return null;
try {
const seriesId = seriesIds[0];
const query = buildQueryParams({ seriesId });
const episodes = sonarr.fetchJson('episode', query) || [];
const candidates = episodes.filter(item =>
Number(item?.seasonNumber) === coordinates.seasonNumber
&& Number(item?.episodeNumber) === coordinates.episodeNumber
);
if (candidates.length !== 1) return null;
const episodeId = Number(candidates[0]?.id);
const trackedIds = matches.map(item => Number(item?.episodeId)).filter(Boolean);
if (!episodeId || !trackedIds.includes(episodeId)) return null;
Logger.ILog(
`Resolved season-pack match via exact filename S${coordinates.seasonNumber}`
+ `E${coordinates.episodeNumber}: seriesId ${seriesId}, episodeId ${episodeId}.`
);
return { id: seriesId, episodeIds: [episodeId] };
} catch (e) {
Logger.WLog(`Unable to resolve season-pack episode: ${e.message}`);
return null;
}
}
function extractEpisodeCoordinates(value) {
const match = String(value || '').match(/(?:^|[^a-z0-9])s(\d{1,2})e(\d{1,3})(?:[^a-z0-9]|$)/i);
if (!match) return null;
return { seasonNumber: Number(match[1]), episodeNumber: Number(match[2]) };
}
function normalizeReleaseTitle(value) { function normalizeReleaseTitle(value) {
return (value || '').toLowerCase().replace(/[^a-z0-9]+/g, ''); return (value || '').toLowerCase().replace(/[^a-z0-9]+/g, '');
} }