diff --git a/addon/index.js b/addon/index.js index f9339725..4997cb06 100644 --- a/addon/index.js +++ b/addon/index.js @@ -117,6 +117,7 @@ function extractIdsFromMeta(meta) { if (!meta) return ids; const id = meta.id || ''; + if (id) ids.id = id; if (id.startsWith('tmdb:')) ids.tmdbId = id.slice(5); else if (id.startsWith('tvdb:')) ids.tvdbId = id.slice(5); else if (id.startsWith('kitsu:')) ids.kitsuId = id.slice(6); diff --git a/addon/tests/custom-art-id-placeholder.test.js b/addon/tests/custom-art-id-placeholder.test.js new file mode 100644 index 00000000..13d4c572 --- /dev/null +++ b/addon/tests/custom-art-id-placeholder.test.js @@ -0,0 +1,60 @@ +const test = require('node:test'); +const assert = require('node:assert/strict'); +const fs = require('node:fs'); +const path = require('node:path'); +const vm = require('node:vm'); + +function extractFunction(source, functionName) { + const startToken = `function ${functionName}`; + const startIndex = source.indexOf(startToken); + assert.notEqual(startIndex, -1, `Expected ${functionName} to exist`); + + const bodyStart = source.indexOf('{', startIndex); + assert.notEqual(bodyStart, -1, `Expected ${functionName} body`); + + let depth = 0; + for (let index = bodyStart; index < source.length; index += 1) { + const char = source[index]; + if (char === '{') depth += 1; + if (char === '}') depth -= 1; + if (depth === 0) { + return source.slice(startIndex, index + 1); + } + } + + throw new Error(`Could not extract ${functionName}`); +} + +const parsePropsSource = fs.readFileSync( + path.join(__dirname, '..', 'utils', 'parseProps.js'), + 'utf8', +); +const indexSource = fs.readFileSync( + path.join(__dirname, '..', 'index.js'), + 'utf8', +); + +const resolvePattern = vm.runInNewContext( + `(${extractFunction(parsePropsSource, 'resolvePattern')})`, +); +const extractIdsFromMeta = vm.runInNewContext( + `(${extractFunction(indexSource, 'extractIdsFromMeta')})`, +); + +test('extractIdsFromMeta preserves the raw meta id', () => { + const ids = extractIdsFromMeta({ id: 'mal:16498' }); + + assert.equal(ids.id, 'mal:16498'); + assert.equal(ids.malId, '16498'); +}); + +test('resolvePattern supports the raw id placeholder', () => { + const url = resolvePattern( + 'https://example.com/poster/{id}.jpg?lang={language_short}', + { id: 'mal:16498', malId: '16498' }, + 'series', + { language: 'en-US', apiKeys: {} }, + ); + + assert.equal(url, 'https://example.com/poster/mal:16498.jpg?lang=en'); +}); diff --git a/addon/utils/parseProps.js b/addon/utils/parseProps.js index 840738f7..6ef90306 100644 --- a/addon/utils/parseProps.js +++ b/addon/utils/parseProps.js @@ -66,7 +66,7 @@ function isPosterRatingEnabled(config) { * imdb: 'tt1234567', tmdb/tvdb: 'movie-12345' or 'series-12345' * * @param {string} pattern - URL pattern with placeholders - * @param {object} ids - Object with tmdbId, imdbId, tvdbId, malId, kitsuId, anilistId, anidbId properties + * @param {object} ids - Object with id, tmdbId, imdbId, tvdbId, malId, kitsuId, anilistId, anidbId properties * @param {string} type - Content type (movie, series) * @param {object} [config] - User config (for API key and language placeholders) * @returns {string|null} Resolved URL or null @@ -91,6 +91,7 @@ function resolvePattern(pattern, ids, type, config, extra) { const lang = config?.language || 'en-US'; const placeholders = { + '{id}': ids?.id || '', '{tmdb_id}': ids?.tmdbId || '', '{imdb_id}': ids?.imdbId || '', '{tvdb_id}': ids?.tvdbId || '', diff --git a/configure/src/components/sections/ArtProviderSettings.tsx b/configure/src/components/sections/ArtProviderSettings.tsx index 4495bf28..ec60a3f0 100644 --- a/configure/src/components/sections/ArtProviderSettings.tsx +++ b/configure/src/components/sections/ArtProviderSettings.tsx @@ -517,7 +517,7 @@ export function ArtProviderSettings() { : 'Override art with custom URL patterns. If a placeholder references an unavailable value, normal art is used instead.'}
- ID placeholders: {'{imdb_id}'}, {'{tmdb_id}'}, {'{tvdb_id}'}, {'{mal_id}'}, {'{kitsu_id}'}, {'{anilist_id}'}, {'{anidb_id}'}, {'{type}'}.
+ ID placeholders: {'{id}'}, {'{imdb_id}'}, {'{tmdb_id}'}, {'{tvdb_id}'}, {'{mal_id}'}, {'{kitsu_id}'}, {'{anilist_id}'}, {'{anidb_id}'}, {'{type}'}.
API keys: {'{rpdb_key}'}, {'{top_key}'}, {'{tmdb_key}'}, {'{mdblist_key}'}, {'{fanart_key}'}.
Language: {'{language}'} (e.g. fr-FR), {'{language_short}'} (e.g. fr).
RPDB/TOP patterns automatically fall back to alternative IDs when the primary one is unavailable.