Skip to content

Commit de2d4d3

Browse files
committed
fix(api): genre scanning issue
fix(web): scan progress indication in the button
1 parent 35c3c57 commit de2d4d3

18 files changed

Lines changed: 1450 additions & 894 deletions

File tree

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
/**
2+
* Genre mapping configuration
3+
*
4+
* This file centralizes genre name normalization across different metadata providers.
5+
* Each provider may return genre names in different formats, and this mapping ensures
6+
* consistency in the database.
7+
*
8+
* Example:
9+
* - TMDB might return "Science Fiction"
10+
* - Another provider might return "Sci-Fi"
11+
* - Both should map to "Science Fiction" in our database
12+
*/
13+
14+
/**
15+
* Normalized genre names used in the database
16+
* These are the canonical genre names we use across the application
17+
*/
18+
export const NORMALIZED_GENRES = {
19+
// Movies & TV Shows
20+
ACTION: "Action",
21+
ADVENTURE: "Adventure",
22+
ANIMATION: "Animation",
23+
COMEDY: "Comedy",
24+
CRIME: "Crime",
25+
DOCUMENTARY: "Documentary",
26+
DRAMA: "Drama",
27+
FAMILY: "Family",
28+
FANTASY: "Fantasy",
29+
HISTORY: "History",
30+
HORROR: "Horror",
31+
MUSIC: "Music",
32+
MYSTERY: "Mystery",
33+
ROMANCE: "Romance",
34+
SCIENCE_FICTION: "Science Fiction",
35+
TV_MOVIE: "TV Movie",
36+
THRILLER: "Thriller",
37+
WAR: "War",
38+
WESTERN: "Western",
39+
40+
// TV Specific
41+
ACTION_ADVENTURE: "Action & Adventure",
42+
KIDS: "Kids",
43+
NEWS: "News",
44+
REALITY: "Reality",
45+
SOAP: "Soap",
46+
TALK: "Talk",
47+
WAR_POLITICS: "War & Politics",
48+
49+
// Comics
50+
SUPERHERO: "Superhero",
51+
MANGA: "Manga",
52+
GRAPHIC_NOVEL: "Graphic Novel",
53+
54+
// Music
55+
ROCK: "Rock",
56+
POP: "Pop",
57+
JAZZ: "Jazz",
58+
CLASSICAL: "Classical",
59+
ELECTRONIC: "Electronic",
60+
HIP_HOP: "Hip Hop",
61+
METAL: "Metal",
62+
COUNTRY: "Country",
63+
BLUES: "Blues",
64+
FOLK: "Folk",
65+
REGGAE: "Reggae",
66+
RNB: "R&B",
67+
INDIE: "Indie",
68+
ALTERNATIVE: "Alternative",
69+
PUNK: "Punk",
70+
} as const;
71+
72+
/**
73+
* Genre mapping table
74+
* Maps various genre name variations to their normalized form
75+
*
76+
* Key: lowercase version of what providers might return
77+
* Value: normalized genre name from NORMALIZED_GENRES
78+
*/
79+
export const GENRE_MAPPING: Record<string, string> = {
80+
// Action variations
81+
action: NORMALIZED_GENRES.ACTION,
82+
83+
// Adventure variations
84+
adventure: NORMALIZED_GENRES.ADVENTURE,
85+
86+
// Animation variations
87+
animation: NORMALIZED_GENRES.ANIMATION,
88+
animated: NORMALIZED_GENRES.ANIMATION,
89+
90+
// Comedy variations
91+
comedy: NORMALIZED_GENRES.COMEDY,
92+
93+
// Crime variations
94+
crime: NORMALIZED_GENRES.CRIME,
95+
96+
// Documentary variations
97+
documentary: NORMALIZED_GENRES.DOCUMENTARY,
98+
documentaries: NORMALIZED_GENRES.DOCUMENTARY,
99+
100+
// Drama variations
101+
drama: NORMALIZED_GENRES.DRAMA,
102+
103+
// Family variations
104+
family: NORMALIZED_GENRES.FAMILY,
105+
106+
// Fantasy variations
107+
fantasy: NORMALIZED_GENRES.FANTASY,
108+
109+
// History variations
110+
history: NORMALIZED_GENRES.HISTORY,
111+
historical: NORMALIZED_GENRES.HISTORY,
112+
113+
// Horror variations
114+
horror: NORMALIZED_GENRES.HORROR,
115+
116+
// Music variations
117+
music: NORMALIZED_GENRES.MUSIC,
118+
musical: NORMALIZED_GENRES.MUSIC,
119+
120+
// Mystery variations
121+
mystery: NORMALIZED_GENRES.MYSTERY,
122+
123+
// Romance variations
124+
romance: NORMALIZED_GENRES.ROMANCE,
125+
romantic: NORMALIZED_GENRES.ROMANCE,
126+
127+
// Science Fiction variations
128+
"science fiction": NORMALIZED_GENRES.SCIENCE_FICTION,
129+
"sci-fi": NORMALIZED_GENRES.SCIENCE_FICTION,
130+
scifi: NORMALIZED_GENRES.SCIENCE_FICTION,
131+
"science-fiction": NORMALIZED_GENRES.SCIENCE_FICTION,
132+
133+
// TV Movie
134+
"tv movie": NORMALIZED_GENRES.TV_MOVIE,
135+
136+
// Thriller variations
137+
thriller: NORMALIZED_GENRES.THRILLER,
138+
139+
// War variations
140+
war: NORMALIZED_GENRES.WAR,
141+
142+
// Western variations
143+
western: NORMALIZED_GENRES.WESTERN,
144+
145+
// TV Specific
146+
"action & adventure": NORMALIZED_GENRES.ACTION_ADVENTURE,
147+
"action and adventure": NORMALIZED_GENRES.ACTION_ADVENTURE,
148+
kids: NORMALIZED_GENRES.KIDS,
149+
children: NORMALIZED_GENRES.KIDS,
150+
news: NORMALIZED_GENRES.NEWS,
151+
reality: NORMALIZED_GENRES.REALITY,
152+
"reality-tv": NORMALIZED_GENRES.REALITY,
153+
soap: NORMALIZED_GENRES.SOAP,
154+
talk: NORMALIZED_GENRES.TALK,
155+
"war & politics": NORMALIZED_GENRES.WAR_POLITICS,
156+
"war and politics": NORMALIZED_GENRES.WAR_POLITICS,
157+
158+
// Comics
159+
superhero: NORMALIZED_GENRES.SUPERHERO,
160+
"super hero": NORMALIZED_GENRES.SUPERHERO,
161+
"super-hero": NORMALIZED_GENRES.SUPERHERO,
162+
manga: NORMALIZED_GENRES.MANGA,
163+
"graphic novel": NORMALIZED_GENRES.GRAPHIC_NOVEL,
164+
165+
// Music genres
166+
rock: NORMALIZED_GENRES.ROCK,
167+
pop: NORMALIZED_GENRES.POP,
168+
jazz: NORMALIZED_GENRES.JAZZ,
169+
classical: NORMALIZED_GENRES.CLASSICAL,
170+
electronic: NORMALIZED_GENRES.ELECTRONIC,
171+
"hip hop": NORMALIZED_GENRES.HIP_HOP,
172+
"hip-hop": NORMALIZED_GENRES.HIP_HOP,
173+
hiphop: NORMALIZED_GENRES.HIP_HOP,
174+
rap: NORMALIZED_GENRES.HIP_HOP,
175+
metal: NORMALIZED_GENRES.METAL,
176+
"heavy metal": NORMALIZED_GENRES.METAL,
177+
country: NORMALIZED_GENRES.COUNTRY,
178+
blues: NORMALIZED_GENRES.BLUES,
179+
folk: NORMALIZED_GENRES.FOLK,
180+
reggae: NORMALIZED_GENRES.REGGAE,
181+
"r&b": NORMALIZED_GENRES.RNB,
182+
rnb: NORMALIZED_GENRES.RNB,
183+
"rhythm and blues": NORMALIZED_GENRES.RNB,
184+
indie: NORMALIZED_GENRES.INDIE,
185+
alternative: NORMALIZED_GENRES.ALTERNATIVE,
186+
punk: NORMALIZED_GENRES.PUNK,
187+
};
188+
189+
/**
190+
* Normalize a genre name to its canonical form
191+
*
192+
* @param genreName - The genre name from a metadata provider
193+
* @returns The normalized genre name, or the original if no mapping exists
194+
*/
195+
export function normalizeGenreName(genreName: string): string {
196+
const lowercased = genreName.toLowerCase().trim();
197+
return GENRE_MAPPING[lowercased] || genreName;
198+
}
199+
200+
/**
201+
* Create a slug from a genre name
202+
*
203+
* @param genreName - The genre name
204+
* @returns A URL-friendly slug
205+
*/
206+
export function createGenreSlug(genreName: string): string {
207+
return genreName
208+
.toLowerCase()
209+
.trim()
210+
.replace(/[^\w\s-]/g, "") // Remove special characters
211+
.replace(/\s+/g, "-") // Replace spaces with hyphens
212+
.replace(/-+/g, "-"); // Replace multiple hyphens with single hyphen
213+
}

apps/api/src/lib/bulk/bulk.service.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,7 @@ class BulkOperationsService {
519519
);
520520

521521
if (freshMetadata) {
522+
// Update media fields
522523
await prisma.media.update({
523524
where: { id: mediaId },
524525
data: {
@@ -530,6 +531,16 @@ class BulkOperationsService {
530531
rating: freshMetadata.rating,
531532
},
532533
});
534+
535+
// Update genres if available
536+
if (freshMetadata.genres && freshMetadata.genres.length > 0) {
537+
const { genreService } = await import("../genreService.js");
538+
await genreService.updateGenresForMedia(
539+
mediaId,
540+
freshMetadata.genres
541+
);
542+
}
543+
533544
logger.info(`Refreshed metadata for ${media.type} ${mediaId}`);
534545
result.queued++;
535546
} else {

0 commit comments

Comments
 (0)