diff --git a/src/index.ts b/src/index.ts index dd5fc98..2975d45 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,6 +5,7 @@ import { MergedCustomFormatResource } from "./__generated__/mergedTypes"; import { configureApi, configureRadarrApi, configureSonarrApi, getArrApi, unsetApi } from "./api"; import { getConfig, validateConfig } from "./config"; import { calculateCFsToManage, loadCFFromConfig, loadLocalCfs, loadServerCustomFormats, manageCf, mergeCfSources } from "./custom-formats"; +import { loadLocalRecyclarrTemplate } from "./local-importer"; import { logHeading, logger } from "./logger"; import { calculateQualityDefinitionDiff, loadQualityDefinitionFromServer } from "./quality-definitions"; import { calculateQualityProfilesDiff, filterInvalidQualityProfiles, loadQualityProfilesFromServer } from "./quality-profiles"; @@ -33,13 +34,19 @@ const mergeConfigsAndTemplates = async ( arrType: ArrType, ): Promise<{ mergedCFs: CFProcessing; config: MergedConfigInstance }> => { const recyclarrTemplateMap = loadRecyclarrTemplates(arrType); + const localTemplateMap = loadLocalRecyclarrTemplate(arrType); const trashTemplates = await loadQPFromTrash(arrType); + logger.debug( + `Loaded ${recyclarrTemplateMap.size} Recyclarr templates, ${localTemplateMap.size} local templates and ${trashTemplates.size} trash templates.`, + ); + const recylarrMergedTemplates: MappedMergedTemplates = { custom_formats: [], quality_profiles: [], }; + // TODO: customFormatDefinitions not supported in templates yet if (value.include) { const mappedIncludes = value.include.reduce<{ recyclarr: InputConfigIncludeItem[]; trash: InputConfigIncludeItem[] }>( (previous, current) => { @@ -60,11 +67,11 @@ const mergeConfigsAndTemplates = async ( ); logger.info( - `Found ${value.include.length} templates to include: [recyclarr]=${mappedIncludes.recyclarr.length}, [trash]=${mappedIncludes.trash.length} ...`, + `Found ${value.include.length} templates to include. Mapped to [recyclarr]=${mappedIncludes.recyclarr.length}, [trash]=${mappedIncludes.trash.length} ...`, ); mappedIncludes.recyclarr.forEach((e) => { - const template = recyclarrTemplateMap.get(e.template); + const template = recyclarrTemplateMap.get(e.template) ?? localTemplateMap.get(e.template); if (!template) { logger.warn(`Unknown recyclarr template requested: ${e.template}`); @@ -91,6 +98,7 @@ const mergeConfigsAndTemplates = async ( } }); + // TODO: local trash guide QP templates do not work yet mappedIncludes.trash.forEach((e) => { const template = trashTemplates.get(e.template); diff --git a/src/local-importer.ts b/src/local-importer.ts new file mode 100644 index 0000000..ec8a9a2 --- /dev/null +++ b/src/local-importer.ts @@ -0,0 +1,69 @@ +import { default as fs } from "node:fs"; +import path from "node:path"; +import yaml from "yaml"; +import { getConfig } from "./config"; +import { logger } from "./logger"; +import { ArrType, MappedTemplates } from "./types/common.types"; +import { RecyclarrTemplates } from "./types/recyclarr.types"; + +export const getLocalTemplatePath = () => { + const config = getConfig(); + + if (config.localConfigTemplatesPath == null) { + logger.debug(`No local templates specified. Skipping.`); + return null; + } + + const customPath = path.resolve(config.localConfigTemplatesPath); + + if (!fs.existsSync(customPath)) { + logger.info(`Provided local templates path '${config.localCustomFormatsPath}' does not exist.`); + return null; + } + + return customPath; +}; + +export const loadLocalRecyclarrTemplate = (arrType: ArrType): Map => { + const map = new Map(); + + const fillMap = (path: string) => { + const files = fs.readdirSync(`${path}`).filter((fn) => fn.endsWith("yaml") || fn.endsWith("yml")); + + files.forEach((f) => map.set(f.substring(0, f.lastIndexOf(".")), yaml.parse(fs.readFileSync(`${path}/${f}`, "utf8")))); + }; + + const localPath = getLocalTemplatePath(); + + if (localPath) { + fillMap(localPath); + } + + logger.debug(`Found ${map.size} local templates.`); + + return new Map( + Array.from(map, ([k, v]) => { + const customFormats = v.custom_formats?.map((cf) => { + // Changes from Recyclarr 7.2.0: https://github.com/recyclarr/recyclarr/releases/tag/v7.2.0 + if (cf.assign_scores_to == null && cf.quality_profiles == null) { + logger.warn(`Local Template "${k}" does not provide correct profile for custom format. Ignoring.`); + } + + if (cf.quality_profiles) { + logger.warn( + `Deprecated: (Local Template '${k}') For custom_formats please rename 'quality_profiles' to 'assign_scores_to'. See recyclarr v7.2.0`, + ); + } + return { ...cf, assign_scores_to: cf.assign_scores_to ?? cf.quality_profiles ?? [] }; + }); + + return [ + k, + { + ...v, + custom_formats: customFormats, + }, + ]; + }), + ); +}; diff --git a/src/recyclarr-importer.ts b/src/recyclarr-importer.ts index f0b2fa1..017aa3f 100644 --- a/src/recyclarr-importer.ts +++ b/src/recyclarr-importer.ts @@ -1,5 +1,4 @@ import { default as fs } from "node:fs"; -import path from "node:path"; import yaml from "yaml"; import { getConfig } from "./config"; import { logger } from "./logger"; @@ -21,24 +20,6 @@ export const cloneRecyclarrTemplateRepo = async () => { logger.info(`Recyclarr repo: ref[${cloneResult.ref}], hash[${cloneResult.hash}], path[${cloneResult.localPath}]`); }; -export const getLocalTemplatePath = () => { - const config = getConfig(); - - if (config.localConfigTemplatesPath == null) { - logger.debug(`No local templates specified. Skipping.`); - return null; - } - - const customPath = path.resolve(config.localConfigTemplatesPath); - - if (!fs.existsSync(customPath)) { - logger.info(`Provided local templates path '${config.localCustomFormatsPath}' does not exist.`); - return null; - } - - return customPath; -}; - export const loadRecyclarrTemplates = (arrType: ArrType): Map => { const map = new Map(); @@ -58,12 +39,6 @@ export const loadRecyclarrTemplates = (arrType: ArrType): Map