Merge branch '170-channel-meta-links' into 'develop'
See #170: added proper meta and support embed for channels See merge request funkwhale/funkwhale!1014
This commit is contained in:
commit
a04b0b706b
|
@ -1,5 +1,6 @@
|
||||||
from django import urls
|
from django import urls
|
||||||
|
|
||||||
|
from funkwhale_api.audio import spa_views as audio_spa_views
|
||||||
from funkwhale_api.music import spa_views
|
from funkwhale_api.music import spa_views
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,4 +21,9 @@ urlpatterns = [
|
||||||
spa_views.library_playlist,
|
spa_views.library_playlist,
|
||||||
name="library_playlist",
|
name="library_playlist",
|
||||||
),
|
),
|
||||||
|
urls.re_path(
|
||||||
|
r"^channels/(?P<uuid>[0-9a-f-]+)/?$",
|
||||||
|
audio_spa_views.channel_detail,
|
||||||
|
name="channel_detail",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
import urllib.parse
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.urls import reverse
|
||||||
|
|
||||||
|
from funkwhale_api.common import preferences
|
||||||
|
from funkwhale_api.common import utils
|
||||||
|
from funkwhale_api.music import spa_views
|
||||||
|
|
||||||
|
from . import models
|
||||||
|
|
||||||
|
|
||||||
|
def channel_detail(request, uuid):
|
||||||
|
queryset = models.Channel.objects.filter(uuid=uuid).select_related(
|
||||||
|
"artist__attachment_cover", "actor", "library"
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
obj = queryset.get()
|
||||||
|
except models.Channel.DoesNotExist:
|
||||||
|
return []
|
||||||
|
obj_url = utils.join_url(
|
||||||
|
settings.FUNKWHALE_URL,
|
||||||
|
utils.spa_reverse("channel_detail", kwargs={"uuid": obj.uuid}),
|
||||||
|
)
|
||||||
|
metas = [
|
||||||
|
{"tag": "meta", "property": "og:url", "content": obj_url},
|
||||||
|
{"tag": "meta", "property": "og:title", "content": obj.artist.name},
|
||||||
|
{"tag": "meta", "property": "og:type", "content": "profile"},
|
||||||
|
]
|
||||||
|
|
||||||
|
if obj.artist.attachment_cover:
|
||||||
|
metas.append(
|
||||||
|
{
|
||||||
|
"tag": "meta",
|
||||||
|
"property": "og:image",
|
||||||
|
"content": obj.artist.attachment_cover.download_url_medium_square_crop,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if preferences.get("federation__enabled"):
|
||||||
|
metas.append(
|
||||||
|
{
|
||||||
|
"tag": "link",
|
||||||
|
"rel": "alternate",
|
||||||
|
"type": "application/activity+json",
|
||||||
|
"href": obj.actor.fid,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if obj.library.uploads.all().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(obj_url))
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
# twitter player is also supported in various software
|
||||||
|
metas += spa_views.get_twitter_card_metas(type="channel", id=obj.uuid)
|
||||||
|
return metas
|
|
@ -614,6 +614,36 @@ class OembedSerializer(serializers.Serializer):
|
||||||
data["author_url"] = federation_utils.full_url(
|
data["author_url"] = federation_utils.full_url(
|
||||||
common_utils.spa_reverse("library_artist", kwargs={"pk": artist.pk})
|
common_utils.spa_reverse("library_artist", kwargs={"pk": artist.pk})
|
||||||
)
|
)
|
||||||
|
elif match.url_name == "channel_detail":
|
||||||
|
from funkwhale_api.audio.models import Channel
|
||||||
|
|
||||||
|
qs = Channel.objects.filter(uuid=match.kwargs["uuid"]).select_related(
|
||||||
|
"artist__attachment_cover"
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
channel = qs.get()
|
||||||
|
except models.Artist.DoesNotExist:
|
||||||
|
raise serializers.ValidationError(
|
||||||
|
"No channel matching id {}".format(match.kwargs["uuid"])
|
||||||
|
)
|
||||||
|
embed_type = "channel"
|
||||||
|
embed_id = channel.uuid
|
||||||
|
|
||||||
|
if channel.artist.attachment_cover:
|
||||||
|
data[
|
||||||
|
"thumbnail_url"
|
||||||
|
] = channel.artist.attachment_cover.download_url_medium_square_crop
|
||||||
|
data["thumbnail_width"] = 200
|
||||||
|
data["thumbnail_height"] = 200
|
||||||
|
data["title"] = channel.artist.name
|
||||||
|
data["description"] = channel.artist.name
|
||||||
|
data["author_name"] = channel.artist.name
|
||||||
|
data["height"] = 400
|
||||||
|
data["author_url"] = federation_utils.full_url(
|
||||||
|
common_utils.spa_reverse(
|
||||||
|
"channel_detail", kwargs={"uuid": channel.uuid}
|
||||||
|
)
|
||||||
|
)
|
||||||
elif match.url_name == "library_playlist":
|
elif match.url_name == "library_playlist":
|
||||||
qs = playlists_models.Playlist.objects.filter(
|
qs = playlists_models.Playlist.objects.filter(
|
||||||
pk=int(match.kwargs["pk"]), privacy_level="everyone"
|
pk=int(match.kwargs["pk"]), privacy_level="everyone"
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
import urllib.parse
|
||||||
|
|
||||||
|
from django.urls import reverse
|
||||||
|
|
||||||
|
from funkwhale_api.common import utils
|
||||||
|
from funkwhale_api.federation import utils as federation_utils
|
||||||
|
from funkwhale_api.music import serializers
|
||||||
|
|
||||||
|
|
||||||
|
def test_library_artist(spa_html, no_api_auth, client, factories, settings):
|
||||||
|
channel = factories["audio.Channel"]()
|
||||||
|
factories["music.Upload"](playable=True, library=channel.library)
|
||||||
|
url = "/channels/{}".format(channel.uuid)
|
||||||
|
|
||||||
|
response = client.get(url)
|
||||||
|
|
||||||
|
expected_metas = [
|
||||||
|
{
|
||||||
|
"tag": "meta",
|
||||||
|
"property": "og:url",
|
||||||
|
"content": utils.join_url(settings.FUNKWHALE_URL, url),
|
||||||
|
},
|
||||||
|
{"tag": "meta", "property": "og:title", "content": channel.artist.name},
|
||||||
|
{"tag": "meta", "property": "og:type", "content": "profile"},
|
||||||
|
{
|
||||||
|
"tag": "meta",
|
||||||
|
"property": "og:image",
|
||||||
|
"content": channel.artist.attachment_cover.download_url_medium_square_crop,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tag": "link",
|
||||||
|
"rel": "alternate",
|
||||||
|
"type": "application/activity+json",
|
||||||
|
"href": channel.actor.fid,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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("channel", id=channel.uuid),
|
||||||
|
},
|
||||||
|
{"tag": "meta", "property": "twitter:player:width", "content": "600"},
|
||||||
|
{"tag": "meta", "property": "twitter:player:height", "content": "400"},
|
||||||
|
]
|
||||||
|
|
||||||
|
metas = utils.parse_meta(response.content.decode())
|
||||||
|
|
||||||
|
# we only test our custom metas, not the default ones
|
||||||
|
assert metas[: len(expected_metas)] == expected_metas
|
||||||
|
|
||||||
|
|
||||||
|
def test_oembed_channel(factories, no_api_auth, api_client, settings):
|
||||||
|
settings.FUNKWHALE_URL = "http://test"
|
||||||
|
settings.FUNKWHALE_EMBED_URL = "http://embed"
|
||||||
|
channel = factories["audio.Channel"]()
|
||||||
|
artist = channel.artist
|
||||||
|
url = reverse("api:v1:oembed")
|
||||||
|
obj_url = "https://test.com/channels/{}".format(channel.uuid)
|
||||||
|
iframe_src = "http://embed?type=channel&id={}".format(channel.uuid)
|
||||||
|
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(
|
||||||
|
artist.attachment_cover.file.crop["200x200"].url
|
||||||
|
),
|
||||||
|
"thumbnail_height": 200,
|
||||||
|
"thumbnail_width": 200,
|
||||||
|
"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("channel_detail", kwargs={"uuid": channel.uuid})
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
response = api_client.get(url, {"url": obj_url, "format": "json"})
|
||||||
|
|
||||||
|
assert response.data == expected
|
|
@ -139,7 +139,7 @@ export default {
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
time,
|
time,
|
||||||
supportedTypes: ['track', 'album', 'artist', 'playlist'],
|
supportedTypes: ['track', 'album', 'artist', 'playlist', 'channel'],
|
||||||
baseUrl: '',
|
baseUrl: '',
|
||||||
error: null,
|
error: null,
|
||||||
type: null,
|
type: null,
|
||||||
|
@ -230,7 +230,10 @@ export default {
|
||||||
this.fetchTrack(id)
|
this.fetchTrack(id)
|
||||||
}
|
}
|
||||||
if (type === 'album') {
|
if (type === 'album') {
|
||||||
this.fetchTracks({album: id, playable: true, ordering: ",disc_number,position"})
|
this.fetchTracks({album: id, playable: true, ordering: "disc_number,position"})
|
||||||
|
}
|
||||||
|
if (type === 'channel') {
|
||||||
|
this.fetchTracks({channel: id, playable: true, include_channels: 'true', ordering: "-creation_date"})
|
||||||
}
|
}
|
||||||
if (type === 'artist') {
|
if (type === 'artist') {
|
||||||
this.fetchTracks({artist: id, playable: true, ordering: "-release_date,disc_number,position"})
|
this.fetchTracks({artist: id, playable: true, ordering: "-release_date,disc_number,position"})
|
||||||
|
|
Loading…
Reference in New Issue