diff options
| author | Bobby <[email protected]> | 2024-12-24 06:50:59 -0500 |
|---|---|---|
| committer | Bobby <[email protected]> | 2024-12-24 06:50:59 -0500 |
| commit | 6df9f0dc40501e8f55bcc883dfe5be65e60d3c3d (patch) | |
| tree | 273148d564c11ae46e9f96c0231ce57427f5591f /apps | |
| parent | 4feba2452a151ed999d52d4a0d53b0b0584bf70e (diff) | |
| download | thatcomputerscientist-6df9f0dc40501e8f55bcc883dfe5be65e60d3c3d.tar.xz thatcomputerscientist-6df9f0dc40501e8f55bcc883dfe5be65e60d3c3d.zip | |
bucket load of things
Diffstat (limited to 'apps')
| -rw-r--r-- | apps/administration/urls.py | 26 | ||||
| -rw-r--r-- | apps/administration/views.py | 189 | ||||
| -rw-r--r-- | apps/anime/views.py | 155 |
3 files changed, 317 insertions, 53 deletions
diff --git a/apps/administration/urls.py b/apps/administration/urls.py index d4781c66..1aa0146f 100644 --- a/apps/administration/urls.py +++ b/apps/administration/urls.py @@ -3,4 +3,28 @@ from django.urls import path from . import views app_name = "administration" -urlpatterns = [] +urlpatterns = [ + path( + "storage-buckets/", views.manage_storage_buckets, name="manage_storage_buckets" + ), + path( + "storage-buckets/create-folder/", + views.create_folder, + name="manage_storage_bucket_create_folder", + ), + path( + "storage-buckets/upload-file/", + views.upload_file, + name="manage_storage_bucket_upload_file", + ), + path( + "storage-buckets/<str:bucket_name>/", + views.view_single_bucket, + name="view_single_bucket", + ), + path( + "storage-buckets/<str:bucket_name>/<path:object_path>/", + views.view_object_path, + name="view_object_path", + ), +] diff --git a/apps/administration/views.py b/apps/administration/views.py index 91ea44a2..bfe2cc57 100644 --- a/apps/administration/views.py +++ b/apps/administration/views.py @@ -1,3 +1,192 @@ +import os +from uuid import uuid4 + +from django.contrib.auth.decorators import login_required, user_passes_test +from django.http import JsonResponse +from django.views.decorators.http import require_http_methods +from django.core.files.storage import default_storage +from internal.admin_utilities import MinioFileManager from django.shortcuts import render + # Create your views here. +@login_required +@user_passes_test(lambda u: u.is_superuser) +def manage_storage_buckets(request): + file_manager = MinioFileManager() + buckets = file_manager.list_buckets() + + context = { + "buckets": buckets, + } + + return render( + request, "en/administration/manage_storage_buckets_home.html", context + ) + + +@login_required +@user_passes_test(lambda u: u.is_superuser) +def view_single_bucket(request, bucket_name): + file_manager = MinioFileManager() + objects = file_manager.list_objects(bucket_name) + for obj in objects: + file_type = obj["path"].split(".")[-1] + obj["file_type"] = file_type.lower() + + context = { + "bucket_name": bucket_name, + "objects": objects, + } + + return render( + request, "en/administration/manage_storage_buckets_bucket.html", context + ) + + +@login_required +@user_passes_test(lambda u: u.is_superuser) +def view_object_path(request, bucket_name, object_path): + file_manager = MinioFileManager() + objects = file_manager.list_objects(bucket_name, object_path) + for obj in objects: + file_type = obj["path"].split(".")[-1] + obj["file_type"] = file_type.lower() + + object_path_s = "" + if "/" in object_path: + object_path_s = object_path.split("/")[-2] + else: + object_path = f"{object_path}/" + + up_url = f"/admin/storage-buckets/{bucket_name}/{object_path_s}" + context = { + "bucket_name": bucket_name, + "object_path": object_path, + "objects": objects, + "up_url": up_url, + } + + return render( + request, "en/administration/manage_storage_buckets_bucket.html", context + ) + + +@login_required +@user_passes_test(lambda u: u.is_superuser) +@require_http_methods(["POST"]) +def create_folder(request): + """ + Create a folder in a specified bucket. + Requires staff permissions. + """ + try: + # For Django, parse POST data differently + bucket_name = request.POST.get("bucket") + folder_path = request.POST.get("folder_path", "").strip("/") + + # Additional debug logging + print(f"Bucket: {bucket_name}") + print(f"Folder Path: {folder_path}") + print(f"Request POST: {request.POST}") + print(f"Request body: {request.body}") + + # Validate inputs + if not bucket_name or not folder_path: + return JsonResponse( + {"status": "error", "message": "Bucket and folder path are required"}, + status=400, + ) + + # Initialize file manager without user context + file_manager = MinioFileManager() + + # Ensure folder path ends with a slash for Minio + full_folder_path = f"{folder_path}/" + + # Attempt to create folder + success = file_manager.create_folder( + bucket=bucket_name, folder_path=full_folder_path + ) + + if success: + return JsonResponse( + { + "status": "success", + "message": "Folder created successfully", + "folder_path": folder_path, + } + ) + else: + return JsonResponse( + {"status": "error", "message": "Failed to create folder"}, status=500 + ) + + except Exception as e: + # Log the full error for debugging + import traceback + + traceback.print_exc() + return JsonResponse({"status": "error", "message": str(e)}, status=500) + + +@login_required +@require_http_methods(["POST"]) +def upload_file(request): + """ + Upload a file to a specified bucket. + Requires user to be logged in. + """ + try: + # Get uploaded file + uploaded_file = request.FILES.get("file") + bucket_name = request.POST.get("bucket") + current_path = request.POST.get("current_path", "") + + # Validate inputs + if not uploaded_file or not bucket_name: + return JsonResponse( + {"status": "error", "message": "File and bucket are required"}, + status=400, + ) + + # Generate unique filename + file_ext = os.path.splitext(uploaded_file.name)[1] + unique_filename = f"{uuid4()}{file_ext}" + + # Construct full path + full_path = ( + os.path.join(current_path, unique_filename) + if current_path + else unique_filename + ) + + # Temporarily save file to local storage + temp_file_path = default_storage.save(f"temp/{unique_filename}", uploaded_file) + + # Initialize file manager without user context + file_manager = MinioFileManager() + + # Upload file + success = file_manager.upload_file( + bucket=bucket_name, file_path=temp_file_path, object_name=full_path + ) + + # Clean up temporary file + default_storage.delete(temp_file_path) + + if success: + return JsonResponse( + { + "status": "success", + "message": "File uploaded successfully", + "filename": unique_filename, + } + ) + else: + return JsonResponse( + {"status": "error", "message": "Failed to upload file"}, status=500 + ) + + except Exception as e: + return JsonResponse({"status": "error", "message": str(e)}, status=500) diff --git a/apps/anime/views.py b/apps/anime/views.py index c1012d09..fe1e90dc 100644 --- a/apps/anime/views.py +++ b/apps/anime/views.py @@ -2,10 +2,8 @@ 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 +from internal.cache_utils import cache_data genres = [ "Action", @@ -43,6 +41,24 @@ CONSUMET_BASE_URL = os.getenv("CONSUMET_URL") ZORO_URL = os.getenv("ZORO_URL") +def sort_mapper(sort_by, order): + sort_mappings = { + "popularity": "POPULARITY", + "trending": "TRENDING", + "start_date": "START_DATE", + "end_date": "END_DATE", + "score": "SCORE", + "favourites": "FAVOURITES", + "title": "TITLE_ROMAJI", + } + + if sort_by not in sort_mappings or order not in ["asc", "desc"]: + return None + + return f"{sort_mappings[sort_by]}{'_DESC' if order == 'desc' else ''}" + + +@cache_data(timeout=60 * 60, prefix="anime_data") def get_anime(anime_id, dub=False): provider = ANIME_PROVIDER_MAP.get(anime_id, "zoro") params = {"dub": "true"} if dub else {} @@ -79,6 +95,25 @@ def get_anime(anime_id, dub=False): return data +def find_optimal_server(episode_id, dub): + params = {"animeEpisodeId": episode_id} + response = requests.get(f"{ZORO_URL}/api/v2/hianime/episode/servers", params=params) + response = response.json() + if "message" in response: + return None + + response = response["data"] + if dub and "dub" in response and len(response["dub"]) > 0: + return response["dub"][0]["serverName"] + elif len(response["sub"]) > 0 and "sub" in response: + return response["sub"][0]["serverName"] + elif len(response["raw"]) > 0: + return response["raw"][0]["serverName"] + else: + return None + + +@cache_data(timeout=60 * 60 * 24 * 7, prefix="streaming_data") def get_anime_streaming_data(anime_id, current_episode, dub=False): provider = ANIME_PROVIDER_MAP.get(anime_id, "zoro") current_episode_id = current_episode.get("id") @@ -92,34 +127,52 @@ def get_anime_streaming_data(anime_id, current_episode, dub=False): response = requests.get( f"{ZORO_URL}/api/v2/hianime/episode/sources", params=params ) - return response.json() + if response.status_code == 200: + return response.json() + else: + server = find_optimal_server(episode_id, dub) + params["server"] = server + response = requests.get( + f"{ZORO_URL}/api/v2/hianime/episode/sources", params=params + ) + if response.status_code == 200: + return response.json() + else: + ANIME_PROVIDER_MAP[anime_id] = "gogoanime" + return get_anime_streaming_data(anime_id, current_episode, dub) else: response = requests.get( f"{CONSUMET_BASE_URL}/meta/anilist/watch/{current_episode_id}", ) - return response.json() + data = { + "tracks": [], + "intro": {"start": 0, "end": 0}, + "outro": {"start": 0, "end": 0}, + "sources": [], + "anilistID": 0, + "malID": 0, + } + + if not "message" in response.json(): + default_source = next( + (s for s in response.json()["sources"] if s["quality"] == "default"), + None, + ) + data["sources"].append({"url": default_source["url"], "type": "hls"}) + else: + data["sources"].append( + { + "url": "", + "type": "", + } + ) -def sort_mapper(sort_by, order): - sort_mappings = { - "popularity": "POPULARITY", - "trending": "TRENDING", - "start_date": "START_DATE", - "end_date": "END_DATE", - "score": "SCORE", - "favourites": "FAVOURITES", - "title": "TITLE_ROMAJI", - } - - if sort_by not in sort_mappings or order not in ["asc", "desc"]: - return None - - return f"{sort_mappings[sort_by]}{'_DESC' if order == 'desc' else ''}" + return {"data": data} -def anime_results( - sort="trending", order="desc", genre="", query="", page=1, per_page=12, status="" -): +@cache_data(timeout=60 * 60, prefix="anime_results") +def anime_results(**kwargs): supported_status = [ "releasing", "not_yet_released", @@ -127,6 +180,15 @@ def anime_results( "finished", "hiatus", ] + + sort = kwargs.get("sort", "trending") + order = kwargs.get("order", "desc") + genre = kwargs.get("genre", "") + query = kwargs.get("query", "") + page = kwargs.get("page", 1) + per_page = kwargs.get("per_page", 12) + status = kwargs.get("status", "") + params = { "page": page, "perPage": per_page, @@ -144,27 +206,6 @@ def anime_results( return response.json() -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 cached_response: - return cached_response - - response = view_func(request, anime_id, e, *args, **kwargs) - if response.status_code == 200: - cache.set(cache_key, response, timeout) - return response - - return _wrapped_view - - return decorator - - -# @cache_page(60 * 15) def home(request): LANGUAGE_CODE = i18npatterns(request.LANGUAGE_CODE) request.meta.update({"title": "Anime: Home"}) @@ -211,7 +252,6 @@ def search(request): 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) @@ -243,25 +283,36 @@ def anime(request, anime_id, e=None): return redirect( reverse("anime:anime", kwargs={"anime_id": anime_id, "e": 1}) ) + anime_data["current_episode"] = ( + anime_data.get("episodes", [])[e - 1] if e else None + ) - anime_data["current_episode"] = anime_data.get("episodes", [])[e - 1] if e else None anime_data["totalEpisodes"] = ( len(episode_numbers) if not anime_data["totalEpisodes"] else anime_data["totalEpisodes"] ) - streaming_data = get_anime_streaming_data( - anime_id, anime_data["current_episode"], dub - ) + if "current_episode" in anime_data: + streaming_data = get_anime_streaming_data( + anime_id, anime_data["current_episode"], dub + ) + print(streaming_data) + streaming_data["data"]["sources"] = streaming_data["data"]["sources"][0] + else: + streaming_data = {} + LANGUAGE_CODE = i18npatterns(request.LANGUAGE_CODE) request.meta.update( {"title": f"Anime: {anime_data.get('title', {}).get('romaji')}"} ) - streaming_data["data"]["sources"] = streaming_data["data"]["sources"][0] - print(streaming_data) + context = { + "anime": anime_data, + "streaming_data": streaming_data, + } + return render( request, f"{LANGUAGE_CODE}/anime/anime.html", - {"anime": anime_data, "streaming_data": streaming_data}, + context, ) |
