diff options
| author | Bobby <[email protected]> | 2024-09-01 21:09:06 -0400 |
|---|---|---|
| committer | Bobby <[email protected]> | 2024-09-01 21:09:06 -0400 |
| commit | 562d0172688fc81c2134a31692c0da4b2b2e9501 (patch) | |
| tree | 736266a24770d0d80d3b166ec3a7a24443bb3f77 | |
| parent | aff11dfbab143a177ba320b93359e862d5217a88 (diff) | |
| download | yugen-562d0172688fc81c2134a31692c0da4b2b2e9501.tar.xz yugen-562d0172688fc81c2134a31692c0da4b2b2e9501.zip | |
episode metadata
| -rw-r--r-- | static/css/main.css | 127 | ||||
| -rw-r--r-- | templates/watch/watch.html | 7 | ||||
| -rw-r--r-- | watch/tmdbmapper.py | 424 | ||||
| -rw-r--r-- | watch/views.py | 38 |
4 files changed, 480 insertions, 116 deletions
diff --git a/static/css/main.css b/static/css/main.css index c6ee6a9..a0ea458 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -673,16 +673,12 @@ video { z-index: 10; } -.z-50 { - z-index: 50; -} - .z-20 { z-index: 20; } -.z-30 { - z-index: 30; +.z-50 { + z-index: 50; } .-mx-4 { @@ -705,11 +701,6 @@ video { margin-bottom: 2rem; } -.-mx-2 { - margin-left: -0.5rem; - margin-right: -0.5rem; -} - .mb-2 { margin-bottom: 0.5rem; } @@ -750,14 +741,6 @@ video { margin-top: 2rem; } -.mt-\[0\.35rem\] { - margin-top: 0.35rem; -} - -.ml-1 { - margin-left: 0.25rem; -} - .block { display: block; } @@ -802,9 +785,8 @@ video { height: 1.5rem; } -.size-5 { - width: 1.25rem; - height: 1.25rem; +.h-16 { + height: 4rem; } .h-24 { @@ -847,18 +829,10 @@ video { height: 100%; } -.h-16 { - height: 4rem; -} - .max-h-24 { max-height: 6rem; } -.max-h-screen { - max-height: 100vh; -} - .max-h-96 { max-height: 24rem; } @@ -871,6 +845,10 @@ video { width: 2.5rem; } +.w-12 { + width: 3rem; +} + .w-16 { width: 4rem; } @@ -887,6 +865,10 @@ video { width: 1rem; } +.w-48 { + width: 12rem; +} + .w-5 { width: 1.25rem; } @@ -916,32 +898,20 @@ video { width: max-content; } -.w-48 { - width: 12rem; -} - -.w-12 { - width: 3rem; -} - .min-w-32 { min-width: 8rem; } -.min-w-96 { - min-width: 24rem; -} - -.min-w-80 { - min-width: 20rem; +.max-w-7xl { + max-width: 80rem; } -.min-w-72 { - min-width: 18rem; +.max-w-\[calc\(100\%-10rem\)\] { + max-width: calc(100% - 10rem); } -.max-w-7xl { - max-width: 80rem; +.max-w-\[calc\(100\%-4rem\)\] { + max-width: calc(100% - 4rem); } .max-w-\[calc\(100\%-6rem\)\] { @@ -957,23 +927,6 @@ video { max-width: max-content; } -.max-w-\[calc\(100\%-10rem\)\] { - max-width: calc(100% - 10rem); -} - -.max-w-fit { - max-width: -moz-fit-content; - max-width: fit-content; -} - -.max-w-\[calc\(100\%-4rem\)\] { - max-width: calc(100% - 4rem); -} - -.max-w-0\.5 { - max-width: 0.125rem; -} - .flex-1 { flex: 1 1 0%; } @@ -1078,10 +1031,6 @@ video { justify-content: space-around; } -.justify-evenly { - justify-content: space-evenly; -} - .gap-1 { gap: 0.25rem; } @@ -1098,10 +1047,6 @@ video { gap: 2rem; } -.gap-11 { - gap: 2.75rem; -} - .space-y-2 > :not([hidden]) ~ :not([hidden]) { --tw-space-y-reverse: 0; margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse))); @@ -2301,18 +2246,6 @@ main { height: 1rem; } - .sm\:flex-wrap { - flex-wrap: wrap; - } - - .sm\:place-content-center { - place-content: center; - } - - .sm\:items-center { - align-items: center; - } - .sm\:gap-2 { gap: 0.5rem; } @@ -2360,18 +2293,10 @@ main { height: 4rem; } - .lg\:h-\[42vw\] { - height: 42vw; - } - .lg\:h-\[39vw\] { height: 39vw; } - .lg\:max-h-\[806px\] { - max-height: 806px; - } - .lg\:max-h-\[761px\] { max-height: 761px; } @@ -2396,10 +2321,6 @@ main { width: 15rem; } - .lg\:w-1\/3 { - width: 33.333333%; - } - .lg\:flex-row { flex-direction: row; } @@ -2420,18 +2341,6 @@ main { justify-content: flex-end; } - .lg\:justify-between { - justify-content: space-between; - } - - .lg\:justify-around { - justify-content: space-around; - } - - .lg\:justify-evenly { - justify-content: space-evenly; - } - .lg\:bg-white { --tw-bg-opacity: 1; background-color: rgb(255 255 255 / var(--tw-bg-opacity)); diff --git a/templates/watch/watch.html b/templates/watch/watch.html index 910f0e6..8b9a688 100644 --- a/templates/watch/watch.html +++ b/templates/watch/watch.html @@ -106,6 +106,9 @@ <a href="{% url "watch:watch_episode" anime_id current_episode_data.number %}?mode=dub" class="{% if mode == "dub" %}bg-{{ user.preferences.accent_colour }}-600{% else %}bg-white bg-opacity-10{% endif %} text-white text-sm font-bold px-4 py-2 rounded">Dub</a> </div> </div> + <p class="my-4"> + {{ current_episode_metadata.description }} + </p> <div class="flex flex-col lg:flex-row w-full my-4 bg-neutral-950 rounded p-2 gap-4"> <div class="flex flex-col items-center lg:items-start gap-2 min-w-32"> <img src="{{ anime_data.image }}" alt="{{ anime_data.title.english }}" class="rounded-lg w-48 h-72 object-cover"/> @@ -569,7 +572,8 @@ <span>Recommendations</span> </div> <div class="flex flex-col gap-2"> - {% for recommendation in anime_data.recommendations %} + {% for recommendation in anime_data.recommendations|slice:":10" %} + {% if recommendation.id %} <a href="{% url 'watch:watch' recommendation.id %}" class="flex flex-row w-full gap-4 bg-white bg-opacity-10 p-2 rounded hover:bg-{{ user.preferences.accent_colour }}-600 hover:bg-opacity-30"> <img src="{{ recommendation.image }}" alt="{{ recommendation.title.english }}" class="rounded-lg w-12 h-16 object-cover"/> <div class="flex flex-col gap-2 max-w-[calc(100%-4rem)]"> @@ -632,6 +636,7 @@ {% endif %} </div> </a> + {% endif %} {% endfor %} </div> {% endif %} diff --git a/watch/tmdbmapper.py b/watch/tmdbmapper.py new file mode 100644 index 0000000..0f23c2e --- /dev/null +++ b/watch/tmdbmapper.py @@ -0,0 +1,424 @@ +import os +import re +import requests +from dotenv import load_dotenv +from datetime import datetime + +load_dotenv() + +API_KEY = os.getenv("TMDB_API_KEY") +API_READ_ACCESS_TOKEN = os.getenv("TMDB_READ_ACCESS_TOKEN") +BASE_URL = "https://api.themoviedb.org/3" +IMAGE_URL = "https://image.tmdb.org/t/p/original" + +def get_headers(): + return { + "Authorization": f"Bearer {API_READ_ACCESS_TOKEN}", + "accept": "application/json" + } + +def compare_two_strings(str1, str2): + str1 = re.sub(r'\s+', '', str1) + str2 = re.sub(r'\s+', '', str2) + if str1 == str2: + return 1 + if len(str1) < 2 or len(str2) < 2: + return 0 + bigrams = {} + for i in range(len(str1) - 1): + bigram = str1[i:i+2] + bigrams[bigram] = bigrams.get(bigram, 0) + 1 + + intersections = 0 + for i in range(len(str2) - 1): + bigram = str2[i:i+2] + if bigram in bigrams and bigrams[bigram] > 0: + bigrams[bigram] -= 1 + intersections += 1 + + return 2.0 * intersections / (len(str1) + len(str2) - 2) + +def roman_to_decimal(roman): + values = { + 'I': 1, 'V': 5, 'X': 10, 'L': 50, + 'C': 100, 'D': 500, 'M': 1000 + } + total = 0 + prev_value = 0 + for char in reversed(roman.upper()): + current_value = values[char] + if current_value >= prev_value: + total += current_value + else: + total -= current_value + prev_value = current_value + return total + +def parse_title_and_season(title): + media_types = ["TV", "TV_SHORT", "OVA", "ONA", "MOVIE", "SPECIAL", "MUSIC"] + pattern = re.compile(r'\s*(' + '|'.join(media_types) + r')\s*$', re.IGNORECASE) + title = pattern.sub('', title).strip() + + match = re.search(r'(.*?)(Season \d+|Cour \d+|Part \d+|Series \d+|Level \d+|(\bI{1,3}\b|\bIV\b|\bV\b|\bVI\b|\bVII\b|\bVIII\b|\bIX\b|\bX\b)( Part \d+)?|\bI{1,3}\b|\bIV\b|\bV\b|\bVI\b|\bVII\b|\bVIII\b|\bIX\b|\bX\b)$', title, re.IGNORECASE) + + if not match: + return {"show_name": title, "season_info": ""} + + show_name = match.group(1).strip() + season_info = match.group(2).strip() + + roman_match = re.search(r'(\bI{1,3}\b|\bIV\b|\bV\b|\bVI\b|\bVII\b|\bVIII\b|\bIX\b|\bX\b)', season_info, re.IGNORECASE) + if roman_match: + season_info = f"Season {roman_to_decimal(roman_match.group(1))}" + + return {"show_name": show_name, "season_info": season_info} + +def search_tv_shows_by_title(title, alternative_title=None, is_adult=None, country_priority=None, max_year=None): + try: + query = title.split(':')[0].strip() + if ' ' not in query and alternative_title: + query += f" {alternative_title}" + + response = requests.get(f"{BASE_URL}/search/tv", headers=get_headers(), params={"query": query}) + results = response.json().get('results', []) + + if not results: + response = requests.get(f"{BASE_URL}/search/tv", headers=get_headers(), params={"query": title}) + results = response.json().get('results', []) + + if not results: + pattern = re.compile(r'\s*\([A-Z_]+\)\s*$', re.IGNORECASE) + if pattern.search(title): + clean_title = pattern.sub('', title).strip() + response = requests.get(f"{BASE_URL}/search/tv", headers=get_headers(), params={"query": clean_title}) + results = response.json().get('results', []) + + if results: + if is_adult is not None: + results = [r for r in results if r.get('adult', False) == is_adult] + + if country_priority: + results.sort(key=lambda x: int(country_priority in x.get('origin_country', [])), reverse=True) + + if max_year: + try: + max_year = int(max_year) + results = [r for r in results if r.get('first_air_date') and int(r['first_air_date'].split('-')[0]) <= max_year] + except ValueError: + # If max_year is not a valid integer, we'll skip this filter + pass + + return results + except Exception as e: + print(f"Error in search_tv_shows_by_title: {str(e)}") + return [] + +def get_tv_show_details(show_id): + try: + response = requests.get(f"{BASE_URL}/tv/{show_id}", headers=get_headers()) + data = response.json() + return {"show": data, "seasons": data.get("seasons", [])} + except Exception as e: + print(f"Error in get_tv_show_details: {str(e)}") + return None + +def get_image_url(path): + return f"https://image.tmdb.org/t/p/original{path}" if path else None + +def get_tv_season_details(show_id, season_number): + try: + response = requests.get(f"{BASE_URL}/tv/{show_id}/season/{season_number}", headers=get_headers()) + episodes = response.json().get('episodes', []) + + if not episodes: + return [] + + return [ + { + "id": f"{show_id}-S{season_number}E{ep['episode_number']}", + "lastVisited": None, + "episodeId": str(ep['id']), + "title": ep['name'], + "description": ep['overview'] or "No Description.", + "number": ep['episode_number'], + "image": get_image_url(ep.get('still_path')), + "airDate": ep.get('air_date'), + "isFiller": False + } + for ep in episodes + ] + except Exception as e: + print(f"Error in get_tv_season_details: {str(e)}") + return None + +def get_tv_episode_groups(show_id): + try: + response = requests.get(f"{BASE_URL}/tv/{show_id}/episode_groups", headers=get_headers()) + return response.json().get('results', []) + except Exception as e: + print(f"Error in get_tv_episode_groups: {str(e)}") + return [] + +def get_tv_episode_group_details(group_id): + try: + response = requests.get(f"{BASE_URL}/tv/episode_group/{group_id}", headers=get_headers()) + data = response.json() + + if not data or 'groups' not in data: + return [] + + return [ + { + "id": f"{group_id}-E{ep['episode_number']}", + "lastVisited": None, + "episodeId": str(ep['id']), + "title": ep['name'], + "description": ep['overview'], + "number": ep['episode_number'], + "image": get_image_url(ep.get('still_path')), + "airDate": ep.get('air_date'), + "isFiller": False + } + for group in data['groups'] + for ep in group.get('episodes', []) + ] + except Exception as e: + print(f"Error in get_tv_episode_group_details: {str(e)}") + return [] + +def filter_episodes_by_year(episodes, episode_count=None, start_date=None, start_year=None, duration=None, media_type=None): + start_date = datetime.strptime(start_date, "%Y-%m-%d") if start_date else None + start_year = int(start_year) if start_year else None + + filtered_episodes = [ + ep for ep in episodes + if ep['airDate'] and ( + (not start_date or datetime.strptime(ep['airDate'], "%Y-%m-%d") >= start_date) and + (not start_year or int(ep['airDate'].split('-')[0]) >= start_year) + ) + ] + + if media_type in ['OVA', 'ONA', 'SPECIAL']: + excluded_keywords = ['chibi', 'live', 'event', 'recap', 'summary'] + filtered_episodes = [ + ep for ep in filtered_episodes + if not any(keyword in ep['title'].lower() for keyword in excluded_keywords) and + (not ep.get('runtime') or ep['runtime'] >= 20) + ] + + filtered_episodes.sort(key=lambda ep: ( + abs((datetime.strptime(ep['airDate'], "%Y-%m-%d") - start_date).total_seconds()) if start_date else 0, + abs(int(ep['airDate'].split('-')[0]) - start_year) if start_year else 0, + abs((ep.get('runtime') or 0) - (duration or 0)) + )) + + return filtered_episodes[:episode_count] if episode_count else filtered_episodes + +def search_movies_by_title(title, alternative_title=None, year=None): + try: + if alternative_title: + response = requests.get(f"{BASE_URL}/search/movie", headers=get_headers(), params={"query": alternative_title, "year": year}) + results = response.json().get('results', []) + if len(results) == 1 and compare_two_strings((results[0].get('original_title') or '').lower(), alternative_title.lower()) >= 0.9: + return results + + response = requests.get(f"{BASE_URL}/search/movie", headers=get_headers(), params={"query": title, "year": year}) + results = response.json().get('results', []) + + if not results: + pattern = re.compile(r'\s*\([A-Z_]+\)\s*$', re.IGNORECASE) + if pattern.search(title): + clean_title = pattern.sub('', title).strip() + response = requests.get(f"{BASE_URL}/search/movie", headers=get_headers(), params={"query": clean_title, "year": year}) + results = response.json().get('results', []) + + return results + except Exception as e: + print(f"Error in search_movies_by_title: {str(e)}") + return None + +def get_movie_details(movie_id): + try: + response = requests.get(f"{BASE_URL}/movie/{movie_id}", headers=get_headers()) + movie = response.json() + + return { + "id": str(movie['id']), + "lastVisited": None, + "episodeId": str(movie['id']), + "title": movie['title'], + "description": movie['overview'], + "number": 1, + "image": get_image_url(movie.get('poster_path')), + "airDate": movie.get('release_date'), + "isFiller": False + } + except Exception as e: + print(f"Error in get_movie_details: {str(e)}") + return None + +def fetch_and_filter_episodes(show_id, season, start_date, year, episode_count, media_type): + episodes = get_tv_season_details(show_id, season['season_number']) + return filter_episodes_by_year(episodes, episode_count or season['episode_count'], start_date, year, None, media_type) if episodes else [] + +def find_best_matching_season(seasons, season_info, episode_count, year, date): + matching_seasons = [ + s for s in seasons + if (season_info and f"Season {s['season_number']}".lower() == season_info.lower()) or + (s['episode_count'] == episode_count and + (not s.get('air_date') or + (s.get('air_date') and str(s['air_date']).startswith(str(year or ''))))) + ] + + if len(matching_seasons) > 1 and date: + matching_seasons = [ + s for s in matching_seasons + if s.get('air_date') and datetime.strptime(s['air_date'], "%Y-%m-%d").month == date.month + ] + + if len(matching_seasons) > 1 and date: + matching_seasons = [ + s for s in matching_seasons + if s.get('air_date') and datetime.strptime(s['air_date'], "%Y-%m-%d").day == date.day + ] + + return matching_seasons[0] if matching_seasons else None + +def search_and_fetch_all_episodes(title, media_type, alternative_title, year, episode_count, date, is_adult, country_priority, duration): + parsed_title = parse_title_and_season(title) + show_name, season_info = parsed_title['show_name'], parsed_title['season_info'] + + if media_type == "MOVIE": + movies = search_movies_by_title(title, alternative_title, year) + if movies and len(movies) > 0: + movie = max(movies, key=lambda m: m['popularity']) + movie_details = get_movie_details(movie['id']) + return [movie_details] if movie_details else [] + return [] + + start_date = f"{year}-{date.month:02d}-{date.day:02d}" if date else None + tv_shows = search_tv_shows_by_title(show_name, alternative_title, is_adult, country_priority, year) + + if not tv_shows: + print(f"No TV shows found for title: {show_name}") + return [] + + best_match = next( + (show for show in tv_shows if ( + show['name'].lower() == show_name.lower() or + show.get('original_name', '').lower() == show_name.lower() + ) and (not year or show['first_air_date'].startswith(str(year)))), + tv_shows[0] + ) + + show_details = get_tv_show_details(best_match['id']) + if not show_details: + return [] + + seasons = show_details['seasons'] + if media_type == "TV": + seasons = [s for s in seasons if s['season_number'] != 0] + + best_season = find_best_matching_season(seasons, season_info, episode_count, year, date) + + if best_season: + return fetch_and_filter_episodes(best_match['id'], best_season, start_date, year, episode_count, media_type) + else: + print(f"No matching season found for {show_name}") + # You might want to implement a fallback strategy here, such as: + # 1. Try to fetch episodes from the latest season + # 2. Generate placeholder episodes + # 3. Or simply return an empty list + # return [] <- removed this early return to avoid breaking the code + + if media_type in ["OVA", "ONA", "SPECIAL"]: + episode_groups = get_tv_episode_groups(best_match['id']) + if episode_groups and len(episode_groups) > 0: + absolute_order_group = next((g for g in episode_groups if "absolute order" in g['name'].lower()), None) + if absolute_order_group: + episodes = get_tv_episode_group_details(absolute_order_group['id']) + filtered_episodes = filter_episodes_by_year(episodes, episode_count, start_date, year, duration, media_type) + if filtered_episodes: + return filtered_episodes + + special_season = next((s for s in seasons if s['season_number'] == 0), None) + if special_season: + episodes = fetch_and_filter_episodes(best_match['id'], special_season, start_date, year, episode_count, media_type) + if episodes: + return episodes + + if season_info: + matching_season = next((s for s in seasons if f"Season {s['season_number']}".lower() == season_info.lower()), None) + if matching_season: + return fetch_and_filter_episodes(best_match['id'], matching_season, start_date, year, episode_count, media_type) + + episode_groups = get_tv_episode_groups(best_match['id']) + if episode_groups and len(episode_groups) > 0: + all_episodes_group = next( + (g for g in episode_groups if any(keyword in g['name'].lower() for keyword in + ['all episodes', 'absolute order', 'absolute', 'correct order', 'bluray order'])), + None + ) + if all_episodes_group: + episodes = get_tv_episode_group_details(all_episodes_group['id']) + return filter_episodes_by_year(episodes, episode_count, start_date, year, duration, media_type) + + best_match = max(tv_shows, key=lambda show: compare_two_strings(show_name.lower(), (show['name'] or '').lower())) + show_details = get_tv_show_details(best_match['id']) + if show_details: + seasons = show_details['seasons'] + if media_type == "TV": + seasons = [s for s in seasons if s['season_number'] != 0] + + best_season = next( + (s for s in seasons if s['episode_count'] == episode_count and + (s['air_date'] and s['air_date'].startswith(str(year)))), + None + ) + + if not best_season: + possible_seasons = [s for s in seasons if s['episode_count'] > episode_count and + (s['air_date'] and int(s['air_date'].split('-')[0]) <= int(year))] + if possible_seasons: + best_season = min( + possible_seasons, + key=lambda s: ( + abs(int(s['air_date'].split('-')[0]) - int(year)), + abs(s['episode_count'] - episode_count) + ) + ) + + if best_season: + return fetch_and_filter_episodes(best_match['id'], best_season, start_date, year, episode_count, media_type) + + return [] + +# Usage example +def get_anime_episodes(anime_data): + title = anime_data['title']['english'] or anime_data['title']['romaji'] + alternative_title = anime_data['title']['native'] + media_type = anime_data['type'] + year = anime_data['startDate']['year'] + episode_count = anime_data['totalEpisodes'] + date = datetime( + year=anime_data['startDate']['year'], + month=anime_data['startDate']['month'], + day=anime_data['startDate']['day'] + ) + is_adult = anime_data.get('isAdult', False) + country_priority = anime_data['countryOfOrigin'] + duration = anime_data['duration'] + + episodes = search_and_fetch_all_episodes( + title=title, + media_type=media_type, + alternative_title=alternative_title, + year=year, + episode_count=episode_count, + date=date, + is_adult=is_adult, + country_priority=country_priority, + duration=duration + ) + + return episodes diff --git a/watch/views.py b/watch/views.py index 9eadb30..9ed390d 100644 --- a/watch/views.py +++ b/watch/views.py @@ -6,6 +6,7 @@ from authentication.utils import get_single_anime_mal from watch.utils import update_anime_user_history, get_anime_user_history, get_from_redis_cache, store_in_redis_cache import requests import json +from watch.tmdbmapper import get_anime_episodes dotenv.load_dotenv() @@ -27,13 +28,29 @@ def watch(request, anime_id, episode=None): anime_selected_cached = get_from_redis_cache(f"anime_{anime_id}_anime_selected") anime_episodes_cached = get_from_redis_cache(f"anime_{anime_id}_anime_episodes") + if anime_data_cached: + try: + anime_data = json.loads(anime_data_cached) + except: + anime_data = None + + if anime_selected_cached: + try: + anime_selected = json.loads(anime_selected_cached) + except: + anime_selected = None + + if anime_episodes_cached: + try: + anime_episodes = json.loads(anime_episodes_cached) + except: + anime_episodes = None + if not anime_data_cached: base_url = f"{os.getenv("CONSUMET_URL")}/meta/anilist/info/{anime_id}?provider=zoro" response = requests.get(base_url) anime_data = response.json() store_in_redis_cache(f"anime_{anime_id}_anime_data", json.dumps(anime_data)) - else: - anime_data = json.loads(anime_data_cached) if not anime_selected_cached: z_anime_id = anime_data["episodes"][0]["id"].split("$")[0] @@ -41,16 +58,12 @@ def watch(request, anime_id, episode=None): response = requests.get(base_url) anime_selected = response.json() store_in_redis_cache(f"anime_{anime_id}_anime_selected", json.dumps(anime_selected)) - else: - anime_selected = json.loads(anime_selected_cached) if not anime_episodes_cached: base_url = f"{os.getenv("ZORO_URL")}/anime/episodes/{anime_selected["anime"]["info"]["id"]}" response = requests.get(base_url) anime_episodes = response.json() store_in_redis_cache(f"anime_{anime_id}_anime_episodes", json.dumps(anime_episodes)) - else: - anime_episodes = json.loads(anime_episodes_cached) if not anime_selected["anime"]["info"]["stats"]["episodes"][mode] or anime_selected["anime"]["info"]["stats"]["episodes"][mode] < episode: mode = "sub" @@ -83,6 +96,18 @@ def watch(request, anime_id, episode=None): current_episode_data = [e for e in anime_episodes["episodes"] if e["number"] == episode][0] + episode_metadata = get_from_redis_cache(f"anime_{anime_id}_episode_metadata") + try: + episode_metadata = json.loads(episode_metadata) + except: + episode_metadata = None + + if not episode_metadata: + episode_metadata = get_anime_episodes(anime_data) + store_in_redis_cache(f"anime_{anime_id}_episode_metadata", json.dumps(episode_metadata)) + + current_episode_metadata = episode_metadata[episode - 1] if episode_metadata else None + context = { "anime_data": anime_data, "anime_selected": anime_selected, @@ -96,6 +121,7 @@ def watch(request, anime_id, episode=None): "anime_history": anime_history, "watched_episodes": watched_episodes, "current_watched_time": current_watched_time, + "current_episode_metadata": current_episode_metadata, "mode": mode, } |
