diff --git a/PROJECT.org b/PROJECT.org index 179e0da..222982a 100644 --- a/PROJECT.org +++ b/PROJECT.org @@ -88,7 +88,7 @@ fetching and simple saving. *** Metadata sources **** Scraper -* Backlog [3/23] :vrobbler:project:personal: +* Backlog [4/24] :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] Add script to clean up TV series metadata :videos:metadata: +:PROPERTIES: +:ID: a468b328-59d9-f84b-9ddb-087216783453 +:END: ** DONE [#A] Update youtube video detail pages with links to channel :videos:templates: :PROPERTIES: :ID: 8b87cb42-09e5-a3f5-136f-182f967fa81f diff --git a/vrobbler/apps/videos/management/commands/cleanup_series_metadata.py b/vrobbler/apps/videos/management/commands/cleanup_series_metadata.py new file mode 100644 index 0000000..94bb7eb --- /dev/null +++ b/vrobbler/apps/videos/management/commands/cleanup_series_metadata.py @@ -0,0 +1,68 @@ +import logging + +from django.core.management.base import BaseCommand +from django.db import transaction + +logger = logging.getLogger(__name__) + + +class Command(BaseCommand): + help = "Enrich TV series metadata from TMDB/OMDB APIs" + + def add_arguments(self, parser): + parser.add_argument( + "--force", + action="store_true", + help="Overwrite existing cover image", + ) + parser.add_argument( + "--dry-run", + action="store_true", + help="Show what would be done without making changes", + ) + parser.add_argument( + "--imdb-id", + type=str, + help="Only process series with this imdb_id", + ) + + def handle(self, *args, **options): + from videos.models import Series + + force = options["force"] + dry_run = options["dry_run"] + imdb_id = options["imdb_id"] + + qs = Series.objects.all() + if imdb_id: + qs = qs.filter(imdb_id=imdb_id) + + total = qs.count() + self.stdout.write(f"Processing {total} series") + + if dry_run: + for series in qs.iterator(): + self.stdout.write( + f" [DRY RUN] Would fix {series.name} (imdb_id={series.imdb_id})" + ) + return + + updated = 0 + errors = 0 + for series in qs.iterator(): + try: + with transaction.atomic(): + series.fix_metadata(force_update=force) + updated += 1 + self.stdout.write(f" [{updated}/{total}] {series.name}") + except Exception as e: + errors += 1 + self.stdout.write( + self.style.ERROR( + f" Error updating series {series.name} (imdb_id={series.imdb_id}): {e}" + ) + ) + + self.stdout.write( + self.style.SUCCESS(f"\nDone! {updated} series updated, {errors} errors") + ) diff --git a/vrobbler/apps/videos/models.py b/vrobbler/apps/videos/models.py index c64c87a..be82333 100644 --- a/vrobbler/apps/videos/models.py +++ b/vrobbler/apps/videos/models.py @@ -330,16 +330,18 @@ class Series(TimeStampedModel): logger.warning(f"No imdb data for {self}") return - cover_url = imdb_dict.get("cover_url") - - if (not self.cover_image or force_update) and cover_url: - r = requests.get(cover_url) + if video_metadata.cover_url and (not self.cover_image or force_update): + r = requests.get(video_metadata.cover_url) if r.status_code == 200: fname = f"{self.name}_{self.uuid}.jpg" self.cover_image.save(fname, ContentFile(r.content), save=True) - if genres := imdb_dict.get("genres"): - self.genre.add(*genres) + self.plot = video_metadata.plot + self.imdb_rating = video_metadata.imdb_rating + self.save() + + if video_metadata.genres: + self.genre.add(*video_metadata.genres) @classmethod def find_or_create(cls, imdb_id: str, overwrite: bool = True):