This commit has no recorded session.
diff
commit b4180afbed4727572bbd4f9dd0c6b8cbfebc1d8eAuthor: Colin Powell <colin@unbl.ink>Date: Wed Jun 17 12:05:22 2026 -0400 [videos] Fix metadata for series script@@ -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:@@ -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 @@ -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:@@ -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