Fixed compat with iTunes
This commit is contained in:
parent
c70135c9e3
commit
c96fd5d539
|
@ -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")
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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 = "/?"
|
||||||
|
|
|
@ -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,39 +628,66 @@ 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()
|
||||||
actor = utils.get_actor_from_request(request)
|
return handle_stream(track, request, **config)
|
||||||
queryset = track.uploads.prefetch_related(
|
|
||||||
"track__album__artist", "track__artist"
|
|
||||||
)
|
|
||||||
explicit_file = request.GET.get("upload")
|
|
||||||
download = request.GET.get("download", "true").lower() == "true"
|
|
||||||
if explicit_file:
|
|
||||||
queryset = queryset.filter(uuid=explicit_file)
|
|
||||||
queryset = queryset.playable_by(actor)
|
|
||||||
queryset = queryset.order_by(F("audio_file").desc(nulls_last=True))
|
|
||||||
upload = queryset.first()
|
|
||||||
if not upload:
|
|
||||||
return Response(status=404)
|
|
||||||
|
|
||||||
format = request.GET.get("to")
|
|
||||||
max_bitrate = request.GET.get("max_bitrate")
|
|
||||||
try:
|
|
||||||
max_bitrate = min(max(int(max_bitrate), 0), 320) or None
|
|
||||||
except (TypeError, ValueError):
|
|
||||||
max_bitrate = None
|
|
||||||
|
|
||||||
if max_bitrate:
|
def handle_stream(track, request, download, explicit_file, format, max_bitrate):
|
||||||
max_bitrate = max_bitrate * 1000
|
actor = utils.get_actor_from_request(request)
|
||||||
return handle_serve(
|
queryset = track.uploads.prefetch_related("track__album__artist", "track__artist")
|
||||||
upload=upload,
|
if explicit_file:
|
||||||
user=request.user,
|
queryset = queryset.filter(uuid=explicit_file)
|
||||||
format=format,
|
queryset = queryset.playable_by(actor)
|
||||||
max_bitrate=max_bitrate,
|
queryset = queryset.order_by(F("audio_file").desc(nulls_last=True))
|
||||||
proxy_media=settings.PROXY_MEDIA,
|
upload = queryset.first()
|
||||||
download=download,
|
if not upload:
|
||||||
wsgi_request=request._request,
|
return Response(status=404)
|
||||||
)
|
|
||||||
|
try:
|
||||||
|
max_bitrate = min(max(int(max_bitrate), 0), 320) or None
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
max_bitrate = None
|
||||||
|
|
||||||
|
if max_bitrate:
|
||||||
|
max_bitrate = max_bitrate * 1000
|
||||||
|
return handle_serve(
|
||||||
|
upload=upload,
|
||||||
|
user=request.user,
|
||||||
|
format=format,
|
||||||
|
max_bitrate=max_bitrate,
|
||||||
|
proxy_media=settings.PROXY_MEDIA,
|
||||||
|
download=download,
|
||||||
|
wsgi_request=request._request,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
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(
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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"](
|
||||||
|
|
Loading…
Reference in New Issue