diff --git a/PROJECT.org b/PROJECT.org index 56d1849..b01cc97 100644 --- a/PROJECT.org +++ b/PROJECT.org @@ -86,7 +86,7 @@ fetching and simple saving. **** Bookmarklet *** Metadata sources **** Scraper -* Backlog [0/12] :vrobbler:project:personal: +* Backlog [1/13] :vrobbler:project:personal: ** TODO [#C] Create small utility to clean up tracks scrobbled with wonky playback times :vrobbler:personal:bug:music:scrobbles: :PROPERTIES: :ID: 702462cf-d54b-48c6-8a7c-78b8de751deb @@ -498,6 +498,16 @@ needed import celery task. This is how the WebDAV celery task currently works. This would also be an opporunity to clean up the code around WebDAV imports and make them more re-usable for other import services. +** DONE [#B] Add fix_metadta method to Video instances :videos:metadata: +:PROPERTIES: +:ID: 9df5404d-1b60-4eee-b7cf-1f7e6dfade65 +:END: + +*** Description + +Turns out we don't have a fix_metadata method for videos. We should add that using +the basic logic from find_or_create on the Video model. + * Version 47.2 [1/1] ** DONE [#B] Add OMDB source as backup when TMDB returns nothing :videos:metadata:imdb: :PROPERTIES: diff --git a/vrobbler/apps/videos/models.py b/vrobbler/apps/videos/models.py index daf8714..7d40fe6 100644 --- a/vrobbler/apps/videos/models.py +++ b/vrobbler/apps/videos/models.py @@ -225,6 +225,8 @@ class Series(TimeStampedModel): def is_episode_playing(self, user_id: int) -> bool: last_scrobble = self.scrobbles_for_user(user_id, include_playing=True).first() + if not last_scrobble: + return False return not last_scrobble.played_to_completion def fix_metadata(self, force_update=False): @@ -266,9 +268,7 @@ class Series(TimeStampedModel): metadata = lookup_video_from_omdb(imdb_id) if not metadata or not metadata.title: - logger.warning( - f"No metadata found for series {imdb_id} from TMDB or OMDB" - ) + logger.warning(f"No metadata found for series {imdb_id} from TMDB or OMDB") return series vdict, _, cover, genres = metadata.as_dict_with_cover_and_genres() @@ -420,6 +420,84 @@ class Video(ScrobblableMixin): fname = f"{self.title}_{self.uuid}.jpg" self.cover_image.save(fname, ContentFile(r.content), save=True) + def fix_metadata(self, force_update: bool = False) -> None: + if self.video_type == self.VideoType.YOUTUBE and self.youtube_id: + vdict, _, cover, genres = lookup_video_from_youtube( + self.youtube_id + ).as_dict_with_cover_and_genres() + + for k, v in vdict.items(): + setattr(self, k, v) + self.save() + + if cover: + self.save_image_from_url(cover) + if genres: + self.genre.add(*genres) + return + + if self.video_type == self.VideoType.TWITCH and self.twitch_id: + from videos.sources.twitch import lookup_video_from_twitch + + metadata = lookup_video_from_twitch(self.twitch_id) + self.title = metadata.title or f"Twitch VOD {self.twitch_id}" + self.overview = metadata.overview + self.base_run_time_seconds = metadata.base_run_time_seconds + if metadata.upload_date: + self.upload_date = metadata.upload_date + if metadata.year: + self.year = metadata.year + self.video_type = Video.VideoType.TWITCH + + if metadata.channel_id: + from videos.models import Channel + + self.channel = Channel.objects.filter( + id=metadata.channel_id + ).first() + + self.save() + + if metadata.cover_url: + self.save_image_from_url(metadata.cover_url) + return + + if self.imdb_id: + try: + metadata = lookup_video_from_tmdb(self.imdb_id) + except Exception as e: + logger.warning(f"TMDB lookup failed for {self.imdb_id}: {e}") + metadata = None + + if not metadata or not metadata.title: + metadata = lookup_video_from_omdb(self.imdb_id) + + if not metadata or not metadata.title: + logger.warning(f"No metadata found for {self} from TMDB or OMDB") + return + + vdict, series_id, cover, genres = ( + metadata.as_dict_with_cover_and_genres() + ) + + for k, v in vdict.items(): + setattr(self, k, v) + + if series_id: + self.tv_series = Series.find_or_create(imdb_id=series_id) + + self.save() + + if cover: + self.save_image_from_url(cover) + if genres: + self.genre.add(*genres) + return + + logger.warning( + f"No metadata source available for {self} (type={self.video_type})" + ) + @classmethod def get_from_youtube_id(cls, youtube_id: str, overwrite: bool = False) -> "Video": video, created = cls.objects.get_or_create(youtube_id=youtube_id) @@ -456,9 +534,7 @@ class Video(ScrobblableMixin): metadata = lookup_video_from_omdb(imdb_id) if not metadata or not metadata.title: - logger.warning( - f"No metadata found for {imdb_id} from TMDB or OMDB" - ) + logger.warning(f"No metadata found for {imdb_id} from TMDB or OMDB") return video vdict, series_id, cover, genres = metadata.as_dict_with_cover_and_genres() diff --git a/vrobbler/apps/videos/views.py b/vrobbler/apps/videos/views.py index ab419d8..d0a5702 100644 --- a/vrobbler/apps/videos/views.py +++ b/vrobbler/apps/videos/views.py @@ -32,7 +32,11 @@ class SeriesDetailView(LoginRequiredMixin, ChartContextMixin, generic.DetailView context_data = super().get_context_data(**kwargs) context_data["scrobbles"] = self.object.scrobbles_for_user(user_id) - next_episode_id = self.object.last_scrobbled_episode(user_id).next_imdb_id or "" + next_episode_id = "" + if self.object.last_scrobbled_episode(user_id): + next_episode_id = ( + self.object.last_scrobbled_episode(user_id).next_imdb_id or "" + ) if self.object.is_episode_playing(user_id): next_episode_id = "" if next_episode_id: