kopia lustrzana https://dev.funkwhale.audio/funkwhale/funkwhale
Fix #747: Support embedding full artist discographies
rodzic
1d787904f4
commit
8ae00b0698
|
@ -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"])
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -184,6 +184,8 @@ class TrackViewSet(
|
|||
"title",
|
||||
"album__release_date",
|
||||
"size",
|
||||
"position",
|
||||
"disc_number",
|
||||
"artist__name",
|
||||
)
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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": '<iframe width="600" height="400" scrolling="no" frameborder="no" src="{}"></iframe>'.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
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Support embedding full artist discographies (#747)
|
|
@ -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) {
|
||||
|
|
|
@ -29,7 +29,11 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="preview">
|
||||
<h3><translate :translate-context="'Popup/Embed/Title/Noun'">Preview</translate></h3>
|
||||
<h3>
|
||||
<a :href="iframeSrc" target="_blank">
|
||||
<translate :translate-context="'Popup/Embed/Title/Noun'">Preview</translate>
|
||||
</a>
|
||||
</h3>
|
||||
<iframe :width="frameWidth" :height="height" scrolling="no" frameborder="no" :src="iframeSrc"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -35,6 +35,30 @@
|
|||
<i class="external icon"></i>
|
||||
<translate :translate-context="'Content/*/Button.Label/Verb'">View on MusicBrainz</translate>
|
||||
</a>
|
||||
<template v-if="publicLibraries.length > 0">
|
||||
<button
|
||||
@click="showEmbedModal = !showEmbedModal"
|
||||
class="ui button icon labeled">
|
||||
<i class="code icon"></i>
|
||||
<translate :translate-context="'Content/*/Button.Label/Verb'">Embed</translate>
|
||||
</button>
|
||||
<modal :show.sync="showEmbedModal">
|
||||
<div class="header">
|
||||
<translate :translate-context="'Popup/Artist/Title/Verb'">Embed this artist work on your website</translate>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="description">
|
||||
<embed-wizard type="artist" :id="artist.id" />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<div class="ui deny button">
|
||||
<translate :translate-context="'Popup/*/Button.Label/Verb'">Cancel</translate>
|
||||
</div>
|
||||
</div>
|
||||
</modal>
|
||||
</template>
|
||||
</div>
|
||||
</section>
|
||||
<div class="ui small text container" v-if="contentFilter">
|
||||
|
@ -72,7 +96,7 @@
|
|||
<h2>
|
||||
<translate :translate-context="'Content/Artist/Title'">User libraries</translate>
|
||||
</h2>
|
||||
<library-widget :url="'artists/' + id + '/libraries/'">
|
||||
<library-widget @loaded="libraries = $event" :url="'artists/' + id + '/libraries/'">
|
||||
<translate :translate-context="'Content/Artist/Paragraph'" slot="subtitle">This artist is present in the following libraries:</translate>
|
||||
</library-widget>
|
||||
</section>
|
||||
|
@ -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 ""
|
||||
|
|
Ładowanie…
Reference in New Issue