diff --git a/api/funkwhale_api/music/admin.py b/api/funkwhale_api/music/admin.py index 219b40a91deeeb9d06dd55d3292aa5ca22ccc654..667a7c2a1b10659c4d608fa2af5b6bcaf04e2681 100644 --- a/api/funkwhale_api/music/admin.py +++ b/api/funkwhale_api/music/admin.py @@ -38,6 +38,7 @@ class ImportBatchAdmin(admin.ModelAdmin): search_fields = [ 'import_request__name', 'source', 'batch__pk', 'mbid'] + @admin.register(models.ImportJob) class ImportJobAdmin(admin.ModelAdmin): list_display = ['source', 'batch', 'track_file', 'status', 'mbid'] @@ -77,5 +78,10 @@ class TrackFileAdmin(admin.ModelAdmin): list_select_related = [ 'track' ] - search_fields = ['source', 'acoustid_track_id'] + search_fields = [ + 'source', + 'acoustid_track_id', + 'track__title', + 'track__album__title', + 'track__artist__name'] list_filter = ['mimetype'] diff --git a/api/funkwhale_api/subsonic/renderers.py b/api/funkwhale_api/subsonic/renderers.py index 74cf13d887d9186a1bd50b4569fe59970daff762..3a56645012f07910a9c0e175b3b3e3d6f6bec8f2 100644 --- a/api/funkwhale_api/subsonic/renderers.py +++ b/api/funkwhale_api/subsonic/renderers.py @@ -15,6 +15,9 @@ class SubsonicJSONRenderer(renderers.JSONRenderer): } } final['subsonic-response'].update(data) + if 'error' in final: + # an error was returned + final['subsonic-response']['status'] = 'failed' return super().render(final, accepted_media_type, renderer_context) @@ -31,6 +34,9 @@ class SubsonicXMLRenderer(renderers.JSONRenderer): 'version': '1.16.0', } final.update(data) + if 'error' in final: + # an error was returned + final['status'] = 'failed' tree = dict_to_xml_tree('subsonic-response', final) return b'<?xml version="1.0" encoding="UTF-8"?>\n' + ET.tostring(tree, encoding='utf-8') diff --git a/api/funkwhale_api/subsonic/views.py b/api/funkwhale_api/subsonic/views.py index 475e61aa73119ff8df9a71fa2a27cf3188dc0cff..2692a3dda804ad22c07ee340012ad037afb5e42b 100644 --- a/api/funkwhale_api/subsonic/views.py +++ b/api/funkwhale_api/subsonic/views.py @@ -31,15 +31,19 @@ def find_object(queryset, model_field='pk', field='id', cast=int): raw_value = data[field] except KeyError: return response.Response({ - 'code': 10, - 'message': "required parameter '{}' not present".format(field) + 'error': { + 'code': 10, + 'message': "required parameter '{}' not present".format(field) + } }) try: value = cast(raw_value) except (TypeError, ValidationError): return response.Response({ - 'code': 0, - 'message': 'For input string "{}"'.format(raw_value) + 'error': { + 'code': 0, + 'message': 'For input string "{}"'.format(raw_value) + } }) qs = queryset if hasattr(qs, '__call__'): @@ -48,9 +52,11 @@ def find_object(queryset, model_field='pk', field='id', cast=int): obj = qs.get(**{model_field: value}) except qs.model.DoesNotExist: return response.Response({ - 'code': 70, - 'message': '{} not found'.format( - qs.model.__class__.__name__) + 'error': { + 'code': 70, + 'message': '{} not found'.format( + qs.model.__class__.__name__) + } }) kwargs['obj'] = obj return func(self, request, *args, **kwargs) @@ -83,15 +89,14 @@ class SubsonicViewSet(viewsets.GenericViewSet): payload = { 'status': 'failed' } - try: + if exc.__class__ in mapping: code, message = mapping[exc.__class__] - except KeyError: - return super().handle_exception(exc) else: - payload['error'] = { - 'code': code, - 'message': message - } + return super().handle_exception(exc) + payload['error'] = { + 'code': code, + 'message': message + } return response.Response(payload, status=200) @@ -450,8 +455,10 @@ class SubsonicViewSet(viewsets.GenericViewSet): name = data.get('name', '') if not name: return response.Response({ - 'code': 10, - 'message': 'Playlist ID or name must be specified.' + 'error': { + 'code': 10, + 'message': 'Playlist ID or name must be specified.' + } }, data) playlist = request.user.playlists.create( diff --git a/changes/changelog.d/199.bugfix b/changes/changelog.d/199.bugfix new file mode 100644 index 0000000000000000000000000000000000000000..0dff4f9fe4d8efb07d58ffb10580b334f2fdff86 --- /dev/null +++ b/changes/changelog.d/199.bugfix @@ -0,0 +1 @@ +Uplayable tracks are now properly disabled in the interface (#199) diff --git a/front/src/components/audio/PlayButton.vue b/front/src/components/audio/PlayButton.vue index 2662f30b33a5321a1e2d0121c3930c46b5f66097..efa59a29d5c65fe30b88aedc5f453b8e9a250a61 100644 --- a/front/src/components/audio/PlayButton.vue +++ b/front/src/components/audio/PlayButton.vue @@ -1,8 +1,9 @@ <template> - <div :class="['ui', {'tiny': discrete}, 'buttons']"> + <div :title="title" :class="['ui', {'tiny': discrete}, 'buttons']"> <button :title="$t('Add to current queue')" @click="addNext(true)" + :disabled="!playable" :class="['ui', {loading: isLoading}, {'mini': discrete}, {disabled: !playable}, 'button']"> <i class="ui play icon"></i> <template v-if="!discrete"><slot><i18next path="Play"/></slot></template> @@ -10,9 +11,9 @@ <div v-if="!discrete" :class="['ui', {disabled: !playable}, 'floating', 'dropdown', 'icon', 'button']"> <i class="dropdown icon"></i> <div class="menu"> - <div class="item"@click="add"><i class="plus icon"></i><i18next path="Add to queue"/></div> - <div class="item"@click="addNext()"><i class="step forward icon"></i><i18next path="Play next"/></div> - <div class="item"@click="addNext(true)"><i class="arrow down icon"></i><i18next path="Play now"/></div> + <div class="item" :disabled="!playable" @click="add"><i class="plus icon"></i><i18next path="Add to queue"/></div> + <div class="item" :disabled="!playable" @click="addNext()"><i class="step forward icon"></i><i18next path="Play next"/></div> + <div class="item" :disabled="!playable" @click="addNext(true)"><i class="arrow down icon"></i><i18next path="Play now"/></div> </div> </div> </div> @@ -45,9 +46,18 @@ export default { jQuery(this.$el).find('.ui.dropdown').dropdown() }, computed: { + title () { + if (this.playable) { + return this.$t('Play immediatly') + } else { + if (this.track) { + return this.$t('This track is not imported and cannot be played') + } + } + }, playable () { if (this.track) { - return true + return this.track.files.length > 0 } else if (this.tracks) { return this.tracks.length > 0 } else if (this.playlist) {