Skip to content
Snippets Groups Projects
Commit 9ee0f09f authored by Eliot Berriot's avatar Eliot Berriot
Browse files

Merge branch '258-subsonic-covers' into 'develop'

Resolve "Include album covers in subsonic API"

Closes #258

See merge request funkwhale/funkwhale!220
parents 218a9254 8d50743b
No related branches found
No related tags found
No related merge requests found
...@@ -242,8 +242,8 @@ def get_file_path(audio_file): ...@@ -242,8 +242,8 @@ def get_file_path(audio_file):
'You need to specify MUSIC_DIRECTORY_SERVE_PATH and ' 'You need to specify MUSIC_DIRECTORY_SERVE_PATH and '
'MUSIC_DIRECTORY_PATH to serve in-place imported files' 'MUSIC_DIRECTORY_PATH to serve in-place imported files'
) )
path = audio_file.replace(prefix, serve_path, 1).encode('utf-8') path = audio_file.replace(prefix, serve_path, 1)
return path return path.encode('utf-8')
def handle_serve(track_file): def handle_serve(track_file):
......
...@@ -57,8 +57,10 @@ class GetArtistSerializer(serializers.Serializer): ...@@ -57,8 +57,10 @@ class GetArtistSerializer(serializers.Serializer):
'name': album.title, 'name': album.title,
'artist': artist.name, 'artist': artist.name,
'created': album.creation_date, 'created': album.creation_date,
'songCount': len(album.tracks.all()) 'songCount': len(album.tracks.all()),
} }
if album.cover:
album_data['coverArt'] = 'al-{}'.format(album.id)
if album.release_date: if album.release_date:
album_data['year'] = album.release_date.year album_data['year'] = album.release_date.year
payload['album'].append(album_data) payload['album'].append(album_data)
...@@ -81,6 +83,8 @@ def get_track_data(album, track, tf): ...@@ -81,6 +83,8 @@ def get_track_data(album, track, tf):
'artistId': album.artist.pk, 'artistId': album.artist.pk,
'type': 'music', 'type': 'music',
} }
if track.album.cover:
data['coverArt'] = 'al-{}'.format(track.album.id)
if tf.bitrate: if tf.bitrate:
data['bitrate'] = int(tf.bitrate/1000) data['bitrate'] = int(tf.bitrate/1000)
if tf.size: if tf.size:
...@@ -98,6 +102,9 @@ def get_album2_data(album): ...@@ -98,6 +102,9 @@ def get_album2_data(album):
'artist': album.artist.name, 'artist': album.artist.name,
'created': album.creation_date, 'created': album.creation_date,
} }
if album.cover:
payload['coverArt'] = 'al-{}'.format(album.id)
try: try:
payload['songCount'] = album._tracks_count payload['songCount'] = album._tracks_count
except AttributeError: except AttributeError:
......
import datetime import datetime
from django.conf import settings
from django.utils import timezone from django.utils import timezone
from rest_framework import exceptions from rest_framework import exceptions
...@@ -459,7 +460,7 @@ class SubsonicViewSet(viewsets.GenericViewSet): ...@@ -459,7 +460,7 @@ class SubsonicViewSet(viewsets.GenericViewSet):
'code': 10, 'code': 10,
'message': 'Playlist ID or name must be specified.' 'message': 'Playlist ID or name must be specified.'
} }
}, data) })
playlist = request.user.playlists.create( playlist = request.user.playlists.create(
name=name name=name
...@@ -503,3 +504,51 @@ class SubsonicViewSet(viewsets.GenericViewSet): ...@@ -503,3 +504,51 @@ class SubsonicViewSet(viewsets.GenericViewSet):
} }
} }
return response.Response(data) return response.Response(data)
@list_route(
methods=['get', 'post'],
url_name='get_cover_art',
url_path='getCoverArt')
def get_cover_art(self, request, *args, **kwargs):
data = request.GET or request.POST
id = data.get('id', '')
if not id:
return response.Response({
'error': {
'code': 10,
'message': 'cover art ID must be specified.'
}
})
if id.startswith('al-'):
try:
album_id = int(id.replace('al-', ''))
album = music_models.Album.objects.exclude(
cover__isnull=True
).exclude(cover='').get(pk=album_id)
except (TypeError, ValueError, music_models.Album.DoesNotExist):
return response.Response({
'error': {
'code': 70,
'message': 'cover art not found.'
}
})
cover = album.cover
else:
return response.Response({
'error': {
'code': 70,
'message': 'cover art not found.'
}
})
mapping = {
'nginx': 'X-Accel-Redirect',
'apache2': 'X-Sendfile',
}
path = music_views.get_file_path(cover)
file_header = mapping[settings.REVERSE_PROXY_TYPE]
# let the proxy set the content-type
r = response.Response({}, content_type='')
r[file_header] = path
return r
\ No newline at end of file
...@@ -60,6 +60,7 @@ def test_get_artist_serializer(factories): ...@@ -60,6 +60,7 @@ def test_get_artist_serializer(factories):
'album': [ 'album': [
{ {
'id': album.pk, 'id': album.pk,
'coverArt': 'al-{}'.format(album.id),
'artistId': artist.pk, 'artistId': artist.pk,
'name': album.title, 'name': album.title,
'artist': artist.name, 'artist': artist.name,
...@@ -88,11 +89,13 @@ def test_get_album_serializer(factories): ...@@ -88,11 +89,13 @@ def test_get_album_serializer(factories):
'songCount': 1, 'songCount': 1,
'created': album.creation_date, 'created': album.creation_date,
'year': album.release_date.year, 'year': album.release_date.year,
'coverArt': 'al-{}'.format(album.id),
'song': [ 'song': [
{ {
'id': track.pk, 'id': track.pk,
'isDir': 'false', 'isDir': 'false',
'title': track.title, 'title': track.title,
'coverArt': 'al-{}'.format(album.id),
'album': album.title, 'album': album.title,
'artist': artist.name, 'artist': artist.name,
'track': track.position, 'track': track.position,
......
...@@ -391,3 +391,16 @@ def test_get_indexes(f, db, logged_in_api_client, factories): ...@@ -391,3 +391,16 @@ def test_get_indexes(f, db, logged_in_api_client, factories):
assert response.status_code == 200 assert response.status_code == 200
assert response.data == expected assert response.data == expected
def test_get_cover_art_album(factories, logged_in_api_client):
url = reverse('api:subsonic-get-cover-art')
assert url.endswith('getCoverArt') is True
album = factories['music.Album']()
response = logged_in_api_client.get(url, {'id': 'al-{}'.format(album.pk)})
assert response.status_code == 200
assert response['Content-Type'] == ''
assert response['X-Accel-Redirect'] == music_views.get_file_path(
album.cover
).decode('utf-8')
Implemented getCovertArt in Subsonic API to serve album covers (#258)
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment