From 8ae00b069889e164c4be624811cf280aedfd7df2 Mon Sep 17 00:00:00 2001 From: Eliot Berriot Date: Fri, 8 Mar 2019 10:30:04 +0100 Subject: [PATCH] Fix #747: Support embedding full artist discographies --- api/funkwhale_api/music/serializers.py | 30 ++++++++++++++++ api/funkwhale_api/music/spa_views.py | 19 ++++++++++ api/funkwhale_api/music/views.py | 2 ++ api/tests/music/test_spa_views.py | 20 +++++++++++ api/tests/music/test_views.py | 35 ++++++++++++++++++ changes/changelog.d/747.feature | 1 + front/src/EmbedFrame.vue | 8 +++-- front/src/components/audio/EmbedWizard.vue | 6 +++- front/src/components/library/Artist.vue | 42 ++++++++++++++++++++-- 9 files changed, 157 insertions(+), 6 deletions(-) create mode 100644 changes/changelog.d/747.feature diff --git a/api/funkwhale_api/music/serializers.py b/api/funkwhale_api/music/serializers.py index 99e3dc40c..8a8c4c5c2 100644 --- a/api/funkwhale_api/music/serializers.py +++ b/api/funkwhale_api/music/serializers.py @@ -470,6 +470,36 @@ class OembedSerializer(serializers.Serializer): "library_artist", kwargs={"pk": album.artist.pk} ) ) + elif match.url_name == "library_artist": + qs = models.Artist.objects.filter(pk=int(match.kwargs["pk"])) + try: + artist = qs.get() + except models.Artist.DoesNotExist: + raise serializers.ValidationError( + "No artist matching id {}".format(match.kwargs["pk"]) + ) + embed_type = "artist" + embed_id = artist.pk + album = ( + artist.albums.filter(cover__isnull=False) + .exclude(cover="") + .order_by("-id") + .first() + ) + + if album and album.cover: + data["thumbnail_url"] = federation_utils.full_url( + album.cover.crop["400x400"].url + ) + data["thumbnail_width"] = 400 + data["thumbnail_height"] = 400 + data["title"] = artist.name + data["description"] = artist.name + data["author_name"] = artist.name + data["height"] = 400 + data["author_url"] = federation_utils.full_url( + common_utils.spa_reverse("library_artist", kwargs={"pk": artist.pk}) + ) else: raise serializers.ValidationError( "Unsupported url: {}".format(validated_data["url"]) diff --git a/api/funkwhale_api/music/spa_views.py b/api/funkwhale_api/music/spa_views.py index 351431d0f..7fafedf61 100644 --- a/api/funkwhale_api/music/spa_views.py +++ b/api/funkwhale_api/music/spa_views.py @@ -2,6 +2,7 @@ import urllib.parse from django.conf import settings from django.urls import reverse +from django.db.models import Q from funkwhale_api.common import utils @@ -183,4 +184,22 @@ def library_artist(request, pk): } ) + if ( + models.Upload.objects.filter(Q(track__artist=obj) | Q(track__album__artist=obj)) + .playable_by(None) + .exists() + ): + metas.append( + { + "tag": "link", + "rel": "alternate", + "type": "application/json+oembed", + "href": ( + utils.join_url(settings.FUNKWHALE_URL, reverse("api:v1:oembed")) + + "?format=json&url={}".format(urllib.parse.quote_plus(artist_url)) + ), + } + ) + # twitter player is also supported in various software + metas += get_twitter_card_metas(type="artist", id=obj.pk) return metas diff --git a/api/funkwhale_api/music/views.py b/api/funkwhale_api/music/views.py index f6bed500c..e7897edbb 100644 --- a/api/funkwhale_api/music/views.py +++ b/api/funkwhale_api/music/views.py @@ -184,6 +184,8 @@ class TrackViewSet( "title", "album__release_date", "size", + "position", + "disc_number", "artist__name", ) diff --git a/api/tests/music/test_spa_views.py b/api/tests/music/test_spa_views.py index 901c6fe43..b9397009c 100644 --- a/api/tests/music/test_spa_views.py +++ b/api/tests/music/test_spa_views.py @@ -149,6 +149,7 @@ def test_library_album(spa_html, no_api_auth, client, factories, settings): def test_library_artist(spa_html, no_api_auth, client, factories, settings): album = factories["music.Album"]() + factories["music.Upload"](playable=True, track__album=album) artist = album.artist url = "/library/artists/{}".format(artist.pk) @@ -169,6 +170,25 @@ def test_library_artist(spa_html, no_api_auth, client, factories, settings): settings.FUNKWHALE_URL, album.cover.crop["400x400"].url ), }, + { + "tag": "link", + "rel": "alternate", + "type": "application/json+oembed", + "href": ( + utils.join_url(settings.FUNKWHALE_URL, reverse("api:v1:oembed")) + + "?format=json&url={}".format( + urllib.parse.quote_plus(utils.join_url(settings.FUNKWHALE_URL, url)) + ) + ), + }, + {"tag": "meta", "property": "twitter:card", "content": "player"}, + { + "tag": "meta", + "property": "twitter:player", + "content": serializers.get_embed_url("artist", id=artist.id), + }, + {"tag": "meta", "property": "twitter:player:width", "content": "600"}, + {"tag": "meta", "property": "twitter:player:height", "content": "400"}, ] metas = utils.parse_meta(response.content.decode()) diff --git a/api/tests/music/test_views.py b/api/tests/music/test_views.py index b11f9b006..7b12c6c8f 100644 --- a/api/tests/music/test_views.py +++ b/api/tests/music/test_views.py @@ -701,3 +701,38 @@ def test_oembed_album(factories, no_api_auth, api_client, settings): response = api_client.get(url, {"url": album_url, "format": "json"}) assert response.data == expected + + +def test_oembed_artist(factories, no_api_auth, api_client, settings): + settings.FUNKWHALE_URL = "http://test" + settings.FUNKWHALE_EMBED_URL = "http://embed" + track = factories["music.Track"]() + album = track.album + artist = track.artist + url = reverse("api:v1:oembed") + artist_url = "https://test.com/library/artists/{}".format(artist.pk) + iframe_src = "http://embed?type=artist&id={}".format(artist.pk) + expected = { + "version": "1.0", + "type": "rich", + "provider_name": settings.APP_NAME, + "provider_url": settings.FUNKWHALE_URL, + "height": 400, + "width": 600, + "title": artist.name, + "description": artist.name, + "thumbnail_url": federation_utils.full_url(album.cover.crop["400x400"].url), + "thumbnail_height": 400, + "thumbnail_width": 400, + "html": ''.format( + iframe_src + ), + "author_name": artist.name, + "author_url": federation_utils.full_url( + utils.spa_reverse("library_artist", kwargs={"pk": artist.pk}) + ), + } + + response = api_client.get(url, {"url": artist_url, "format": "json"}) + + assert response.data == expected diff --git a/changes/changelog.d/747.feature b/changes/changelog.d/747.feature new file mode 100644 index 000000000..a278f0a10 --- /dev/null +++ b/changes/changelog.d/747.feature @@ -0,0 +1 @@ +Support embedding full artist discographies (#747) diff --git a/front/src/EmbedFrame.vue b/front/src/EmbedFrame.vue index 50219204c..7dcc371ea 100644 --- a/front/src/EmbedFrame.vue +++ b/front/src/EmbedFrame.vue @@ -139,7 +139,7 @@ export default { data () { return { time, - supportedTypes: ['track', 'album'], + supportedTypes: ['track', 'album', 'artist'], baseUrl: '', error: null, type: null, @@ -158,6 +158,7 @@ export default { }, created () { let params = getURLParams() + this.baseUrl = params.b || '' this.type = params.type if (this.supportedTypes.indexOf(this.type) === -1) { this.error = 'invalid_type' @@ -229,7 +230,10 @@ export default { this.fetchTrack(id) } if (type === 'album') { - this.fetchTracks({album: id, playable: true}) + this.fetchTracks({album: id, playable: true, ordering: ",disc_number,position"}) + } + if (type === 'artist') { + this.fetchTracks({artist: id, playable: true, ordering: "-release_date,disc_number,position"}) } }, play (index) { diff --git a/front/src/components/audio/EmbedWizard.vue b/front/src/components/audio/EmbedWizard.vue index 0022b60df..ebb65b369 100644 --- a/front/src/components/audio/EmbedWizard.vue +++ b/front/src/components/audio/EmbedWizard.vue @@ -29,7 +29,11 @@
-

Preview

+

+ + Preview + +

diff --git a/front/src/components/library/Artist.vue b/front/src/components/library/Artist.vue index 084ef34c4..b23153d22 100644 --- a/front/src/components/library/Artist.vue +++ b/front/src/components/library/Artist.vue @@ -35,6 +35,30 @@ View on MusicBrainz +
@@ -72,7 +96,7 @@

User libraries

- + This artist is present in the following libraries: @@ -90,6 +114,8 @@ import RadioButton from "@/components/radios/Button" import PlayButton from "@/components/audio/PlayButton" import TrackTable from "@/components/audio/track/Table" import LibraryWidget from "@/components/federation/LibraryWidget" +import EmbedWizard from "@/components/audio/EmbedWizard" +import Modal from '@/components/semantic/Modal' export default { props: ["id"], @@ -98,7 +124,9 @@ export default { RadioButton, PlayButton, TrackTable, - LibraryWidget + LibraryWidget, + EmbedWizard, + Modal }, data() { return { @@ -108,7 +136,9 @@ export default { albums: null, totalTracks: 0, totalAlbums: 0, - tracks: [] + tracks: [], + libraries: [], + showEmbedModal: false } }, created() { @@ -185,6 +215,12 @@ export default { return album.cover })[0] }, + + publicLibraries () { + return this.libraries.filter(l => { + return l.privacy_level === 'everyone' + }) + }, headerStyle() { if (!this.cover || !this.cover.original) { return ""