sessions / b4180afbed4727572bbd4f9dd0c6b8cbfebc1d8e

/home/powellc/src/code.unbl.ink/secstate/vrobbler

This commit has no recorded session.

diff

ignore whitespace

commit b4180afbed4727572bbd4f9dd0c6b8cbfebc1d8eAuthor: Colin Powell <colin@unbl.ink>Date:   Wed Jun 17 12:05:22 2026 -0400    [videos] Fix metadata for series scriptdiff --git a/PROJECT.org b/PROJECT.orgindex 9427137..5f43c2f 100644--- a/PROJECT.org+++ b/PROJECT.org@@ -88,7 +88,7 @@ fetching and simple saving. *** Metadata sources **** Scraper -* Backlog [0/20] :vrobbler:project:personal:+* Backlog [1/21] :vrobbler:project:personal: ** TODO [#C] Create small utility to clean up tracks scrobbled with wonky playback times :bug:music:scrobbles: :PROPERTIES: :ID:       702462cf-d54b-48c6-8a7c-78b8de751deb@@ -590,6 +590,10 @@ We should rename `email_scrobble_board_game` to reflect the fact that it's just a helper method to create board game scrobbles given a json blob. It's independent of the email flow it was originally creatdd for +** DONE [#B] Fix bug in series metadata cleanup script :videos:metadta:+:PROPERTIES:+:ID:       85448702-907c-5d63-f5af-7795661d7c46+:END: * Version 54.2 [4/4] ** DONE [#B] Add script to clean up TV series metadata :videos:metadata: :PROPERTIES:diff --git a/vrobbler/apps/videos/management/commands/cleanup_series_metadata.py b/vrobbler/apps/videos/management/commands/cleanup_series_metadata.pyindex 94bb7eb..b70659c 100644--- a/vrobbler/apps/videos/management/commands/cleanup_series_metadata.py+++ b/vrobbler/apps/videos/management/commands/cleanup_series_metadata.py@@ -1,7 +1,7 @@ import logging  from django.core.management.base import BaseCommand-from django.db import transaction+from django.db import models, transaction  logger = logging.getLogger(__name__) @@ -25,6 +25,11 @@ class Command(BaseCommand):             type=str,             help="Only process series with this imdb_id",         )+        parser.add_argument(+            "--needs-metadata",+            action="store_true",+            help="Only process series missing imdb_id or cover image",+        )      def handle(self, *args, **options):         from videos.models import Series@@ -32,18 +37,30 @@ class Command(BaseCommand):         force = options["force"]         dry_run = options["dry_run"]         imdb_id = options["imdb_id"]+        needs_metadata = options["needs_metadata"]          qs = Series.objects.all()         if imdb_id:             qs = qs.filter(imdb_id=imdb_id)+        if needs_metadata:+            qs = qs.filter(+                models.Q(imdb_id__isnull=True)+                | models.Q(imdb_id="")+                | models.Q(cover_image__isnull=True)+                | models.Q(cover_image="")+            )          total = qs.count()         self.stdout.write(f"Processing {total} series")          if dry_run:             for series in qs.iterator():+                has_imdb = bool(series.imdb_id)+                has_image = bool(series.cover_image)                 self.stdout.write(-                    f"  [DRY RUN] Would fix {series.name} (imdb_id={series.imdb_id})"+                    f"  [DRY RUN] Would fix {series.name}"+                    f" (imdb_id={'✓' if has_imdb else '✗'}"+                    f", image={'✓' if has_image else '✗'})"                 )             return diff --git a/vrobbler/apps/videos/models.py b/vrobbler/apps/videos/models.pyindex be82333..0306a8a 100644--- a/vrobbler/apps/videos/models.py+++ b/vrobbler/apps/videos/models.py@@ -321,13 +321,27 @@ class Series(TimeStampedModel):         return not last_scrobble.played_to_completion      def fix_metadata(self, force_update=False):-        name_or_id = self.name-        if self.imdb_id:-            name_or_id = self.imdb_id-        video_metadata: VideoMetadata = lookup_video_from_tmdb(name_or_id)+        from tmdbv3api import TV++        if not self.imdb_id:+            tv = TV()+            results = tv.search(self.name)+            if results:+                show_id = results[0].id+                external_ids = tv.external_ids(show_id)+                if external_ids and external_ids.imdb_id:+                    self.imdb_id = external_ids.imdb_id+                    self.save(update_fields=["imdb_id"])+                else:+                    logger.warning(f"No IMDB ID found on TMDB for {self}")+                    return+            else:+                logger.warning(f"No results on TMDB for {self.name}")+                return -        if not video_metadata.title:-            logger.warning(f"No imdb data for {self}")+        video_metadata = lookup_video_from_tmdb(self.imdb_id)+        if not video_metadata or not video_metadata.title:+            logger.warning(f"No metadata for {self}")             return          if video_metadata.cover_url and (not self.cover_image or force_update):@@ -336,8 +350,8 @@ class Series(TimeStampedModel):                 fname = f"{self.name}_{self.uuid}.jpg"                 self.cover_image.save(fname, ContentFile(r.content), save=True) -        self.plot = video_metadata.plot-        self.imdb_rating = video_metadata.imdb_rating+        self.plot = video_metadata.plot or ""+        self.imdb_rating = getattr(video_metadata, "imdb_rating", None)         self.save()          if video_metadata.genres:diff --git a/vrobbler/apps/videos/sources/tmdb.py b/vrobbler/apps/videos/sources/tmdb.pyindex 82ca9b8..e34c4fb 100644--- a/vrobbler/apps/videos/sources/tmdb.py+++ b/vrobbler/apps/videos/sources/tmdb.py@@ -1,4 +1,5 @@ import logging+import os  import pendulum from django.conf import settings@@ -8,6 +9,8 @@ from videos.metadata import VideoMetadata, VideoType  TMDB_KEY = getattr(settings, "TMDB_API_KEY", "") +os.environ.setdefault("TMDB_API_KEY", TMDB_KEY)+ tmdb = TMDb(key=TMDB_KEY, language="en-US", region="US")  TMDB_IMAGE_URL = "https://image.tmdb.org/t/p/original"@@ -43,6 +46,28 @@ def lookup_video_from_tmdb(name_or_id: str, kind: str = "movie") -> VideoMetadat         )  # TODO: enrich this with TMDB url         video_metadata.year = pendulum.parse(media.release_date).year         video_metadata.genres = [g.get("name", "") for g in media.genres]+        video_metadata.tmdb_id = media.id+        video_metadata.base_run_time_seconds = media.runtime * 60+        video_metadata.plot = media.overview+        video_metadata.overview = media.overview+        video_metadata.tmdb_rating = media.vote_average++    if len(tmdb_result.tv_results) > 0:+        media = TV().details(tmdb_result.tv_results[0].id)+        video_metadata.video_type = VideoType.TV_EPISODE.value+        video_metadata.title = media.name+        video_metadata.cover_url = (+            TMDB_IMAGE_URL + media.poster_path+        )+        video_metadata.year = pendulum.parse(media.first_air_date).year if media.first_air_date else None+        video_metadata.genres = [g.get("name", "") for g in media.genres]+        video_metadata.tmdb_id = media.id+        video_metadata.base_run_time_seconds = (+            media.episode_run_time[0] * 60 if media.episode_run_time else 1800+        )+        video_metadata.plot = media.overview+        video_metadata.overview = media.overview+        video_metadata.tmdb_rating = media.vote_average      if len(tmdb_result.tv_episode_results) > 0:         video_metadata.video_type = VideoType.TV_EPISODE.value@@ -63,15 +88,12 @@ def lookup_video_from_tmdb(name_or_id: str, kind: str = "movie") -> VideoMetadat             series.save()         video_metadata.tv_series_id = series.id +        video_metadata.tmdb_id = media.id+        video_metadata.plot = media.overview+        video_metadata.overview = media.overview+     if not media:         logger.warning("Video not found on TMDB", extra={"imdb_id": imdb_id})         return video_metadata -    video_metadata.tmdb_id = media.id-    video_metadata.base_run_time_seconds = media.runtime * 60-    video_metadata.plot = media.overview-    video_metadata.overview = media.overview-    video_metadata.tmdb_rating = media.vote_average-    # video_metadata.next_imdb_id = imdb_result.get("next episode", None)-     return video_metadata