Fix #260: Implemented scrobble endpoint of subsonic API
This commit is contained in:
parent
9ee0f09ff4
commit
0f792bf75c
|
@ -4,6 +4,7 @@ from django.db.models import functions, Count
|
||||||
|
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from funkwhale_api.history import models as history_models
|
||||||
from funkwhale_api.music import models as music_models
|
from funkwhale_api.music import models as music_models
|
||||||
|
|
||||||
|
|
||||||
|
@ -228,3 +229,18 @@ def get_music_directory_data(artist):
|
||||||
td['size'] = tf.size
|
td['size'] = tf.size
|
||||||
data['child'].append(td)
|
data['child'].append(td)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
class ScrobbleSerializer(serializers.Serializer):
|
||||||
|
submission = serializers.BooleanField(default=True, required=False)
|
||||||
|
id = serializers.PrimaryKeyRelatedField(
|
||||||
|
queryset=music_models.Track.objects.annotate(
|
||||||
|
files_count=Count('files')
|
||||||
|
).filter(files_count__gt=0)
|
||||||
|
)
|
||||||
|
|
||||||
|
def create(self, data):
|
||||||
|
return history_models.Listening.objects.create(
|
||||||
|
user=self.context['user'],
|
||||||
|
track=data['id'],
|
||||||
|
)
|
||||||
|
|
|
@ -519,7 +519,7 @@ class SubsonicViewSet(viewsets.GenericViewSet):
|
||||||
'message': 'cover art ID must be specified.'
|
'message': 'cover art ID must be specified.'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if id.startswith('al-'):
|
if id.startswith('al-'):
|
||||||
try:
|
try:
|
||||||
album_id = int(id.replace('al-', ''))
|
album_id = int(id.replace('al-', ''))
|
||||||
|
@ -551,4 +551,23 @@ class SubsonicViewSet(viewsets.GenericViewSet):
|
||||||
# let the proxy set the content-type
|
# let the proxy set the content-type
|
||||||
r = response.Response({}, content_type='')
|
r = response.Response({}, content_type='')
|
||||||
r[file_header] = path
|
r[file_header] = path
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
@list_route(
|
||||||
|
methods=['get', 'post'],
|
||||||
|
url_name='scrobble',
|
||||||
|
url_path='scrobble')
|
||||||
|
def scrobble(self, request, *args, **kwargs):
|
||||||
|
data = request.GET or request.POST
|
||||||
|
serializer = serializers.ScrobbleSerializer(
|
||||||
|
data=data, context={'user': request.user})
|
||||||
|
if not serializer.is_valid():
|
||||||
|
return response.Response({
|
||||||
|
'error': {
|
||||||
|
'code': 0,
|
||||||
|
'message': 'Invalid payload'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if serializer.validated_data['submission']:
|
||||||
|
serializer.save()
|
||||||
|
return response.Response({})
|
||||||
|
|
|
@ -214,3 +214,22 @@ def test_directory_serializer_artist(factories):
|
||||||
}
|
}
|
||||||
data = serializers.get_music_directory_data(artist)
|
data = serializers.get_music_directory_data(artist)
|
||||||
assert data == expected
|
assert data == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_scrobble_serializer(factories):
|
||||||
|
tf = factories['music.TrackFile']()
|
||||||
|
track = tf.track
|
||||||
|
user = factories['users.User']()
|
||||||
|
payload = {
|
||||||
|
'id': track.pk,
|
||||||
|
'submission': True,
|
||||||
|
}
|
||||||
|
serializer = serializers.ScrobbleSerializer(
|
||||||
|
data=payload, context={'user': user})
|
||||||
|
|
||||||
|
assert serializer.is_valid(raise_exception=True)
|
||||||
|
|
||||||
|
listening = serializer.save()
|
||||||
|
|
||||||
|
assert listening.user == user
|
||||||
|
assert listening.track == track
|
||||||
|
|
|
@ -404,3 +404,17 @@ def test_get_cover_art_album(factories, logged_in_api_client):
|
||||||
assert response['X-Accel-Redirect'] == music_views.get_file_path(
|
assert response['X-Accel-Redirect'] == music_views.get_file_path(
|
||||||
album.cover
|
album.cover
|
||||||
).decode('utf-8')
|
).decode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
|
def test_scrobble(factories, logged_in_api_client):
|
||||||
|
tf = factories['music.TrackFile']()
|
||||||
|
track = tf.track
|
||||||
|
url = reverse('api:subsonic-scrobble')
|
||||||
|
assert url.endswith('scrobble') is True
|
||||||
|
response = logged_in_api_client.get(
|
||||||
|
url, {'id': track.pk, 'submission': True})
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
l = logged_in_api_client.user.listenings.latest('id')
|
||||||
|
assert l.track == track
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Implemented scrobble endpoint of subsonic API, listenings are now tracked
|
||||||
|
correctly from third party apps that use this endpoint (#260)
|
Loading…
Reference in New Issue