diff options
Diffstat (limited to 'watch/views.py')
| -rw-r--r-- | watch/views.py | 269 |
1 files changed, 167 insertions, 102 deletions
diff --git a/watch/views.py b/watch/views.py index e5ccb06..32452a1 100644 --- a/watch/views.py +++ b/watch/views.py @@ -6,141 +6,206 @@ from authentication.utils import get_single_anime_mal from watch.utils import get_episode_metadata, update_anime_user_history, get_anime_user_history, get_from_redis_cache, store_in_redis_cache import requests import json - +from functools import lru_cache dotenv.load_dotenv() def watch(request, anime_id, episode=None): - forward_detail = request.GET.get("forward") == "detail" - if not episode and request.user.preferences.default_watch_page == "detail" and not forward_detail: + user = request.user + preferences = user.preferences + + if not episode and preferences.default_watch_page == "detail" and request.GET.get("forward") != "detail": return redirect("detail:detail", anime_id=anime_id) - anime_history = get_anime_user_history(request.user, anime_id) - + anime_history = get_anime_user_history(user, anime_id) watched_episodes = [h.episode for h in anime_history] - current_watched_time = [h.time_watched for h in anime_history if h.episode == episode] - current_watched_time = current_watched_time[0] if current_watched_time else 0 - + if not episode or episode < 1: - episode = [h.episode for h in anime_history if h.last_watched] - episode = episode[0] if episode else 1 + episode = next((h.episode for h in anime_history if h.last_watched), 1) return redirect("watch:watch_episode", anime_id=anime_id, episode=episode) - mode = request.GET.get("mode", request.user.preferences.default_language) - - anime_data_cached = get_from_redis_cache(f"anime_{anime_id}_anime_data") - 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") + mode = request.GET.get("mode", preferences.default_language) + + anime_data, anime_selected, anime_episodes = get_anime_combined_data(anime_id) - anime_data = None - anime_selected = None - anime_episodes = None - episode_data = None - current_episode_data = None - current_episode_name = None - current_episode_metadata = None + if not anime_data or anime_data.get("status") == "Not yet aired" or not anime_selected or not anime_episodes: + return redirect("detail:detail", anime_id=anime_id) - if anime_data_cached: - try: - anime_data = json.loads(anime_data_cached) - except: - anime_data = None + mode = process_mode(mode, anime_selected, episode) + episode_data, current_episode_data = process_episode_data(anime_episodes, episode, mode, preferences) + + if not episode_data: + return redirect("watch:watch_episode", anime_id=anime_id, episode=anime_episodes["totalEpisodes"]) - if anime_selected_cached: - try: - anime_selected = json.loads(anime_selected_cached) - except: - anime_selected = None + current_watched_time = next((h.time_watched for h in anime_history if h.episode == episode), 0) + update_anime_user_history(user, anime_id, episode, current_watched_time) - if anime_episodes_cached: - try: - anime_episodes = json.loads(anime_episodes_cached) - except: - anime_episodes = None + context = build_context(anime_data, anime_selected, anime_episodes, episode_data, current_episode_data, + episode, anime_id, anime_history, watched_episodes, mode) - 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)) + if user.mal_access_token and "malId" in anime_data: + context["mal_data"] = get_mal_data(user.mal_access_token, anime_data["malId"]) - if anime_data["status"] == "Not yet aired": - return redirect("detail:detail", anime_id=anime_id) + return render(request, "watch/watch.html", context) - if not anime_selected_cached: - z_anime_id = anime_data["episodes"][0]["id"].split("$")[0] if len(anime_data["episodes"]) > 0 else None - if z_anime_id is not None: - base_url = f"{os.getenv("ZORO_URL")}/anime/info?id={z_anime_id}" - response = requests.get(base_url) - anime_selected = response.json() - store_in_redis_cache(f"anime_{anime_id}_anime_selected", json.dumps(anime_selected)) - - if not anime_episodes_cached and anime_selected is not None: - 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)) - - if anime_selected is not None: - if not anime_selected["anime"]["info"]["stats"]["episodes"][mode] or anime_selected["anime"]["info"]["stats"]["episodes"][mode] < episode: - mode = "sub" - - if anime_episodes is not None: - if episode > anime_episodes["totalEpisodes"]: - return redirect("watch:watch_episode", anime_id=anime_id, episode=anime_episodes["totalEpisodes"]) - - episode_d = [e for e in anime_episodes["episodes"] if e["number"] == episode][0] - - base_url = f"{os.getenv("ZORO_URL")}/anime/episode-srcs?id={episode_d["episodeId"]}?server&category={mode}" - response = requests.get(base_url) - episode_data = response.json() - - if "message" in episode_data and episode_data["message"] == "Couldn't find server. Try another server": - base_url = f"{os.getenv("ZORO_URL")}/anime/episode-srcs?id={episode_d["episodeId"]}?server=hd-2&category={mode}" - response = requests.get(base_url) - episode_data = response.json() - - # if no captions are present and the mode is dub, and ingrain_sub_subtitles_in_dub is true, then fetch the sub track - if "tracks" in episode_data and not any(t["kind"] == "captions" for t in episode_data["tracks"]) and mode == "dub" and request.user.preferences.ingrain_sub_subtitles_in_dub: - base_url = f"{os.getenv("ZORO_URL")}/anime/episode-srcs?id={episode_d["episodeId"]}?server&category=sub" - response = requests.get(base_url).json() - captions = [t for t in response["tracks"] if t["kind"] == "captions"] - if captions: - episode_data["tracks"].extend(captions) - - current_episode_name = [e["title"] for e in anime_episodes["episodes"] if e["number"] == episode][0] +@lru_cache(maxsize=100) +def get_anime_combined_data(anime_id): + cache_keys = [ + f"anime_{anime_id}_combined_data", + f"anime_{anime_id}_data", + f"anime_{anime_id}_selected", + f"anime_{anime_id}_episodes" + ] - update_anime_user_history(request.user, anime_id, episode, current_watched_time) - - current_episode_data = [e for e in anime_episodes["episodes"] if e["number"] == episode][0] + cached_data = get_multiple_from_cache(cache_keys) + + if cached_data.get(cache_keys[0]): + return json.loads(cached_data[cache_keys[0]]) + + anime_data = cached_data.get(cache_keys[1]) or fetch_anime_data(anime_id) + if not anime_data: + return None, None, None + + anime_selected = cached_data.get(cache_keys[2]) or fetch_anime_selected(anime_data) + anime_episodes = cached_data.get(cache_keys[3]) or fetch_anime_episodes(anime_selected) if anime_selected else None + + combined_data = [anime_data, anime_selected, anime_episodes] + store_in_redis_cache(cache_keys[0], json.dumps(combined_data), 3600) # Cache for 1 hour + + return combined_data + +def get_multiple_from_cache(keys): + return {key: json.loads(value) if value else None + for key, value in zip(keys, map(get_from_redis_cache, keys))} + +def fetch_anime_data(anime_id): + url = f"{os.getenv('CONSUMET_URL')}/meta/anilist/info/{anime_id}?provider=zoro" + try: + response = requests.get(url, timeout=10) + response.raise_for_status() + data = response.json() + store_in_redis_cache(f"anime_{anime_id}_data", json.dumps(data), 86400) # Cache for 24 hours + return data + except requests.RequestException as e: + print(f"Error fetching anime data for ID {anime_id}: {e}") + return None + +def fetch_anime_selected(anime_data): + if anime_data and anime_data.get("episodes"): + z_anime_id = anime_data["episodes"][0]["id"].split("$")[0] + url = f"{os.getenv('ZORO_URL')}/anime/info?id={z_anime_id}" + try: + response = requests.get(url, timeout=10) + response.raise_for_status() + data = response.json() + store_in_redis_cache(f"anime_{anime_data['id']}_selected", json.dumps(data), 86400) # Cache for 24 hours + return data + except requests.RequestException as e: + print(f"Error fetching anime selected data: {e}") + return None + +def fetch_anime_episodes(anime_selected): + if anime_selected and anime_selected.get("anime", {}).get("info", {}).get("id"): + url = f"{os.getenv('ZORO_URL')}/anime/episodes/{anime_selected['anime']['info']['id']}" + try: + response = requests.get(url, timeout=10) + response.raise_for_status() + data = response.json() + store_in_redis_cache(f"anime_{anime_selected['anime']['info']['id']}_episodes", json.dumps(data), 86400) # Cache for 24 hours + return data + except requests.RequestException as e: + print(f"Error fetching anime episodes: {e}") + return None + +def process_mode(mode, anime_selected, episode): + if not anime_selected.get("anime", {}).get("info", {}).get("stats", {}).get("episodes", {}).get(mode) or \ + anime_selected["anime"]["info"]["stats"]["episodes"].get(mode, 0) < episode: + return "sub" + return mode + +def process_episode_data(anime_episodes, episode, mode, preferences): + if not anime_episodes or episode > anime_episodes.get("totalEpisodes", 0): + return None, None + + episode_d = next((e for e in anime_episodes.get("episodes", []) if e["number"] == episode), None) + if not episode_d: + return None, None + + cache_key = f"episode_data_{episode_d['episodeId']}_{mode}" + episode_data = get_from_redis_cache(cache_key) + + if not episode_data: + episode_data = fetch_episode_data(episode_d["episodeId"], mode) + if episode_data: + store_in_redis_cache(cache_key, json.dumps(episode_data), 3600) # Cache for 1 hour + else: + episode_data = json.loads(episode_data) + + if preferences.ingrain_sub_subtitles_in_dub and mode == "dub" and \ + not any(t.get("kind") == "captions" for t in episode_data.get("tracks", [])): + sub_data = fetch_episode_data(episode_d["episodeId"], "sub") + if sub_data: + episode_data.setdefault("tracks", []).extend([t for t in sub_data.get("tracks", []) if t.get("kind") == "captions"]) + + current_episode_data = { + "title": episode_d.get("title", ""), + "number": episode_d.get("number", 0), + } - current_episode_metadata = get_episode_metadata(anime_data, episode) + return episode_data, current_episode_data - context = { +def fetch_episode_data(episode_id, mode): + url = f"{os.getenv('ZORO_URL')}/anime/episode-srcs?id={episode_id}?server&category={mode}" + try: + response = requests.get(url, timeout=10) + response.raise_for_status() + data = response.json() + + if data.get("message") == "Couldn't find server. Try another server": + url = f"{os.getenv('ZORO_URL')}/anime/episode-srcs?id={episode_id}?server=hd-2&category={mode}" + response = requests.get(url, timeout=10) + response.raise_for_status() + data = response.json() + + return data + except requests.RequestException as e: + print(f"Error fetching episode data for ID {episode_id}: {e}") + return None + +def build_context(anime_data, anime_selected, anime_episodes, episode_data, current_episode_data, + episode, anime_id, anime_history, watched_episodes, mode): + return { "anime_data": anime_data, "anime_selected": anime_selected, "anime_episodes": anime_episodes, "episode_data": episode_data, "current_episode": episode, "current_episode_data": current_episode_data, - "stream_url": episode_data["sources"][0]["url"] if episode_data and "sources" in episode_data else None, + "stream_url": episode_data.get("sources", [{}])[0].get("url") if episode_data else None, "anime_id": anime_id, - "current_episode_name": current_episode_name, + "current_episode_name": current_episode_data.get("title") if current_episode_data else None, "anime_history": anime_history, "watched_episodes": watched_episodes, - "current_watched_time": current_watched_time, - "current_episode_metadata": current_episode_metadata, + "current_watched_time": next((h.time_watched for h in anime_history if h.episode == episode), 0), + "current_episode_metadata": get_episode_metadata(anime_data, episode), "mode": mode, } - if request.user.mal_access_token and anime_data and "malId" in anime_data: - mal_data = get_single_anime_mal(request.user.mal_access_token, anime_data["malId"]) +@lru_cache(maxsize=100) +def get_mal_data(mal_access_token, mal_id): + cache_key = f"mal_data_{mal_id}" + mal_data = get_from_redis_cache(cache_key) + + if not mal_data: + mal_data = get_single_anime_mal(mal_access_token, mal_id) if mal_data: - mal_data["average_episode_duration"] = mal_data["average_episode_duration"] // 60 + 1 - context["mal_data"] = mal_data - context["mal_episode_range"] = range(1, mal_data["num_episodes"] + 1) - - return render(request, "watch/watch.html", context) + mal_data["average_episode_duration"] = mal_data.get("average_episode_duration", 0) // 60 + 1 + mal_data["episode_range"] = list(range(1, mal_data.get("num_episodes", 1) + 1)) + store_in_redis_cache(cache_key, json.dumps(mal_data), 86400) # Cache for 24 hours + else: + mal_data = json.loads(mal_data) + + return mal_data def update_episode_watch_time(request): if request.method != "POST": |
