Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5a4ef678a8 | |||
| 5ca22efeaa | |||
| 912ea8bfac | |||
| b541e1084d | |||
| c9b9da4abc |
10
PROJECT.org
10
PROJECT.org
@ -498,7 +498,8 @@ 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:
|
* Version 47.2 [1/1]
|
||||||
|
** 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:
|
||||||
@ -512,6 +513,13 @@ epiodes up in three parts, while they were always broadcast three-in-one, and
|
|||||||
that's how IMDB lists them. Thus, the IMDB ID means nothing, and the videos end
|
that's how IMDB lists them. Thus, the IMDB ID means nothing, and the videos end
|
||||||
up unenriched.
|
up unenriched.
|
||||||
|
|
||||||
|
|
||||||
|
* Version 47.1 [1/1]
|
||||||
|
** DONE [#A] Untangle the sports migrations errors :sports:bug:migrations:
|
||||||
|
:PROPERTIES:
|
||||||
|
:ID: 4d50ca2e-f45b-dde8-e3c9-cd84f353b349
|
||||||
|
:END:
|
||||||
|
|
||||||
* Version 47.0 [1/1]
|
* Version 47.0 [1/1]
|
||||||
** DONE [#B] Change sports scrobbling a bit :feature:sports:scrobbles:
|
** DONE [#B] Change sports scrobbling a bit :feature:sports:scrobbles:
|
||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "vrobbler"
|
name = "vrobbler"
|
||||||
version = "47.0"
|
version = "47.2"
|
||||||
description = ""
|
description = ""
|
||||||
authors = ["Colin Powell <colin@unbl.ink>"]
|
authors = ["Colin Powell <colin@unbl.ink>"]
|
||||||
|
|
||||||
|
|||||||
@ -108,20 +108,4 @@ class Migration(migrations.Migration):
|
|||||||
migrations.RunPython(
|
migrations.RunPython(
|
||||||
migrate_sport_event_data, reverse_code=migrations.RunPython.noop
|
migrate_sport_event_data, reverse_code=migrations.RunPython.noop
|
||||||
),
|
),
|
||||||
migrations.RemoveField(
|
|
||||||
model_name="sportevent",
|
|
||||||
name="home_team",
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name="sportevent",
|
|
||||||
name="away_team",
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name="sportevent",
|
|
||||||
name="player_one",
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name="sportevent",
|
|
||||||
name="player_two",
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
|
|||||||
@ -0,0 +1,27 @@
|
|||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("sports", "0021_team_logo"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name="sportevent",
|
||||||
|
name="home_team",
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name="sportevent",
|
||||||
|
name="away_team",
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name="sportevent",
|
||||||
|
name="player_one",
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name="sportevent",
|
||||||
|
name="player_two",
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -71,6 +71,7 @@ class VideoMetadata:
|
|||||||
twitch_id: Optional[str] = "",
|
twitch_id: Optional[str] = "",
|
||||||
base_run_time_seconds: int = 900,
|
base_run_time_seconds: int = 900,
|
||||||
):
|
):
|
||||||
|
self.title = ""
|
||||||
self.imdb_id = imdb_id
|
self.imdb_id = imdb_id
|
||||||
self.youtube_id = youtube_id
|
self.youtube_id = youtube_id
|
||||||
self.twitch_id = twitch_id
|
self.twitch_id = twitch_id
|
||||||
|
|||||||
@ -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():
|
||||||
|
|||||||
83
vrobbler/apps/videos/sources/omdb.py
Normal file
83
vrobbler/apps/videos/sources/omdb.py
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
if media_type == "episode":
|
||||||
|
raw_season = data.get("Season")
|
||||||
|
if raw_season and raw_season != "N/A":
|
||||||
|
metadata.season_number = int(raw_season)
|
||||||
|
raw_episode = data.get("Episode")
|
||||||
|
if raw_episode and raw_episode != "N/A":
|
||||||
|
metadata.episode_number = int(raw_episode)
|
||||||
|
series_imdb_id = data.get("seriesID")
|
||||||
|
if series_imdb_id and series_imdb_id != "N/A":
|
||||||
|
metadata.tv_series_imdb_id = series_imdb_id
|
||||||
|
|
||||||
|
raw_genres = data.get("Genre")
|
||||||
|
if raw_genres:
|
||||||
|
metadata.genres = [g.strip() for g in raw_genres.split(",") if g.strip()]
|
||||||
|
|
||||||
|
return metadata
|
||||||
@ -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
|
||||||
|
|||||||
@ -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")
|
||||||
|
|||||||
Reference in New Issue
Block a user