@@ -35,6 +35,7 @@ type Movie struct {
3535 Title string `json:"title"`
3636 Year int `json:"year"`
3737 Label []Label `json:"Label,omitempty"`
38+ Genre []Genre `json:"Genre,omitempty"`
3839 Guid FlexibleGuid `json:"Guid,omitempty"`
3940 Media []Media `json:"Media,omitempty"`
4041}
@@ -43,6 +44,10 @@ type Label struct {
4344 Tag string `json:"tag"`
4445}
4546
47+ type Genre struct {
48+ Tag string `json:"tag"`
49+ }
50+
4651type Guid struct {
4752 ID string `json:"id"`
4853}
@@ -205,18 +210,25 @@ func main() {
205210 // Start the periodic processing
206211 fmt .Println ("\n 🔄 Starting periodic movie processing..." )
207212
213+ // Add UPDATE_FIELD env variable
214+ updateField := getEnvWithDefault ("UPDATE_FIELD" , "labels" )
215+ if updateField != "labels" && updateField != "genre" {
216+ fmt .Println ("❌ UPDATE_FIELD must be 'labels' or 'genre'" )
217+ os .Exit (1 )
218+ }
219+
208220 processFunc := func () {
209221 if processAllMovieLibraries {
210222 for _ , lib := range movieLibraries {
211223 fmt .Printf ("\n ==============================\n " )
212224 fmt .Printf ("🎬 Processing library: %s (ID: %s)\n " , lib .Title , lib .Key )
213225 libConfig := config
214226 libConfig .LibraryID = lib .Key
215- processAllMovies (libConfig )
227+ processAllMovies (libConfig , updateField )
216228 }
217229 } else {
218230 config .LibraryID = movieLibraries [0 ].Key
219- processAllMovies (config )
231+ processAllMovies (config , updateField )
220232 }
221233 }
222234
@@ -236,7 +248,7 @@ func main() {
236248 }
237249}
238250
239- func processAllMovies (config Config ) {
251+ func processAllMovies (config Config , updateField string ) {
240252 fmt .Println ("\n 📋 Fetching all movies from library..." )
241253 movies , err := getMoviesFromLibrary (config )
242254 if err != nil {
@@ -257,73 +269,71 @@ func processAllMovies(config Config) {
257269 skippedMovies := 0
258270
259271 for i , movie := range movies {
260- // Check if movie was already processed
261272 processed , exists := processedMovies [movie .RatingKey ]
262273 if exists && processed .KeywordsSynced {
263274 skippedMovies ++
264275 continue
265276 }
266277
267- // Extract TMDb ID from movie first (before any output)
268278 tmdbID := extractTMDbID (movie )
269279 if tmdbID == "" {
270280 fmt .Printf ("⚠️ No TMDb ID found for movie: %s\n " , movie .Title )
271281 continue
272282 }
273283
274- // Get TMDb keywords
275284 keywords , err := getTMDbKeywords (config , tmdbID )
276285 if err != nil {
277286 fmt .Printf ("❌ Error fetching TMDb keywords for %s: %v\n " , movie .Title , err )
278287 continue
279288 }
280289
281- // Fetch detailed movie information to get current labels
282290 movieDetails , err := getMovieDetails (config , movie .RatingKey )
283291 if err != nil {
284292 fmt .Printf ("❌ Error fetching movie details for %s: %v\n " , movie .Title , err )
285293 continue
286294 }
287295
288- // Get current movie labels from detailed fetch
289- currentLabels := make ([]string , len (movieDetails .Label ))
290- for j , label := range movieDetails .Label {
291- currentLabels [j ] = label .Tag
296+ var currentValues []string
297+ if updateField == "labels" {
298+ currentValues = make ([]string , len (movieDetails .Label ))
299+ for j , label := range movieDetails .Label {
300+ currentValues [j ] = label .Tag
301+ }
302+ } else {
303+ currentValues = make ([]string , len (movieDetails .Genre ))
304+ for j , genre := range movieDetails .Genre {
305+ currentValues [j ] = genre .Tag
306+ }
292307 }
293308
294- // Check if all keywords already exist as labels (case-insensitive)
295- currentLabelsMap := make (map [string ]bool )
296- for _ , label := range currentLabels {
297- currentLabelsMap [strings .ToLower (label )] = true
309+ currentValuesMap := make (map [string ]bool )
310+ for _ , val := range currentValues {
311+ currentValuesMap [strings .ToLower (val )] = true
298312 }
299313
300314 allKeywordsExist := true
301315 for _ , keyword := range keywords {
302- if ! currentLabelsMap [strings .ToLower (keyword )] {
316+ if ! currentValuesMap [strings .ToLower (keyword )] {
303317 allKeywordsExist = false
304318 break
305319 }
306320 }
307321
308- // If all keywords already exist, skip silently
309322 if allKeywordsExist {
310323 skippedMovies ++
311324 continue
312325 }
313326
314- // Only show processing output for movies that need updates
315327 fmt .Printf ("\n 🎬 Processing movie %d/%d: %s (%d)\n " , i + 1 , len (movies ), movie .Title , movie .Year )
316328 fmt .Printf ("🔑 TMDb ID: %s (%s)\n " , tmdbID , movie .Title )
317329 fmt .Printf ("🏷️ Found %d TMDb keywords\n " , len (keywords ))
318330
319- // Sync labels with keywords
320- err = syncMovieLabelsWithKeywords (config , movie .RatingKey , currentLabels , keywords )
331+ err = syncMovieFieldWithKeywords (config , movie .RatingKey , currentValues , keywords , updateField )
321332 if err != nil {
322- fmt .Printf ("❌ Error syncing labels : %v\n " , err )
333+ fmt .Printf ("❌ Error syncing %s : %v\n " , updateField , err )
323334 continue
324335 }
325336
326- // Update processed movies dictionary
327337 processedMovies [movie .RatingKey ] = & ProcessedMovie {
328338 RatingKey : movie .RatingKey ,
329339 Title : movie .Title ,
@@ -339,8 +349,6 @@ func processAllMovies(config Config) {
339349 }
340350
341351 fmt .Printf ("✅ Successfully processed: %s\n " , movie .Title )
342-
343- // Small delay to avoid overwhelming the APIs
344352 time .Sleep (500 * time .Millisecond )
345353 }
346354
@@ -456,50 +464,50 @@ func getTMDbKeywords(config Config, tmdbID string) ([]string, error) {
456464 return keywords , nil
457465}
458466
459- func syncMovieLabelsWithKeywords (config Config , movieID string , currentLabels []string , keywords []string ) error {
460- // Convert current labels to a map for easy lookup (case-insensitive)
461- currentLabelsMap := make (map [string ]bool )
462- for _ , label := range currentLabels {
463- currentLabelsMap [strings .ToLower (label )] = true
467+ func syncMovieFieldWithKeywords (config Config , movieID string , currentValues []string , keywords []string , updateField string ) error {
468+ currentValuesMap := make (map [string ]bool )
469+ for _ , val := range currentValues {
470+ currentValuesMap [strings .ToLower (val )] = true
464471 }
465472
466- // Find keywords to add (keywords not in current labels, case-insensitive comparison)
467- labelsToAdd := make ([]string , 0 )
473+ valuesToAdd := make ([]string , 0 )
468474 for _ , keyword := range keywords {
469- if ! currentLabelsMap [strings .ToLower (keyword )] {
470- labelsToAdd = append (labelsToAdd , keyword )
475+ if ! currentValuesMap [strings .ToLower (keyword )] {
476+ valuesToAdd = append (valuesToAdd , keyword )
471477 }
472478 }
473479
474- fmt .Printf (" 📝 Labels to add: %v\n " , labelsToAdd )
475- fmt .Printf (" 🏷️ Existing labels : %v\n " , currentLabels )
480+ fmt .Printf (" 📝 %s to add: %v\n " , strings . Title ( updateField ), valuesToAdd )
481+ fmt .Printf (" 🏷️ Existing %s : %v\n " , updateField , currentValues )
476482
477- // Merge existing labels with new keywords
478- allLabels := make ([]string , 0 , len (currentLabels )+ len (labelsToAdd ))
479- allLabels = append (allLabels , currentLabels ... )
480- allLabels = append (allLabels , labelsToAdd ... )
483+ allValues := make ([]string , 0 , len (currentValues )+ len (valuesToAdd ))
484+ allValues = append (allValues , currentValues ... )
485+ allValues = append (allValues , valuesToAdd ... )
481486
482- // Update movie labels with combined list
483- return updateMovieLabelsWithKeywords (config , movieID , allLabels )
487+ return updateMovieFieldWithKeywords (config , movieID , allValues , updateField )
484488}
485489
486- func updateMovieLabelsWithKeywords (config Config , movieID string , keywords []string ) error {
487- // Build the base URL path
490+ func updateMovieFieldWithKeywords (config Config , movieID string , keywords []string , updateField string ) error {
488491 basePath := fmt .Sprintf ("/library/sections/%s/all?type=1&id=%s&includeExternalMedia=1" , config .LibraryID , movieID )
489492
490- // Add each keyword as a label parameter
491- for i , keyword := range keywords {
492- encodedKeyword := url .QueryEscape (keyword )
493- basePath += fmt .Sprintf ("&label%%5B%d%%5D.tag.tag=%s" , i , encodedKeyword )
493+ if updateField == "labels" {
494+ for i , keyword := range keywords {
495+ encodedKeyword := url .QueryEscape (keyword )
496+ basePath += fmt .Sprintf ("&label%%5B%d%%5D.tag.tag=%s" , i , encodedKeyword )
497+ }
498+ basePath += "&label.locked=1"
499+ } else {
500+ for i , keyword := range keywords {
501+ encodedKeyword := url .QueryEscape (keyword )
502+ basePath += fmt .Sprintf ("&genre%%5B%d%%5D.tag.tag=%s" , i , encodedKeyword )
503+ }
504+ basePath += "&genre.locked=1"
494505 }
506+ basePath += fmt .Sprintf ("&X-Plex-Token=%s" , config .PlexToken )
495507
496- // Add label lock and token
497- basePath += fmt .Sprintf ("&label.locked=1&X-Plex-Token=%s" , config .PlexToken )
498-
499- // Build the complete URL
500508 updateURL := buildPlexURL (config , basePath )
501509
502- fmt .Printf (" 📤 Updating movie labels ...\n " )
510+ fmt .Printf (" 📤 Updating movie %s ...\n " , updateField )
503511
504512 req , err := http .NewRequest ("PUT" , updateURL , nil )
505513 if err != nil {
0 commit comments