diff options
Diffstat (limited to 'services/stream')
| -rw-r--r-- | services/stream/urls.py | 1 | ||||
| -rw-r--r-- | services/stream/views.py | 106 |
2 files changed, 105 insertions, 2 deletions
diff --git a/services/stream/urls.py b/services/stream/urls.py index 4fc57ee5..aaedb926 100644 --- a/services/stream/urls.py +++ b/services/stream/urls.py @@ -6,4 +6,5 @@ app_name = "stream" urlpatterns = [ path("random-song", views.random_song, name="random_song"), path("song/<int:song_id>", views.stream_song, name="stream_song"), + path("anime/", views.anime_stream, name="anime_stream"), ] diff --git a/services/stream/views.py b/services/stream/views.py index 21e15dc1..a646d185 100644 --- a/services/stream/views.py +++ b/services/stream/views.py @@ -1,12 +1,18 @@ # views.py import os import random -from urllib.parse import urlparse +import re +from urllib.parse import quote, urljoin, urlparse from django.conf import settings from services.stream.songs import MUSIC_FILES import requests -from django.http import HttpResponse, HttpResponseForbidden, JsonResponse +from django.http import ( + HttpResponse, + HttpResponseForbidden, + JsonResponse, + StreamingHttpResponse, +) CDN_URL = os.getenv("CDN_URL") MUSIC_FILES_COUNT = len(MUSIC_FILES) @@ -60,3 +66,99 @@ def stream_song(request, song_id: int) -> HttpResponse: return HttpResponse(status=404) except Exception: return HttpResponse(status=500) + + +def anime_stream(request): + 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") + + url = request.GET.get("url") + + if not url: + return HttpResponse("No URL provided", status=400) + + try: + # Make the request to the target server + response = requests.get( + url, + stream=True, + headers={ + "User-Agent": request.headers.get("User-Agent", ""), + "Referer": request.headers.get("Referer", ""), + "Origin": request.headers.get("Origin", ""), + }, + ) + + content_type = response.headers.get("content-type", "") + + # Handle M3U8 playlists + if "m3u8" in content_type or url.endswith(".m3u8"): + content = response.text + base_url = url.rsplit("/", 1)[0] + "/" + + # Modify the playlist URLs to go through our proxy + modified_content = [] + for line in content.splitlines(): + if line.startswith("#"): + modified_content.append(line) + elif line.strip(): # Handle content lines (URLs) + # Handle both absolute and relative URLs + if not line.startswith("http"): + line = urljoin(base_url, line) + proxy_url = f"/services/stream/anime?url={quote(line)}" + modified_content.append(proxy_url) + else: + modified_content.append(line) + + return HttpResponse( + "\n".join(modified_content), + content_type="application/vnd.apple.mpegurl", + ) + + # Handle VTT files + elif url.endswith(".vtt"): + content = response.text + base_url = url.rsplit("/", 1)[0] + "/" + + # Pattern to match both full URLs and relative paths with optional #xywh fragment + sprite_pattern = r'((?:https?://[^\s<>"]+?|[\w-]+\.(?:jpg|jpeg|png|webp))(?:#xywh=[\d,]+)?)' + + def replace_url(match): + sprite_url = match.group(1) + # If it's not a full URL, join it with the base URL + if not sprite_url.startswith("http"): + sprite_url = urljoin(base_url, sprite_url) + return f"/watch/stream?url={quote(sprite_url)}" + + # Replace all image URLs with proxied versions + modified_content = re.sub(sprite_pattern, replace_url, content) + + return HttpResponse(modified_content, content_type="text/vtt") + + # Handle images (thumbnails) + elif any(ext in url.lower() for ext in [".jpg", ".jpeg", ".png", ".webp"]): + return StreamingHttpResponse( + response.iter_content(chunk_size=8192), + content_type=response.headers.get("content-type"), + status=response.status_code, + ) + + # For video segments and other content, stream directly + return StreamingHttpResponse( + response.iter_content(chunk_size=8192), + content_type=response.headers.get("content-type"), + status=response.status_code, + ) + + except requests.RequestException as e: + return HttpResponse(f"Error proxying request: {str(e)}", status=500) |
