diff options
| author | Bobby <[email protected]> | 2024-09-03 12:20:35 -0400 |
|---|---|---|
| committer | Bobby <[email protected]> | 2024-09-03 12:20:35 -0400 |
| commit | 51a27b6f400625cb0137e1394de1056ea5fb682b (patch) | |
| tree | 593b782d99784c690705eb397831a9a681d406a3 /homepage/views.py | |
| parent | a9e71a492486a25ec4273716080e0ca744a36646 (diff) | |
| download | yugen-51a27b6f400625cb0137e1394de1056ea5fb682b.tar.xz yugen-51a27b6f400625cb0137e1394de1056ea5fb682b.zip | |
optimizations
Diffstat (limited to 'homepage/views.py')
| -rw-r--r-- | homepage/views.py | 208 |
1 files changed, 119 insertions, 89 deletions
diff --git a/homepage/views.py b/homepage/views.py index 9e0c6cc..15d8811 100644 --- a/homepage/views.py +++ b/homepage/views.py @@ -13,109 +13,139 @@ from homepage.utils import ( get_upcoming_anime, get_next_season, ) +from concurrent.futures import ThreadPoolExecutor, as_completed +from functools import lru_cache -def index(request): - homepage_data_cached = get_from_redis_cache("homepage_data") - - if not homepage_data_cached: - trending_anime = get_trending_anime() - popular_anime = get_popular_anime() - top_anime = get_top_anime() - top_airing_anime = get_top_airing_anime() - upcoming_anime = get_upcoming_anime() - next_season = get_next_season() - +@lru_cache(maxsize=1) +def get_homepage_data(): + homepage_data = get_from_redis_cache("homepage_data") + + if not homepage_data: homepage_data = { - "trending_anime": trending_anime, - "popular_anime": popular_anime, - "top_anime": top_anime, - "top_airing_anime": top_airing_anime, - "upcoming_anime": upcoming_anime, - "next_season": next_season + "trending_anime": get_trending_anime(), + "popular_anime": get_popular_anime(), + "top_anime": get_top_anime(), + "top_airing_anime": get_top_airing_anime(), + "upcoming_anime": get_upcoming_anime(), + "next_season": get_next_season() } - - store_in_redis_cache("homepage_data", json.dumps(homepage_data)) + store_in_redis_cache("homepage_data", json.dumps(homepage_data), 3600) # Cache for 1 hour else: - homepage_data = json.loads(homepage_data_cached) - trending_anime = homepage_data["trending_anime"] - popular_anime = homepage_data["popular_anime"] - top_anime = homepage_data["top_anime"] - top_airing_anime = homepage_data["top_airing_anime"] - upcoming_anime = homepage_data["upcoming_anime"] - next_season = homepage_data["next_season"] - - if request.user.preferences.show_history_on_home: - user_history_data = gather_watch_history(request, limit=10) + homepage_data = json.loads(homepage_data) + + return homepage_data +def index(request): + homepage_data = get_homepage_data() + context = { - "trending_anime": trending_anime["results"], - "popular_anime": popular_anime["results"], - "top_anime": top_anime["results"], - "top_airing_anime": top_airing_anime["results"], - "upcoming_anime": upcoming_anime["results"], - "next_season": next_season, - "user_history_data": user_history_data if request.user.preferences.show_history_on_home else None + "trending_anime": homepage_data["trending_anime"]["results"], + "popular_anime": homepage_data["popular_anime"]["results"], + "top_anime": homepage_data["top_anime"]["results"], + "top_airing_anime": homepage_data["top_airing_anime"]["results"], + "upcoming_anime": homepage_data["upcoming_anime"]["results"], + "next_season": homepage_data["next_season"], } + if request.user.preferences.show_history_on_home: + context["user_history_data"] = gather_watch_history(request) + return render(request, "home/index.html", context) -def gather_watch_history(request, limit=None): +def gather_watch_history(request, limit=10): user = request.user + latest_history = get_user_watch_history(user.id, limit) + anime_ids = [entry['anime_id'] for entry in latest_history] + anime_data_map = get_bulk_anime_data(anime_ids) + + return [ + { + "anime_id": entry['anime_id'], + "title": anime_data_map[entry['anime_id']]["title"], + "cover": anime_data_map[entry['anime_id']]["cover"], + "episode": entry['episode'] if entry['last_watched'] else 1, + "metadata": get_episode_metadata(anime_data_map[entry['anime_id']], entry['episode'] if entry['last_watched'] else 1) + } + for entry in latest_history + ] + +@lru_cache(maxsize=100) +def get_user_watch_history(user_id, limit): + query = """ + WITH ranked_history AS ( + SELECT + id, + anime_id, + episode, + last_watched, + last_updated, + ROW_NUMBER() OVER (PARTITION BY anime_id ORDER BY last_updated DESC) AS rn + FROM user_profile_userhistory + WHERE user_id = %s + ) + SELECT id, anime_id, episode, last_watched, last_updated + FROM ranked_history + WHERE rn = 1 + ORDER BY last_updated DESC + LIMIT %s + """ with connection.cursor() as cursor: - query = """ - WITH ranked_history AS ( - SELECT - id, - anime_id, - episode, - last_watched, - last_updated, - ROW_NUMBER() OVER (PARTITION BY anime_id ORDER BY last_updated DESC) AS rn - FROM user_profile_userhistory - WHERE user_id = %s - ) - SELECT id, anime_id, episode, last_watched, last_updated - FROM ranked_history - WHERE rn = 1 - ORDER BY last_updated DESC - """ - if limit: - query += " LIMIT %s" - cursor.execute(query, [user.id, limit]) - else: - cursor.execute(query, [user.id]) - + cursor.execute(query, [user_id, limit]) columns = [col[0] for col in cursor.description] - latest_history = [dict(zip(columns, row)) for row in cursor.fetchall()] - - user_history_data = [] - - for history_entry in latest_history: - anime_id = history_entry['anime_id'] - last_watched = history_entry['episode'] if history_entry['last_watched'] else 1 - - anime_data = None - try: - anime_data = json.loads(get_from_redis_cache(f"anime_{anime_id}_anime_data")) - except: - 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)) - - # attach metadata to episodes - episode_metadata = get_episode_metadata(anime_data, last_watched) - - user_history_data.append({ - "anime_id": anime_id, - "title": anime_data["title"], - "cover": anime_data["cover"], - "episode": last_watched, - "metadata": episode_metadata - }) - - return user_history_data + return [dict(zip(columns, row)) for row in cursor.fetchall()] + +def get_bulk_anime_data(anime_ids): + anime_data_map = {} + missing_ids = [] + + # Check cache first (including potential caches from other functions) + for anime_id in anime_ids: + cached_data = get_cached_anime_data(anime_id) + if cached_data: + anime_data_map[anime_id] = cached_data + else: + missing_ids.append(anime_id) + + # Fetch missing data in parallel + if missing_ids: + with ThreadPoolExecutor(max_workers=min(10, len(missing_ids))) as executor: + future_to_id = {executor.submit(fetch_anime_data, anime_id): anime_id for anime_id in missing_ids} + for future in as_completed(future_to_id): + anime_id = future_to_id[future] + try: + data = future.result() + anime_data_map[anime_id] = data + store_in_redis_cache(f"anime_{anime_id}_anime_data", json.dumps(data), 86400) # Cache for 24 hours + except Exception as exc: + print(f"Anime ID {anime_id} generated an exception: {exc}") + + return anime_data_map + +def get_cached_anime_data(anime_id): + cache_keys = [ + f"anime_{anime_id}_anime_data", + f"anime_{anime_id}_details", # Example of a cache key that might be used by another function + f"anime_info_{anime_id}" # Another example + ] + + for key in cache_keys: + cached_data = get_from_redis_cache(key) + if cached_data: + return json.loads(cached_data) + + return None + +@lru_cache(maxsize=1000) +def fetch_anime_data(anime_id): + base_url = f"{os.getenv('CONSUMET_URL')}/meta/anilist/info/{anime_id}?provider=zoro" + try: + response = requests.get(base_url, timeout=10) + response.raise_for_status() + return response.json() + except requests.RequestException as e: + print(f"Error fetching data for anime ID {anime_id}: {e}") + return None def search_json(request): |
