Fixes movie details after db changes.

This commit is contained in:
SergeantPanda 2025-08-07 20:42:20 -05:00
parent 3741d28565
commit 325d832c1b
3 changed files with 160 additions and 127 deletions

View file

@ -23,7 +23,7 @@ from .serializers import (
M3USeriesRelationSerializer,
M3UEpisodeRelationSerializer
)
from .tasks import refresh_series_episodes
from .tasks import refresh_series_episodes, refresh_movie_advanced_data
from django.utils import timezone
from datetime import timedelta
@ -88,7 +88,7 @@ class MovieViewSet(viewsets.ReadOnlyModelViewSet):
@action(detail=True, methods=['get'], url_path='provider-info')
def provider_info(self, request, pk=None):
"""Get detailed movie information from the original provider"""
"""Get detailed movie information from the original provider, throttled to 24h."""
movie = self.get_object()
# Get the first active relation
@ -103,130 +103,62 @@ class MovieViewSet(viewsets.ReadOnlyModelViewSet):
status=status.HTTP_400_BAD_REQUEST
)
# Check if detailed data has been fetched
force_refresh = request.query_params.get('force_refresh', 'false').lower() == 'true'
now = timezone.now()
needs_refresh = (
force_refresh or
not relation.last_advanced_refresh or
(now - relation.last_advanced_refresh).total_seconds() > 86400
)
if needs_refresh:
# Trigger advanced data refresh
logger.debug(f"Refreshing advanced data for movie {movie.id} (relation ID: {relation.id})")
refresh_movie_advanced_data(relation.id, force_refresh=force_refresh)
# Use cached advanced data
custom_props = relation.custom_properties or {}
detailed_fetched = custom_props.get('detailed_fetched', False)
# If detailed data hasn't been fetched, fetch it now
if not detailed_fetched:
try:
from core.xtream_codes import Client as XtreamCodesClient
with XtreamCodesClient(
server_url=relation.m3u_account.server_url,
username=relation.m3u_account.username,
password=relation.m3u_account.password,
user_agent=relation.m3u_account.get_user_agent().user_agent
) as client:
# Get detailed VOD info from provider
vod_info = client.get_vod_info(relation.stream_id)
if vod_info and 'info' in vod_info:
# Update movie with detailed info
info = vod_info.get('info', {})
movie_data = vod_info.get('movie_data', {})
movie.description = info.get('plot', movie.description)
movie.rating = info.get('rating', movie.rating)
movie.genre = info.get('genre', movie.genre)
movie.duration = self._convert_duration_to_minutes(info.get('duration_secs'))
if info.get('releasedate'):
movie.year = self._extract_year(info.get('releasedate'))
movie.save()
# Update relation with detailed data
custom_props['detailed_info'] = info
custom_props['movie_data'] = movie_data
custom_props['detailed_fetched'] = True
relation.custom_properties = custom_props
relation.save()
except Exception as e:
logger.error(f"Error fetching detailed VOD info for movie {pk}: {str(e)}")
# Continue with available data
try:
from core.xtream_codes import Client as XtreamCodesClient
# Create XtreamCodes client for final response (minimal call)
with XtreamCodesClient(
server_url=relation.m3u_account.server_url,
username=relation.m3u_account.username,
password=relation.m3u_account.password,
user_agent=relation.m3u_account.get_user_agent().user_agent
) as client:
# Use cached detailed data if available
custom_props = relation.custom_properties or {}
info = custom_props.get('detailed_info', {})
movie_data = custom_props.get('movie_data', {})
# If no cached data, use basic data
if not info:
basic_data = custom_props.get('basic_data', {})
info = {
'name': movie.name,
'plot': movie.description,
'rating': movie.rating,
'genre': movie.genre,
}
movie_data = {
'container_extension': basic_data.get('container_extension', 'mp4'),
'added': basic_data.get('added', ''),
}
# Build response with available data
response_data = {
'id': movie.id,
'stream_id': relation.stream_id,
'name': info.get('name', movie.name),
'o_name': info.get('o_name', ''),
'description': info.get('description', info.get('plot', movie.description)),
'plot': info.get('plot', info.get('description', movie.description)),
'year': movie.year or self._extract_year(info.get('releasedate', '')),
'release_date': info.get('release_date', ''),
'releasedate': info.get('releasedate', ''),
'genre': info.get('genre', movie.genre),
'director': info.get('director', ''),
'actors': info.get('actors', info.get('cast', '')),
'cast': info.get('cast', info.get('actors', '')),
'country': info.get('country', ''),
'rating': info.get('rating', movie.rating or 0),
'tmdb_id': info.get('tmdb_id', movie.tmdb_id or ''),
'youtube_trailer': info.get('youtube_trailer') or info.get('trailer', ''),
'duration': movie.duration or self._convert_duration_to_minutes(info.get('duration_secs', 0)),
'duration_secs': info.get('duration_secs', (movie.duration or 0) * 60),
'episode_run_time': info.get('episode_run_time', 0),
'age': info.get('age', ''),
'backdrop_path': info.get('backdrop_path', []),
'cover': info.get('cover_big', ''),
'cover_big': info.get('cover_big', ''),
'movie_image': info.get('movie_image', ''),
'bitrate': info.get('bitrate', 0),
'video': info.get('video', {}),
'audio': info.get('audio', {}),
# Include movie_data fields
'container_extension': movie_data.get('container_extension', 'mp4'),
'direct_source': movie_data.get('direct_source', ''),
'category_id': movie_data.get('category_id', ''),
'added': movie_data.get('added', ''),
# Include M3U account info
'm3u_account': {
'id': relation.m3u_account.id,
'name': relation.m3u_account.name,
'account_type': relation.m3u_account.account_type
}
}
return Response(response_data)
except Exception as e:
logger.error(f"Error in provider info for movie {pk}: {str(e)}")
return Response(
{'error': f'Failed to fetch information from provider: {str(e)}'},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)
info = custom_props.get('detailed_info', {})
movie_data = custom_props.get('movie_data', {})
# Build response with available data
response_data = {
'id': movie.id,
'stream_id': relation.stream_id,
'name': info.get('name', movie.name),
'o_name': info.get('o_name', ''),
'description': info.get('description', info.get('plot', movie.description)),
'plot':info.get('plot', info.get('description', movie.description)),
'year': movie.year or info.get('year'),
'release_date': (movie.custom_properties or {}).get('release_date') or info.get('release_date') or info.get('releasedate', ''),
'genre': movie.genre or info.get('genre', ''),
'director': (movie.custom_properties or {}).get('director') or info.get('director', ''),
'actors': (movie.custom_properties or {}).get('actors') or info.get('actors', ''),
'country': (movie.custom_properties or {}).get('country') or info.get('country', ''),
'rating': movie.rating or info.get('rating', movie.rating or 0),
'tmdb_id': movie.tmdb_id or info.get('tmdb_id', ''),
'youtube_trailer': (movie.custom_properties or {}).get('youtube_trailer') or info.get('youtube_trailer') or info.get('trailer', ''),
'duration': movie.duration or (int(info.get('duration_secs', 0)) // 60 if info.get('duration_secs') else None),
'duration_secs': info.get('duration_secs', (movie.duration or 0) * 60),
'age': info.get('age', ''),
'backdrop_path': (movie.custom_properties or {}).get('backdrop_path') or info.get('backdrop_path', []),
'cover': info.get('cover_big', ''),
'cover_big': info.get('cover_big', ''),
'movie_image': movie.logo.url or info.get('movie_image', ''),
'bitrate': info.get('bitrate', 0),
'video': info.get('video', {}),
'audio': info.get('audio', {}),
'container_extension': movie_data.get('container_extension', 'mp4'),
'direct_source': movie_data.get('direct_source', ''),
'category_id': movie_data.get('category_id', ''),
'added': movie_data.get('added', ''),
'm3u_account': {
'id': relation.m3u_account.id,
'name': relation.m3u_account.name,
'account_type': relation.m3u_account.account_type
}
}
return Response(response_data)
class EpisodeFilter(django_filters.FilterSet):
name = django_filters.CharFilter(lookup_expr="icontains")

View file

@ -183,6 +183,7 @@ class M3UMovieRelation(models.Model):
# Timestamps
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
last_advanced_refresh = models.DateTimeField(blank=True, null=True, help_text="Last time advanced data was fetched from provider")
class Meta:
verbose_name = 'M3U Movie Relation'

View file

@ -424,8 +424,8 @@ def find_or_create_movie(name, year, tmdb_id, imdb_id, info):
# If we found an existing movie, update it
if movie:
updated = False
if info.get('plot') and info.get('plot') != movie.description:
movie.description = info.get('plot')
if (info.get('plot') or info.get('description')) and (info.get('plot') or info.get('description')) != movie.description:
movie.description = info.get('plot') or info.get('description')
updated = True
if info.get('rating') and info.get('rating') != movie.rating:
movie.rating = info.get('rating')
@ -478,7 +478,7 @@ def find_or_create_movie(name, year, tmdb_id, imdb_id, info):
year=year,
tmdb_id=tmdb_id,
imdb_id=imdb_id,
description=info.get('plot', ''),
description=info.get('plot') or info.get('description', ''),
rating=info.get('rating', ''),
genre=info.get('genre', ''),
duration=convert_duration_to_minutes(info.get('duration_secs')),
@ -692,3 +692,103 @@ def parse_date(date_string):
return datetime.strptime(date_string, '%Y-%m-%d')
except ValueError:
return None # Return None if parsing fails
from django.utils import timezone
from apps.vod.models import M3UMovieRelation, Movie
@shared_task
def refresh_movie_advanced_data(m3u_movie_relation_id, force_refresh=False):
"""
Fetch advanced movie data from provider and update Movie and M3UMovieRelation.
Only fetch if last_advanced_refresh > 24h ago, unless force_refresh is True.
"""
try:
relation = M3UMovieRelation.objects.select_related('movie', 'm3u_account').get(id=m3u_movie_relation_id)
now = timezone.now()
if not force_refresh and relation.last_advanced_refresh and (now - relation.last_advanced_refresh).total_seconds() < 86400:
return "Advanced data recently fetched, skipping."
account = relation.m3u_account
movie = relation.movie
from core.xtream_codes import Client as XtreamCodesClient
with XtreamCodesClient(
server_url=account.server_url,
username=account.username,
password=account.password,
user_agent=account.get_user_agent().user_agent
) as client:
vod_info = client.get_vod_info(relation.stream_id)
if vod_info and 'info' in vod_info:
info = vod_info.get('info', {})
movie_data = vod_info.get('movie_data', {})
# Update Movie fields if changed
updated = False
custom_props = movie.custom_properties or {}
if info.get('plot') and info.get('plot') != movie.description:
movie.description = info.get('plot')
updated = True
if info.get('rating') and info.get('rating') != movie.rating:
movie.rating = info.get('rating')
updated = True
if info.get('genre') and info.get('genre') != movie.genre:
movie.genre = info.get('genre')
updated = True
if info.get('duration_secs'):
duration = int(info.get('duration_secs')) // 60
if duration != movie.duration:
movie.duration = duration
updated = True
# Check for releasedate or release_date
release_date_value = info.get('releasedate') or info.get('release_date')
if release_date_value:
try:
year = int(str(release_date_value).split('-')[0])
if year != movie.year:
movie.year = year
updated = True
except Exception:
pass
if info.get('tmdb_id') and info.get('tmdb_id') != movie.tmdb_id:
movie.tmdb_id = info.get('tmdb_id')
updated = True
if info.get('imdb_id') and info.get('imdb_id') != movie.imdb_id:
movie.imdb_id = info.get('imdb_id')
updated = True
if info.get('trailer') and info.get('trailer') != custom_props.get('youtube_trailer'):
custom_props['youtube_trailer'] = info.get('trailer')
updated = True
if info.get('youtube_trailer') and info.get('youtube_trailer') != custom_props.get('youtube_trailer'):
custom_props['youtube_trailer'] = info.get('youtube_trailer')
updated = True
if info.get('backdrop_path') and info.get('backdrop_path') != custom_props.get('backdrop_path'):
custom_props['backdrop_path'] = info.get('backdrop_path')
updated = True
if info.get('actors') and info.get('actors') != custom_props.get('actors'):
custom_props['actors'] = info.get('actors')
updated = True
if info.get('cast') and info.get('cast') != custom_props.get('actors'):
custom_props['actors'] = info.get('cast')
updated = True
if info.get('director') and info.get('director') != custom_props.get('director'):
custom_props['director'] = info.get('director')
updated = True
if updated:
movie.custom_properties = custom_props
movie.save()
# Update relation custom_properties and last_advanced_refresh
custom_props = relation.custom_properties or {}
custom_props['detailed_info'] = info
custom_props['movie_data'] = movie_data
custom_props['detailed_fetched'] = True
relation.custom_properties = custom_props
relation.last_advanced_refresh = now
relation.save(update_fields=['custom_properties', 'last_advanced_refresh'])
return "Advanced data refreshed."
except Exception as e:
logger.error(f"Error refreshing advanced movie data for relation {m3u_movie_relation_id}: {str(e)}")
return f"Error: {str(e)}"