diff --git a/.cache/emby-user-context.json b/.cache/emby-user-context.json
index 37db410..59a013f 100644
--- a/.cache/emby-user-context.json
+++ b/.cache/emby-user-context.json
@@ -1386,7 +1386,7 @@
"isPlayed": true
},
{
- "id": "1330174",
+ "id": "1345802",
"name": "Project Nazi: The Blueprints of Evil",
"type": "Series",
"seriesName": null,
@@ -1410,15 +1410,7 @@
"isPlayed": true
},
{
- "id": "1329610",
- "name": "Prodigal Son",
- "type": "Series",
- "seriesName": null,
- "datePlayed": null,
- "isPlayed": true
- },
- {
- "id": "1330186",
+ "id": "1345819",
"name": "We Are Who We Are",
"type": "Series",
"seriesName": null,
@@ -1426,7 +1418,7 @@
"isPlayed": true
},
{
- "id": "1328552",
+ "id": "1345818",
"name": "WandaVision",
"type": "Series",
"seriesName": null,
@@ -1434,7 +1426,7 @@
"isPlayed": true
},
{
- "id": "1328551",
+ "id": "1345812",
"name": "The Falcon and The Winter Soldier",
"type": "Series",
"seriesName": null,
@@ -1442,7 +1434,7 @@
"isPlayed": true
},
{
- "id": "1328548",
+ "id": "1345806",
"name": "Snowpiercer",
"type": "Series",
"seriesName": null,
@@ -1450,15 +1442,15 @@
"isPlayed": true
},
{
- "id": "1330183",
- "name": "The Outsider",
+ "id": "1345815",
+ "name": "The Outsider (2020)",
"type": "Series",
"seriesName": null,
"datePlayed": null,
"isPlayed": true
},
{
- "id": "1328549",
+ "id": "1345808",
"name": "Station Eleven",
"type": "Series",
"seriesName": null,
@@ -1466,7 +1458,7 @@
"isPlayed": true
},
{
- "id": "1330988",
+ "id": "1348189",
"name": "Moon Knight",
"type": "Series",
"seriesName": null,
@@ -1474,7 +1466,7 @@
"isPlayed": true
},
{
- "id": "1330175",
+ "id": "1345803",
"name": "Rise of the Nazis",
"type": "Series",
"seriesName": null,
@@ -1482,23 +1474,15 @@
"isPlayed": true
},
{
- "id": "1329606",
- "name": "Coyote",
+ "id": "1345813",
+ "name": "The Head (2020)",
"type": "Series",
"seriesName": null,
"datePlayed": null,
"isPlayed": true
},
{
- "id": "1330182",
- "name": "The Head",
- "type": "Series",
- "seriesName": null,
- "datePlayed": null,
- "isPlayed": true
- },
- {
- "id": "1312988",
+ "id": "1344688",
"name": "Domina",
"type": "Series",
"seriesName": null,
@@ -1506,7 +1490,7 @@
"isPlayed": true
},
{
- "id": "1330172",
+ "id": "1345800",
"name": "Obi-Wan Kenobi",
"type": "Series",
"seriesName": null,
@@ -1514,7 +1498,7 @@
"isPlayed": true
},
{
- "id": "1328550",
+ "id": "1345810",
"name": "The Book of Boba Fett",
"type": "Series",
"seriesName": null,
@@ -1522,7 +1506,7 @@
"isPlayed": true
},
{
- "id": "1330178",
+ "id": "1345807",
"name": "Stanley Tucci: Searching for Italy",
"type": "Series",
"seriesName": null,
@@ -1546,7 +1530,7 @@
"isPlayed": true
},
{
- "id": "1330989",
+ "id": "1348190",
"name": "The Man Who Fell to Earth",
"type": "Series",
"seriesName": null,
@@ -1619,7 +1603,7 @@
}
],
"excludedFolderLookup": {},
- "lastSyncedAt": "2026-04-26T22:31:15.087Z"
+ "lastSyncedAt": "2026-04-28T01:58:15.752Z"
},
"ff5a825760c24f9ab6f63b04513909a4": {
"views": [
diff --git a/src/lib/GenreCleanupPage.svelte b/src/lib/GenreCleanupPage.svelte
new file mode 100644
index 0000000..1b04364
--- /dev/null
+++ b/src/lib/GenreCleanupPage.svelte
@@ -0,0 +1,662 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ event.key === 'Enter' && searchLibrary()}
+ placeholder={mediaType === 'Movie' ? 'Search movies...' : 'Search shows...'}
+ />
+
+
+ The suggested genre uses TMDB order as a hint, but you can change the choice before writing it back to Emby.
+
+
+ {#if searchError}
+
{searchError}
+ {/if}
+ {#if actionError}
+
{actionError}
+ {:else if actionMessage}
+
{actionMessage}
+ {/if}
+
+
+
+
+ {#if !searchBusy && !searchResults.length}
+ Search for a movie or show to start cleaning up its genres.
+ {:else}
+
+ {#each searchResults as item}
+ {@const inspection = inspections[item.id]}
+
+
+
+
+ Emby
+ {(inspection?.currentGenres || item.genres || []).join(', ') || 'None'}
+
+
+ {#if inspection?.error}
+
{inspection.error}
+ {:else if inspection?.item}
+
+
+ TMDB
+ {inspection.tmdb?.genres?.join(', ') || 'No genres returned'}
+
+
+ Match
+
+ {#if inspection.tmdb?.tmdbId}
+ {inspection.tmdb.source === 'providerId' ? 'Provider ID match' : 'Title search match'} · TMDB {inspection.tmdb.tmdbId}
+ {:else}
+ No TMDB match
+ {/if}
+
+
+
+ {#if inspection.tmdb?.genres?.length}
+
+
+
+
+
+
+ {/if}
+
+ {/if}
+
+ {/each}
+
+ {/if}
+
+
+
+
+
+
+
+
diff --git a/src/lib/genre-cleanup.js b/src/lib/genre-cleanup.js
new file mode 100644
index 0000000..19cb0b7
--- /dev/null
+++ b/src/lib/genre-cleanup.js
@@ -0,0 +1,54 @@
+function normalizeGenreKey(value) {
+ return String(value || '')
+ .trim()
+ .toLowerCase()
+ .replace(/[^a-z0-9]+/g, '');
+}
+
+export function normalizeGenreNames(values) {
+ const seen = new Set();
+ const names = [];
+
+ for (const value of values || []) {
+ const trimmed = String(value || '').trim();
+ if (!trimmed) continue;
+ const key = normalizeGenreKey(trimmed);
+ if (!key || seen.has(key)) continue;
+ seen.add(key);
+ names.push(trimmed);
+ }
+
+ return names;
+}
+
+export function pickSuggestedGenre(tmdbGenres, currentGenres = []) {
+ const normalizedTmdbGenres = normalizeGenreNames(tmdbGenres);
+ if (!normalizedTmdbGenres.length) return '';
+
+ const currentKeys = new Set(normalizeGenreNames(currentGenres).map(normalizeGenreKey));
+ const matched = normalizedTmdbGenres.find((genre) => currentKeys.has(normalizeGenreKey(genre)));
+
+ return matched || normalizedTmdbGenres[0];
+}
+
+export function buildSingleGenreUpdate(item, genreName) {
+ const selectedGenre = String(genreName || '').trim();
+ if (!selectedGenre) {
+ throw new Error('A genre is required');
+ }
+
+ const nextItem = JSON.parse(JSON.stringify(item || {}));
+ const existingGenreItems = Array.isArray(item?.GenreItems) ? item.GenreItems : [];
+ const matchedGenreItem = existingGenreItems.find(
+ (entry) => normalizeGenreKey(entry?.Name) === normalizeGenreKey(selectedGenre)
+ );
+
+ nextItem.Genres = [selectedGenre];
+ nextItem.GenreItems = [
+ matchedGenreItem
+ ? { ...matchedGenreItem, Name: selectedGenre }
+ : { Name: selectedGenre }
+ ];
+
+ return nextItem;
+}
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
index a5bf34b..2b7fe8b 100644
--- a/src/routes/+page.svelte
+++ b/src/routes/+page.svelte
@@ -1,6 +1,7 @@