aboutsummaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
Diffstat (limited to 'apps')
-rw-r--r--apps/stream/__init__.py0
-rw-r--r--apps/stream/admin.py3
-rw-r--r--apps/stream/apps.py6
-rw-r--r--apps/stream/migrations/__init__.py0
-rw-r--r--apps/stream/models.py3
-rw-r--r--apps/stream/tests.py3
-rw-r--r--apps/stream/urls.py9
-rw-r--r--apps/stream/views.py101
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"),
+ )