From 9ab6e53cf6d0d8f0b1be9e90555aa441baad7d01 Mon Sep 17 00:00:00 2001 From: advplyr Date: Sun, 21 Dec 2025 15:27:23 -0600 Subject: [PATCH] Update custom metadata provider to dedupe tags/genres and return tags as array --- server/providers/CustomProviderAdapter.js | 25 +++++++- test-validateTagsGenres.js | 77 +++++++++++++++++++++++ 2 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 test-validateTagsGenres.js diff --git a/server/providers/CustomProviderAdapter.js b/server/providers/CustomProviderAdapter.js index c079a128..5c8cad75 100644 --- a/server/providers/CustomProviderAdapter.js +++ b/server/providers/CustomProviderAdapter.js @@ -89,6 +89,27 @@ class CustomProviderAdapter { }) .filter((s) => s !== undefined) } + /** + * Validates and dedupes tags/genres array + * Can be comma separated string or array of strings + * @param {string|string[]} tagsGenres + * @returns {string[]} + */ + const validateTagsGenresArray = (tagsGenres) => { + if (!tagsGenres || (typeof tagsGenres !== 'string' && !Array.isArray(tagsGenres))) return undefined + + // If string, split by comma and trim each item + if (typeof tagsGenres === 'string') tagsGenres = tagsGenres.split(',') + // If array, ensure all items are strings + else if (!tagsGenres.every((t) => typeof t === 'string')) return undefined + + // Trim and filter out empty strings + tagsGenres = tagsGenres.map((t) => t.trim()).filter(Boolean) + if (!tagsGenres.length) return undefined + + // Dedup + return [...new Set(tagsGenres)] + } // re-map keys to throw out return matches.map((match) => { @@ -105,8 +126,8 @@ class CustomProviderAdapter { cover: toStringOrUndefined(cover), isbn: toStringOrUndefined(isbn), asin: toStringOrUndefined(asin), - genres: Array.isArray(genres) && genres.every((g) => typeof g === 'string') ? genres : undefined, - tags: toStringOrUndefined(tags), + genres: validateTagsGenresArray(genres), + tags: validateTagsGenresArray(tags), series: validateSeriesArray(series), language: toStringOrUndefined(language), duration: !isNaN(duration) && duration !== null ? Number(duration) : undefined diff --git a/test-validateTagsGenres.js b/test-validateTagsGenres.js new file mode 100644 index 00000000..c28a335e --- /dev/null +++ b/test-validateTagsGenres.js @@ -0,0 +1,77 @@ +// Quick test for validateTagsGenresArray function +const validateTagsGenresArray = (tagsGenres) => { + if (!tagsGenres || (typeof tagsGenres !== 'string' && !Array.isArray(tagsGenres))) return undefined + + // If string, split by comma and trim each item + if (typeof tagsGenres === 'string') tagsGenres = tagsGenres.split(',') + // If array, ensure all items are strings + else if (!tagsGenres.every((t) => typeof t === 'string')) return undefined + + // Trim and filter out empty strings + tagsGenres = tagsGenres.map((t) => t.trim()).filter(Boolean) + if (!tagsGenres.length) return undefined + + // Dedup + return [...new Set(tagsGenres)] +} + +// Test cases +console.log('Test 1 - String with commas:') +console.log(validateTagsGenresArray('tag1, tag2, tag3')) +console.log('Expected: ["tag1", "tag2", "tag3"]') + +console.log('\nTest 2 - Array of strings:') +console.log(validateTagsGenresArray(['tag1', 'tag2', 'tag3'])) +console.log('Expected: ["tag1", "tag2", "tag3"]') + +console.log('\nTest 3 - Duplicates:') +console.log(validateTagsGenresArray(['tag1', 'tag2', 'tag1', 'tag3', 'tag2'])) +console.log('Expected: ["tag1", "tag2", "tag3"]') + +console.log('\nTest 4 - String with duplicates:') +console.log(validateTagsGenresArray('tag1, tag2, tag1, tag3')) +console.log('Expected: ["tag1", "tag2", "tag3"]') + +console.log('\nTest 5 - Extra whitespace:') +console.log(validateTagsGenresArray(' tag1 , tag2 , tag3 ')) +console.log('Expected: ["tag1", "tag2", "tag3"]') + +console.log('\nTest 6 - Empty string:') +console.log(validateTagsGenresArray('')) +console.log('Expected: undefined') + +console.log('\nTest 7 - String with only commas:') +console.log(validateTagsGenresArray(',,,')) +console.log('Expected: undefined') + +console.log('\nTest 8 - Empty array:') +console.log(validateTagsGenresArray([])) +console.log('Expected: undefined') + +console.log('\nTest 9 - null:') +console.log(validateTagsGenresArray(null)) +console.log('Expected: undefined') + +console.log('\nTest 10 - undefined:') +console.log(validateTagsGenresArray(undefined)) +console.log('Expected: undefined') + +console.log('\nTest 11 - Array with non-strings:') +console.log(validateTagsGenresArray([1, 2, 3])) +console.log('Expected: undefined') + +console.log('\nTest 12 - Array with mixed types:') +console.log(validateTagsGenresArray(['tag1', 2, 'tag3'])) +console.log('Expected: undefined') + +console.log('\nTest 13 - Array with empty strings:') +console.log(validateTagsGenresArray(['tag1', '', 'tag2', ' '])) +console.log('Expected: ["tag1", "tag2"]') + +console.log('\nTest 14 - Single item:') +console.log(validateTagsGenresArray('tag1')) +console.log('Expected: ["tag1"]') + +console.log('\nTest 15 - Object (invalid):') +console.log(validateTagsGenresArray({ tag: 'value' })) +console.log('Expected: undefined')