diff options
| -rw-r--r-- | .gitmodules | 3 | ||||
| -rw-r--r-- | apps/anime/views.py | 153 | ||||
| -rw-r--r-- | middleware/i18nmiddleware.py | 8 | ||||
| -rw-r--r-- | services/stream/views.py | 48 | ||||
| m--------- | static/js/MathJax | 0 | ||||
| -rw-r--r-- | thatcomputerscientist/settings.py | 9 |
6 files changed, 120 insertions, 101 deletions
diff --git a/.gitmodules b/.gitmodules index 09a0cde3..e69de29b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "static/js/MathJax"] - path = static/js/MathJax - url = https://github.com/mathjax/MathJax.git diff --git a/apps/anime/views.py b/apps/anime/views.py index 6b976391..c8a15b92 100644 --- a/apps/anime/views.py +++ b/apps/anime/views.py @@ -2,7 +2,9 @@ import os from django.urls import reverse import requests from django.shortcuts import redirect, render - +from django.views.decorators.cache import cache_page +from functools import wraps +from django.core.cache import cache from thatcomputerscientist.utils import i18npatterns genres = [ @@ -37,30 +39,29 @@ sortings = [ ] ANIME_PROVIDER_MAP = {} +CONSUMET_BASE_URL = os.getenv("CONSUMET_URL") def get_anime(anime_id, dub=False): - if anime_id in ANIME_PROVIDER_MAP: - provider = ANIME_PROVIDER_MAP[anime_id] - else: - provider = "zoro" - - dub = "true" if dub else "false" - base_url = f"{os.getenv('CONSUMET_URL')}/meta/anilist/info/{anime_id}" - params = {"provider": provider, "dub": dub} + provider = ANIME_PROVIDER_MAP.get(anime_id, "zoro") + params = {"dub": "true"} if dub else {} + params.update({"provider": provider} if provider == "zoro" else {}) - response = requests.get(base_url, params=params) + response = requests.get( + f"{CONSUMET_BASE_URL}/meta/anilist/info/{anime_id}", params=params + ) data = response.json() + if ( - (not data.get("episodes") or len(data.get("episodes")) == 0) + not data.get("episodes") and provider == "zoro" and data.get("status") != "Not yet aired" ): ANIME_PROVIDER_MAP[anime_id] = "gogoanime" - return get_anime(anime_id) - else: - ANIME_PROVIDER_MAP[anime_id] = provider - return data + return get_anime(anime_id, dub) + + ANIME_PROVIDER_MAP[anime_id] = provider + return data def sort_mapper(sort_by, order): @@ -77,14 +78,7 @@ def sort_mapper(sort_by, order): if sort_by not in sort_mappings or order not in ["asc", "desc"]: return None - sort_value = sort_mappings[sort_by] - order_suffix = "" if order == "asc" else "_DESC" - - return f"{sort_value}{order_suffix}" - - -def bracketed_string(string): - return f'["{string}"]' + return f"{sort_mappings[sort_by]}{'_DESC' if order == 'desc' else ''}" def anime_results( @@ -97,37 +91,46 @@ def anime_results( "finished", "hiatus", ] + params = { + "page": page, + "perPage": per_page, + "type": "ANIME", + **({"query": query} if query else {}), + **({"sort": f'["{sort_mapper(sort, order)}"]'} if sort and order else {}), + **({"genres": f'["{genre}"]'} if genre else {}), + **({"status": status.upper()} if status in supported_status else {}), + } - if status and status not in supported_status: - status = "" + response = requests.get( + f"{CONSUMET_BASE_URL}/meta/anilist/advanced-search", params=params + ) + return response.json() - base_url = f"{os.getenv('CONSUMET_URL')}/meta/anilist/advanced-search" - params = {"page": page, "perPage": per_page, "type": "ANIME"} - if query: - params["query"] = query +def cache_anime_page(timeout=60 * 15): + def decorator(view_func): + @wraps(view_func) + def _wrapped_view(request, anime_id, e=None, *args, **kwargs): + cache_key = f"anime_page:{anime_id}:ep{e}:dub{request.COOKIES.get('anime_dub', False)}" + cached_response = cache.get(cache_key) - if sort and order: - sort_value = sort_mapper(sort, order) - if sort_value: - params["sort"] = bracketed_string(sort_value) + if cached_response: + return cached_response - if genre: - params["genres"] = bracketed_string(genre) + response = view_func(request, anime_id, e, *args, **kwargs) + if response.status_code == 200: + cache.set(cache_key, response, timeout) + return response - if status: - params["status"] = status.upper() + return _wrapped_view - response = requests.get(base_url, params=params) - return response.json() + return decorator +@cache_page(60 * 15) def home(request): - META = { - "title": "Anime: Home", - } LANGUAGE_CODE = i18npatterns(request.LANGUAGE_CODE) - request.meta.update(META) + request.meta.update({"title": "Anime: Home"}) context = { "genres": genres, @@ -139,72 +142,58 @@ def home(request): sort="popularity", status="releasing", per_page=8 ), } - return render(request, f"{LANGUAGE_CODE}/anime/home.html", context) def search(request): - # Get search parameters query = request.GET.get("q", "") - genre = request.GET.get("genre", "") - sort = request.GET.get("sort", "popularity") - order = request.GET.get("order", "desc") - page = int(request.GET.get("page", 1)) - - META = { - "title": f"Anime: Search", - } - if query: - META["title"] = f"Anime: Search Results for {query}" LANGUAGE_CODE = i18npatterns(request.LANGUAGE_CODE) - request.meta.update(META) + request.meta.update( + {"title": f"Anime: Search Results for {query}" if query else "Anime: Search"} + ) - # Get search results search_results = anime_results( - query=query, genre=genre, sort=sort, order=order, page=page, per_page=32 + query=query, + genre=request.GET.get("genre", ""), + sort=request.GET.get("sort", "popularity"), + order=request.GET.get("order", "desc"), + page=int(request.GET.get("page", 1)), + per_page=32, ) context = { "genres": genres, "sortings": sortings, "search_results": search_results, - "current_page": page, + "current_page": int(request.GET.get("page", 1)), "total_pages": search_results.get("totalPages", 1), "total_results": search_results.get("totalResults", 0), } - return render(request, f"{LANGUAGE_CODE}/anime/search.html", context) +@cache_anime_page(timeout=60 * 15) def anime(request, anime_id, e=None): - dub = request.COOKIES.get("anime_dub", False) - anime_data = get_anime(anime_id, dub) - - if len(anime_data.get("episodes")) > 0: - episode_numbers = [ - int(episode.get("number")) for episode in anime_data.get("episodes") - ] + anime_data = get_anime(anime_id, request.COOKIES.get("anime_dub", False)) + episode_numbers = [ + int(episode.get("number")) for episode in anime_data.get("episodes", []) + ] + if episode_numbers: if not e: - e = episode_numbers[0] return redirect( - reverse("anime:anime", kwargs={"anime_id": anime_id, "e": e}) + reverse( + "anime:anime", + kwargs={"anime_id": anime_id, "e": episode_numbers[0]}, + ) ) - - e = int(e) - if e not in episode_numbers: + if int(e) not in episode_numbers: return redirect( reverse("anime:anime", kwargs={"anime_id": anime_id, "e": 1}) ) - META = { - "title": f"Anime: {anime_data.get('title').get('romaji')}", - } LANGUAGE_CODE = i18npatterns(request.LANGUAGE_CODE) - request.meta.update(META) - - context = { - "anime": anime_data, - } - - return render(request, f"{LANGUAGE_CODE}/anime/anime.html", context) + request.meta.update( + {"title": f"Anime: {anime_data.get('title', {}).get('romaji')}"} + ) + return render(request, f"{LANGUAGE_CODE}/anime/anime.html", {"anime": anime_data}) diff --git a/middleware/i18nmiddleware.py b/middleware/i18nmiddleware.py index b0e7e077..8777491d 100644 --- a/middleware/i18nmiddleware.py +++ b/middleware/i18nmiddleware.py @@ -1,14 +1,12 @@ from django.utils.deprecation import MiddlewareMixin +from django.utils.translation import activate class I18NMiddleware(MiddlewareMixin): def process_request(self, request): - language = request.COOKIES.get("site_language") - if language: - request.LANGUAGE_CODE = language - else: - request.LANGUAGE_CODE = "en" + language = request.COOKIES.get("site_language", "en") request.LANGUAGE_CODE = language + activate(language) def process_response(self, request, response): if not request.COOKIES.get("site_language"): diff --git a/services/stream/views.py b/services/stream/views.py index da1ff6dc..21e15dc1 100644 --- a/services/stream/views.py +++ b/services/stream/views.py @@ -1,10 +1,12 @@ # views.py import os import random -from hashlib import md5 +from urllib.parse import urlparse + +from django.conf import settings from services.stream.songs import MUSIC_FILES import requests -from django.http import HttpResponse, JsonResponse +from django.http import HttpResponse, HttpResponseForbidden, JsonResponse CDN_URL = os.getenv("CDN_URL") MUSIC_FILES_COUNT = len(MUSIC_FILES) @@ -24,11 +26,37 @@ def random_song(request) -> JsonResponse: def stream_song(request, song_id: int) -> HttpResponse: - song = MUSIC_FILES[song_id - 1] - stream_url = get_stream_url(song["songName"]) - response = requests.get(stream_url, stream=True) - - return HttpResponse( - response.raw.read(), - content_type=response.headers.get("Content-Type", "audio/mpeg"), - ) + if not request.COOKIES.get("csrftoken"): + return HttpResponseForbidden("Invalid request") + + referrer = request.META.get("HTTP_REFERER") + if not referrer: + return HttpResponseForbidden("Direct access not allowed") + + parsed_uri = urlparse(referrer) + referrer_host = parsed_uri.netloc.split(":")[0] + + if referrer_host not in settings.ALLOWED_HOSTS: + return HttpResponseForbidden("Access not allowed") + + try: + song = MUSIC_FILES[song_id - 1] + stream_url = get_stream_url(song["songName"]) + response = requests.get(stream_url, stream=True) + + if response.status_code != 200: + return HttpResponse(status=response.status_code) + + return HttpResponse( + response.raw.read(), + content_type=response.headers.get("Content-Type", "audio/mpeg"), + headers={ + "Content-Disposition": f"attachment; filename={song['songName']}.mp3", + "X-Frame-Options": "DENY", + "X-Content-Type-Options": "nosniff", + }, + ) + except (IndexError, KeyError): + return HttpResponse(status=404) + except Exception: + return HttpResponse(status=500) diff --git a/static/js/MathJax b/static/js/MathJax deleted file mode 160000 -Subproject 6273842a9746731b9ecca0de18ec9fd50a36df9 diff --git a/thatcomputerscientist/settings.py b/thatcomputerscientist/settings.py index 97c05169..917a8109 100644 --- a/thatcomputerscientist/settings.py +++ b/thatcomputerscientist/settings.py @@ -44,13 +44,20 @@ SECRET_KEY = os.getenv("AUTHORIZATION_STRING") # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True if os.getenv("ENVIRONMENT") == "development" else False -ALLOWED_HOSTS = ["*"] +ALLOWED_HOSTS = [ + "shi.foo", + "www.shi.foo", + "thatcomputerscientist.com", + "www.thatcomputerscientist.com", +] + (["localhost", "127.0.0.1"] if DEBUG else []) + CSRF_TRUSTED_ORIGINS = [ "https://*.thatcomputerscientist.com", "http://*.thatcomputerscientist.com", "https://*.shi.foo", "http://localhost", ] + DOMAIN_NAME = "shi.foo" SESSION_COOKIE_DOMAIN = ( os.getenv("DOMAIN") if os.getenv("ENVIRONMENT") == "development" else ".shi.foo" |
