Fixed compat with iTunes

This commit is contained in:
Agate 2020-08-11 13:07:39 +02:00
parent c70135c9e3
commit c96fd5d539
6 changed files with 95 additions and 35 deletions

View File

@ -20,6 +20,7 @@ router.register(r"tracks", views.TrackViewSet, "tracks")
router.register(r"uploads", views.UploadViewSet, "uploads") router.register(r"uploads", views.UploadViewSet, "uploads")
router.register(r"libraries", views.LibraryViewSet, "libraries") router.register(r"libraries", views.LibraryViewSet, "libraries")
router.register(r"listen", views.ListenViewSet, "listen") router.register(r"listen", views.ListenViewSet, "listen")
router.register(r"stream", views.StreamViewSet, "stream")
router.register(r"artists", views.ArtistViewSet, "artists") router.register(r"artists", views.ArtistViewSet, "artists")
router.register(r"channels", audio_views.ChannelViewSet, "channels") router.register(r"channels", audio_views.ChannelViewSet, "channels")
router.register(r"subscriptions", audio_views.SubscriptionsViewSet, "subscriptions") router.register(r"subscriptions", audio_views.SubscriptionsViewSet, "subscriptions")

View File

@ -830,7 +830,10 @@ def rss_serialize_item(upload):
{ {
# we enforce MP3, since it's the only format supported everywhere # we enforce MP3, since it's the only format supported everywhere
"url": federation_utils.full_url( "url": federation_utils.full_url(
upload.get_listen_url(to="mp3", download=False) reverse(
"api:v1:stream-detail", kwargs={"uuid": str(upload.track.uuid)}
)
+ ".mp3"
), ),
"length": upload.size or 0, "length": upload.size or 0,
"type": "audio/mpeg", "type": "audio/mpeg",

View File

@ -1,7 +1,7 @@
from rest_framework.routers import SimpleRouter from rest_framework.routers import DefaultRouter
class OptionalSlashRouter(SimpleRouter): class OptionalSlashRouter(DefaultRouter):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.trailing_slash = "/?" self.trailing_slash = "/?"

View File

@ -10,6 +10,7 @@ import django.db.utils
from django.utils import timezone from django.utils import timezone
from rest_framework import mixins from rest_framework import mixins
from rest_framework import renderers
from rest_framework import settings as rest_settings from rest_framework import settings as rest_settings
from rest_framework import views, viewsets from rest_framework import views, viewsets
from rest_framework.decorators import action from rest_framework.decorators import action
@ -614,7 +615,7 @@ def handle_serve(
return response return response
class ListenViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet): class ListenMixin(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
queryset = models.Track.objects.all() queryset = models.Track.objects.all()
serializer_class = serializers.TrackSerializer serializer_class = serializers.TrackSerializer
authentication_classes = ( authentication_classes = (
@ -627,13 +628,19 @@ class ListenViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
lookup_field = "uuid" lookup_field = "uuid"
def retrieve(self, request, *args, **kwargs): def retrieve(self, request, *args, **kwargs):
config = {
"explicit_file": request.GET.get("upload"),
"download": request.GET.get("download", "true").lower() == "true",
"format": request.GET.get("to"),
"max_bitrate": request.GET.get("max_bitrate"),
}
track = self.get_object() track = self.get_object()
return handle_stream(track, request, **config)
def handle_stream(track, request, download, explicit_file, format, max_bitrate):
actor = utils.get_actor_from_request(request) actor = utils.get_actor_from_request(request)
queryset = track.uploads.prefetch_related( queryset = track.uploads.prefetch_related("track__album__artist", "track__artist")
"track__album__artist", "track__artist"
)
explicit_file = request.GET.get("upload")
download = request.GET.get("download", "true").lower() == "true"
if explicit_file: if explicit_file:
queryset = queryset.filter(uuid=explicit_file) queryset = queryset.filter(uuid=explicit_file)
queryset = queryset.playable_by(actor) queryset = queryset.playable_by(actor)
@ -642,8 +649,6 @@ class ListenViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
if not upload: if not upload:
return Response(status=404) return Response(status=404)
format = request.GET.get("to")
max_bitrate = request.GET.get("max_bitrate")
try: try:
max_bitrate = min(max(int(max_bitrate), 0), 320) or None max_bitrate = min(max(int(max_bitrate), 0), 320) or None
except (TypeError, ValueError): except (TypeError, ValueError):
@ -662,6 +667,29 @@ class ListenViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
) )
class ListenViewSet(ListenMixin):
pass
class MP3Renderer(renderers.JSONRenderer):
format = "mp3"
media_type = "audio/mpeg"
class StreamViewSet(ListenMixin):
renderer_classes = [MP3Renderer]
def retrieve(self, request, *args, **kwargs):
config = {
"explicit_file": None,
"download": False,
"format": "mp3",
"max_bitrate": None,
}
track = self.get_object()
return handle_stream(track, request, **config)
class UploadViewSet( class UploadViewSet(
mixins.ListModelMixin, mixins.ListModelMixin,
mixins.CreateModelMixin, mixins.CreateModelMixin,

View File

@ -6,6 +6,7 @@ import pytest
import pytz import pytz
from django.templatetags.static import static from django.templatetags.static import static
from django.urls import reverse
from funkwhale_api.audio import serializers from funkwhale_api.audio import serializers
from funkwhale_api.common import serializers as common_serializers from funkwhale_api.common import serializers as common_serializers
@ -315,7 +316,10 @@ def test_rss_item_serializer(factories):
"enclosure": [ "enclosure": [
{ {
"url": federation_utils.full_url( "url": federation_utils.full_url(
upload.get_listen_url("mp3", download=False) reverse(
"api:v1:stream-detail", kwargs={"uuid": str(upload.track.uuid)}
)
+ ".mp3"
), ),
"length": upload.size, "length": upload.size,
"type": "audio/mpeg", "type": "audio/mpeg",

View File

@ -445,6 +445,30 @@ def test_listen_explicit_file(factories, logged_in_api_client, mocker, settings)
) )
def test_stream(factories, logged_in_api_client, mocker, settings):
mocked_serve = mocker.spy(views, "handle_serve")
upload = factories["music.Upload"](
library__privacy_level="everyone", import_status="finished"
)
url = (
reverse("api:v1:stream-detail", kwargs={"uuid": str(upload.track.uuid)})
+ ".mp3"
)
assert url.endswith("/{}.mp3".format(upload.track.uuid))
response = logged_in_api_client.get(url)
assert response.status_code == 200
mocked_serve.assert_called_once_with(
upload=upload,
user=logged_in_api_client.user,
format="mp3",
download=False,
max_bitrate=None,
proxy_media=True,
wsgi_request=response.wsgi_request,
)
def test_listen_no_proxy(factories, logged_in_api_client, settings): def test_listen_no_proxy(factories, logged_in_api_client, settings):
settings.PROXY_MEDIA = False settings.PROXY_MEDIA = False
upload = factories["music.Upload"]( upload = factories["music.Upload"](