Merge branch '258-subsonic-covers' into 'develop'
Resolve "Include album covers in subsonic API" Closes #258 See merge request funkwhale/funkwhale!220
This commit is contained in:
commit
9ee0f09ff4
|
@ -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):
|
||||||
'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):
|
||||||
'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):
|
||||||
'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:
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
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):
|
||||||
'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):
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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
|
|
@ -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):
|
||||||
'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):
|
||||||
|
|
||||||
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')
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Implemented getCovertArt in Subsonic API to serve album covers (#258)
|
Loading…
Reference in New Issue