[videos] Add OMDB enrichment when TMDb fails

This commit is contained in:
2026-06-07 09:47:52 -04:00
parent b541e1084d
commit 912ea8bfac
5 changed files with 110 additions and 13 deletions

View File

@ -86,7 +86,7 @@ fetching and simple saving.
**** Bookmarklet **** Bookmarklet
*** Metadata sources *** Metadata sources
**** Scraper **** Scraper
* Backlog [0/13] :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: ** TODO [#C] Create small utility to clean up tracks scrobbled with wonky playback times :vrobbler:personal:bug:music:scrobbles:
:PROPERTIES: :PROPERTIES:
:ID: 702462cf-d54b-48c6-8a7c-78b8de751deb :ID: 702462cf-d54b-48c6-8a7c-78b8de751deb
@ -498,7 +498,7 @@ 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 This would also be an opporunity to clean up the code around WebDAV imports
and make them more re-usable for other import services. and make them more re-usable for other import services.
** TODO [#B] Add OMDB source as backup when TMDB returns nothing :videos:metadata:imdb: ** DONE [#B] Add OMDB source as backup when TMDB returns nothing :videos:metadata:imdb:
:PROPERTIES: :PROPERTIES:
:ID: 20195445-7fdd-49be-9767-103b12da0bfb :ID: 20195445-7fdd-49be-9767-103b12da0bfb
:END: :END:

View File

@ -20,6 +20,7 @@ from scrobbles.mixins import (
) )
from taggit.managers import TaggableManager from taggit.managers import TaggableManager
from videos.metadata import VideoMetadata from videos.metadata import VideoMetadata
from videos.sources.omdb import lookup_video_from_omdb
from videos.sources.tmdb import lookup_video_from_tmdb from videos.sources.tmdb import lookup_video_from_tmdb
from videos.sources.youtube import lookup_video_from_youtube from videos.sources.youtube import lookup_video_from_youtube
@ -255,9 +256,22 @@ class Series(TimeStampedModel):
logger.info("Series not created and overwrite=False, returning") logger.info("Series not created and overwrite=False, returning")
return series return series
vdict, _, cover, genres = lookup_video_from_tmdb( try:
imdb_id metadata = lookup_video_from_tmdb(imdb_id)
).as_dict_with_cover_and_genres() except Exception as e:
logger.warning(f"TMDB lookup failed for series {imdb_id}: {e}")
metadata = None
if not metadata or not metadata.title:
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"
)
return series
vdict, _, cover, genres = metadata.as_dict_with_cover_and_genres()
vdict.pop("video_type") vdict.pop("video_type")
vdict["name"] = vdict.pop("title") vdict["name"] = vdict.pop("title")
@ -432,9 +446,22 @@ class Video(ScrobblableMixin):
if not created and not overwrite: if not created and not overwrite:
return video return video
vdict, series_id, cover, genres = lookup_video_from_tmdb( try:
imdb_id metadata = lookup_video_from_tmdb(imdb_id)
).as_dict_with_cover_and_genres() except Exception as e:
logger.warning(f"TMDB lookup failed for {imdb_id}: {e}")
metadata = None
if not metadata or not metadata.title:
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"
)
return video
vdict, series_id, cover, genres = metadata.as_dict_with_cover_and_genres()
if created or overwrite: if created or overwrite:
for k, v in vdict.items(): for k, v in vdict.items():

View File

