diff options
Diffstat (limited to 'apps')
| -rw-r--r-- | apps/stream/__init__.py | 0 | ||||
| -rw-r--r-- | apps/stream/admin.py | 3 | ||||
| -rw-r--r-- | apps/stream/apps.py | 6 | ||||
| -rw-r--r-- | apps/stream/migrations/__init__.py | 0 | ||||
| -rw-r--r-- | apps/stream/models.py | 3 | ||||
| -rw-r--r-- | apps/stream/tests.py | 3 | ||||
| -rw-r--r-- | apps/stream/urls.py | 9 | ||||
| -rw-r--r-- | apps/stream/views.py | 101 |
8 files changed, 125 insertions, 0 deletions
diff --git a/apps/stream/__init__.py b/apps/stream/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/apps/stream/__init__.py diff --git a/apps/stream/admin.py b/apps/stream/admin.py new file mode 100644 index 00000000..8c38f3f3 --- /dev/null +++ b/apps/stream/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/apps/stream/apps.py b/apps/stream/apps.py new file mode 100644 index 00000000..da754011 --- /dev/null +++ b/apps/stream/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class StreamConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "apps.stream" diff --git a/apps/stream/migrations/__init__.py b/apps/stream/migrations/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/apps/stream/migrations/__init__.py diff --git a/apps/stream/models.py b/apps/stream/models.py new file mode 100644 index 00000000..71a83623 --- /dev/null +++ b/apps/stream/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/apps/stream/tests.py b/apps/stream/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/apps/stream/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/stream/urls.py b/apps/stream/urls.py new file mode 100644 index 00000000..326c83e6 --- /dev/null +++ b/apps/stream/urls.py @@ -0,0 +1,9 @@ +from django.urls import path + +from . import views + +app_name = "stream" +urlpatterns = [ + path("random-song", views.random_song, name="random_song"), + path("song/<str:song_id>", views.stream_song, name="stream_song"), +] diff --git a/apps/stream/views.py b/apps/stream/views.py new file mode 100644 index 00000000..820792c2 --- /dev/null +++ b/apps/stream/views.py @@ -0,0 +1,101 @@ +# views.py +import os +import random +from hashlib import md5 +from typing import Dict, Union + +import requests +from django.http import HttpResponse, JsonResponse +from django.conf import settings +from django.core.cache import cache + +# Constants from environment variables +API_CONFIG = { + "BASE_URL": os.getenv("MUSIC_API_BASE_URL", "http://music.shi.foo/rest"), + "USERNAME": os.getenv("MUSIC_API_USERNAME"), + "PASSWORD": os.getenv("MUSIC_API_PASSWORD"), + "VERSION": os.getenv("MUSIC_API_VERSION", "1.16.1"), + "CLIENT": os.getenv("MUSIC_API_CLIENT", "Shifoo"), +} + + +def get_auth_params() -> Dict[str, str]: + """Generate authentication parameters for API requests.""" + salt = "".join(random.choices("abcdefghijklmnopqrstuvwxyz0123456789", k=6)) + token = md5((API_CONFIG["PASSWORD"] + salt).encode()).hexdigest() + + return { + "u": API_CONFIG["USERNAME"], + "t": token, + "s": salt, + "v": API_CONFIG["VERSION"], + "c": API_CONFIG["CLIENT"], + } + + +def make_api_request( + endpoint: str, params: Dict = None, stream: bool = False +) -> Union[requests.Response, Dict]: + """Make an API request with error handling.""" + auth_params = get_auth_params() + if params: + auth_params.update(params) + + url = f"{API_CONFIG['BASE_URL']}/{endpoint}" + + try: + response = requests.get(url, params=auth_params, stream=stream) + response.raise_for_status() + return response if stream else response.json() + except requests.RequestException as e: + # Log error here if you have logging configured + return {"error": str(e)} + + +def random_song(request) -> JsonResponse: + """Get a random song from the API.""" + # Try to get cached response first + cache_key = "random_song_response" + cached_response = cache.get(cache_key) + if cached_response: + return JsonResponse(cached_response) + + response = make_api_request("getRandomSongs", {"size": 1, "f": "json"}) + + if "error" in response: + return JsonResponse({"error": response["error"]}, status=500) + + try: + subsonic_response = response.get("subsonic-response", {}) + song = subsonic_response.get("randomSongs", {}).get("song", [{}])[0] + + if not song: + return JsonResponse({"error": "No song found"}, status=404) + + # Construct response data + response_data = { + "song": song, + "coverURL": f"{API_CONFIG['BASE_URL']}/getCoverArt", + "coverParams": {"id": song.get("coverArt"), **get_auth_params()}, + } + + # Cache the response for a short period + cache.set(cache_key, response_data, 30) # Cache for 30 seconds + + return JsonResponse(response_data) + except Exception as e: + # Log error here if you have logging configured + return JsonResponse({"error": str(e)}, status=500) + + +def stream_song(request, song_id: str) -> HttpResponse: + """Stream a specific song by ID.""" + response = make_api_request("stream", {"id": song_id}, stream=True) + + if isinstance(response, dict) and "error" in response: + return JsonResponse({"error": response["error"]}, status=500) + + return HttpResponse( + response.raw.read(), + content_type=response.headers.get("Content-Type", "audio/mpeg"), + ) |