@ -0,0 +1,72 @@
import logging
import re
from typing import Optional
import requests
from django.conf import settings
from videos.metadata import VideoMetadata, VideoType
logger = logging.getLogger(__name__)
OMDB_API_KEY = getattr(settings, "OMDB_API_KEY", "")
OMDB_URL = "https://www.omdbapi.com/"
RUNTIME_RE = re.compile(r"(\d+)\s*min")
def lookup_video_from_omdb(imdb_id: str) -> Optional[VideoMetadata]:
if not imdb_id.startswith("tt"):
imdb_id = f"tt{imdb_id}"
if not OMDB_API_KEY:
logger.warning("No OMDB API key configured")
return None
params = {"apikey": OMDB_API_KEY, "i": imdb_id, "plot": "full"}
try:
response = requests.get(OMDB_URL, params=params)
response.raise_for_status()
data = response.json()
except requests.exceptions.RequestException as e:
logger.error(f"OMDB API error for {imdb_id}: {e}")
return None
if data.get("Response") == "False":
logger.info(f"OMDB no result for {imdb_id}: {data.get('Error')}")
return None
metadata = VideoMetadata(imdb_id=imdb_id)
metadata.title = data.get("Title")
metadata.plot = data.get("Plot")
metadata.overview = data.get("Plot")
raw_year = data.get("Year")
if raw_year and raw_year.isdigit():
metadata.year = int(raw_year)
raw_rating = data.get("imdbRating")
if raw_rating and raw_rating != "N/A":
metadata.imdb_rating = raw_rating
raw_cover = data.get("Poster")
if raw_cover and raw_cover != "N/A":
metadata.cover_url = raw_cover
raw_runtime = data.get("Runtime")
if raw_runtime:
match = RUNTIME_RE.match(raw_runtime)
if match:
metadata.base_run_time_seconds = int(match.group(1)) * 60
media_type = data.get("Type")
if media_type == "movie":
metadata.video_type = VideoType.MOVIE.value
elif media_type in ("series", "episode"):
metadata.video_type = VideoType.TV_EPISODE.value
raw_genres = data.get("Genre")
if raw_genres:
metadata.genres = [g.strip() for g in raw_genres.split(",") if g.strip()]
return metadata

View File

@ -3,14 +3,11 @@ import logging
import pendulum import pendulum
from django.conf import settings from django.conf import settings
from themoviedb import TMDb from themoviedb import TMDb
from tmdbv3api import TV, Movie, TMDb as TMDb_direct from tmdbv3api import TV, Movie
from videos.metadata import VideoMetadata, VideoType from videos.metadata import VideoMetadata, VideoType
TMDB_KEY = getattr(settings, "TMDB_API_KEY", "") TMDB_KEY = getattr(settings, "TMDB_API_KEY", "")
tmdb_direct = TMDb_direct()
tmdb_direct.api_key = TMDB_KEY
tmdb = TMDb(key=TMDB_KEY, language="en-US", region="US") tmdb = TMDb(key=TMDB_KEY, language="en-US", region="US")
TMDB_IMAGE_URL = "https://image.tmdb.org/t/p/original" TMDB_IMAGE_URL = "https://image.tmdb.org/t/p/original"
@ -36,7 +33,7 @@ def lookup_video_from_tmdb(name_or_id: str, kind: str = "movie") -> VideoMetadat
video_metadata = VideoMetadata(imdb_id=imdb_id) video_metadata = VideoMetadata(imdb_id=imdb_id)
media = None media = None
show = None show_data = None
if len(tmdb_result.movie_results) > 0: if len(tmdb_result.movie_results) > 0:
media = Movie().details(tmdb_result.movie_results[0].id) media = Movie().details(tmdb_result.movie_results[0].id)
video_metadata.video_type = VideoType.MOVIE.value video_metadata.video_type = VideoType.MOVIE.value

View File

@ -60,6 +60,7 @@ THEAUDIODB_API_KEY = os.getenv("VROBBLER_THEAUDIODB_API_KEY", "2")
PODCASTINDEX_API_KEY = os.getenv("VROBBLER_PODCASTINDEX_API_KEY", "") PODCASTINDEX_API_KEY = os.getenv("VROBBLER_PODCASTINDEX_API_KEY", "")
PODCASTINDEX_API_SECRET = os.getenv("VROBBLER_PODCASTINDEX_API_SECRET", "") PODCASTINDEX_API_SECRET = os.getenv("VROBBLER_PODCASTINDEX_API_SECRET", "")
TMDB_API_KEY = os.getenv("VROBBLER_TMDB_API_KEY", "") TMDB_API_KEY = os.getenv("VROBBLER_TMDB_API_KEY", "")
OMDB_API_KEY = os.getenv("VROBBLER_OMDB_API_KEY", "")
LASTFM_API_KEY = os.getenv("VROBBLER_LASTFM_API_KEY") LASTFM_API_KEY = os.getenv("VROBBLER_LASTFM_API_KEY")
LASTFM_SECRET_KEY = os.getenv("VROBBLER_LASTFM_SECRET_KEY") LASTFM_SECRET_KEY = os.getenv("VROBBLER_LASTFM_SECRET_KEY")
IGDB_CLIENT_ID = os.getenv("VROBBLER_IGDB_CLIENT_ID") IGDB_CLIENT_ID = os.getenv("VROBBLER_IGDB_CLIENT_ID")