Merge branch '170-friendica-compat' into 'develop'
See #170: friendica and AP compat See merge request funkwhale/funkwhale!1054
This commit is contained in:
commit
7cae1ae5db
|
@ -220,16 +220,11 @@ def get_default_context():
|
||||||
return [
|
return [
|
||||||
"https://www.w3.org/ns/activitystreams",
|
"https://www.w3.org/ns/activitystreams",
|
||||||
"https://w3id.org/security/v1",
|
"https://w3id.org/security/v1",
|
||||||
{"manuallyApprovesFollowers": "as:manuallyApprovesFollowers"},
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def get_default_context_fw():
|
|
||||||
return [
|
|
||||||
"https://www.w3.org/ns/activitystreams",
|
|
||||||
"https://w3id.org/security/v1",
|
|
||||||
{"manuallyApprovesFollowers": "as:manuallyApprovesFollowers"},
|
|
||||||
"https://funkwhale.audio/ns",
|
"https://funkwhale.audio/ns",
|
||||||
|
{
|
||||||
|
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||||
|
"Hashtag": "as:Hashtag",
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1583,12 +1583,12 @@ class ChannelUploadSerializer(serializers.Serializer):
|
||||||
"url": [
|
"url": [
|
||||||
{
|
{
|
||||||
"type": "Link",
|
"type": "Link",
|
||||||
"mimeType": upload.mimetype,
|
"mediaType": upload.mimetype,
|
||||||
"href": utils.full_url(upload.listen_url_no_download),
|
"href": utils.full_url(upload.listen_url_no_download),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "Link",
|
"type": "Link",
|
||||||
"mimeType": "text/html",
|
"mediaType": "text/html",
|
||||||
"href": utils.full_url(upload.track.get_absolute_url()),
|
"href": utils.full_url(upload.track.get_absolute_url()),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -61,6 +61,16 @@ class ActorViewSet(FederationMixin, mixins.RetrieveModelMixin, viewsets.GenericV
|
||||||
queryset = models.Actor.objects.local().select_related("user")
|
queryset = models.Actor.objects.local().select_related("user")
|
||||||
serializer_class = serializers.ActorSerializer
|
serializer_class = serializers.ActorSerializer
|
||||||
|
|
||||||
|
def retrieve(self, request, *args, **kwargs):
|
||||||
|
instance = self.get_object()
|
||||||
|
if utils.should_redirect_ap_to_html(request.headers.get("accept")):
|
||||||
|
if instance.get_channel():
|
||||||
|
return redirect_to_html(instance.channel.get_absolute_url())
|
||||||
|
return redirect_to_html(instance.get_absolute_url())
|
||||||
|
|
||||||
|
serializer = self.get_serializer(instance)
|
||||||
|
return response.Response(serializer.data)
|
||||||
|
|
||||||
@action(methods=["get", "post"], detail=True)
|
@action(methods=["get", "post"], detail=True)
|
||||||
def inbox(self, request, *args, **kwargs):
|
def inbox(self, request, *args, **kwargs):
|
||||||
inbox_actor = self.get_object()
|
inbox_actor = self.get_object()
|
||||||
|
@ -222,7 +232,6 @@ class MusicLibraryViewSet(
|
||||||
def retrieve(self, request, *args, **kwargs):
|
def retrieve(self, request, *args, **kwargs):
|
||||||
lb = self.get_object()
|
lb = self.get_object()
|
||||||
if utils.should_redirect_ap_to_html(request.headers.get("accept")):
|
if utils.should_redirect_ap_to_html(request.headers.get("accept")):
|
||||||
# XXX: implement this for actors, albums, tracks, artists
|
|
||||||
return redirect_to_html(lb.get_absolute_url())
|
return redirect_to_html(lb.get_absolute_url())
|
||||||
conf = {
|
conf = {
|
||||||
"id": lb.get_federation_id(),
|
"id": lb.get_federation_id(),
|
||||||
|
@ -308,6 +317,14 @@ class MusicUploadViewSet(
|
||||||
serializer_class = serializers.UploadSerializer
|
serializer_class = serializers.UploadSerializer
|
||||||
lookup_field = "uuid"
|
lookup_field = "uuid"
|
||||||
|
|
||||||
|
def retrieve(self, request, *args, **kwargs):
|
||||||
|
instance = self.get_object()
|
||||||
|
if utils.should_redirect_ap_to_html(request.headers.get("accept")):
|
||||||
|
return redirect_to_html(instance.track.get_absolute_url())
|
||||||
|
|
||||||
|
serializer = self.get_serializer(instance)
|
||||||
|
return response.Response(serializer.data)
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
queryset = super().get_queryset()
|
queryset = super().get_queryset()
|
||||||
actor = music_utils.get_actor_from_request(self.request)
|
actor = music_utils.get_actor_from_request(self.request)
|
||||||
|
@ -330,6 +347,14 @@ class MusicArtistViewSet(
|
||||||
serializer_class = serializers.ArtistSerializer
|
serializer_class = serializers.ArtistSerializer
|
||||||
lookup_field = "uuid"
|
lookup_field = "uuid"
|
||||||
|
|
||||||
|
def retrieve(self, request, *args, **kwargs):
|
||||||
|
instance = self.get_object()
|
||||||
|
if utils.should_redirect_ap_to_html(request.headers.get("accept")):
|
||||||
|
return redirect_to_html(instance.get_absolute_url())
|
||||||
|
|
||||||
|
serializer = self.get_serializer(instance)
|
||||||
|
return response.Response(serializer.data)
|
||||||
|
|
||||||
|
|
||||||
class MusicAlbumViewSet(
|
class MusicAlbumViewSet(
|
||||||
FederationMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet
|
FederationMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet
|
||||||
|
@ -342,6 +367,14 @@ class MusicAlbumViewSet(
|
||||||
serializer_class = serializers.AlbumSerializer
|
serializer_class = serializers.AlbumSerializer
|
||||||
lookup_field = "uuid"
|
lookup_field = "uuid"
|
||||||
|
|
||||||
|
def retrieve(self, request, *args, **kwargs):
|
||||||
|
instance = self.get_object()
|
||||||
|
if utils.should_redirect_ap_to_html(request.headers.get("accept")):
|
||||||
|
return redirect_to_html(instance.get_absolute_url())
|
||||||
|
|
||||||
|
serializer = self.get_serializer(instance)
|
||||||
|
return response.Response(serializer.data)
|
||||||
|
|
||||||
|
|
||||||
class MusicTrackViewSet(
|
class MusicTrackViewSet(
|
||||||
FederationMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet
|
FederationMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet
|
||||||
|
@ -360,3 +393,11 @@ class MusicTrackViewSet(
|
||||||
)
|
)
|
||||||
serializer_class = serializers.TrackSerializer
|
serializer_class = serializers.TrackSerializer
|
||||||
lookup_field = "uuid"
|
lookup_field = "uuid"
|
||||||
|
|
||||||
|
def retrieve(self, request, *args, **kwargs):
|
||||||
|
instance = self.get_object()
|
||||||
|
if utils.should_redirect_ap_to_html(request.headers.get("accept")):
|
||||||
|
return redirect_to_html(instance.get_absolute_url())
|
||||||
|
|
||||||
|
serializer = self.get_serializer(instance)
|
||||||
|
return response.Response(serializer.data)
|
||||||
|
|
|
@ -21,7 +21,7 @@ def test_actor_serializer_from_ap(db):
|
||||||
private, public = keys.get_key_pair()
|
private, public = keys.get_key_pair()
|
||||||
actor_url = "https://test.federation/actor"
|
actor_url = "https://test.federation/actor"
|
||||||
payload = {
|
payload = {
|
||||||
"@context": jsonld.get_default_context_fw(),
|
"@context": jsonld.get_default_context(),
|
||||||
"id": actor_url,
|
"id": actor_url,
|
||||||
"type": "Person",
|
"type": "Person",
|
||||||
"outbox": "https://test.com/outbox",
|
"outbox": "https://test.com/outbox",
|
||||||
|
@ -76,7 +76,7 @@ def test_actor_serializer_from_ap_no_icon_mediaType(db):
|
||||||
private, public = keys.get_key_pair()
|
private, public = keys.get_key_pair()
|
||||||
actor_url = "https://test.federation/actor"
|
actor_url = "https://test.federation/actor"
|
||||||
payload = {
|
payload = {
|
||||||
"@context": jsonld.get_default_context_fw(),
|
"@context": jsonld.get_default_context(),
|
||||||
"id": actor_url,
|
"id": actor_url,
|
||||||
"type": "Person",
|
"type": "Person",
|
||||||
"inbox": "https://test.com/inbox",
|
"inbox": "https://test.com/inbox",
|
||||||
|
@ -281,7 +281,7 @@ def test_accept_follow_serializer_save(factories):
|
||||||
follow = factories["federation.Follow"](approved=None)
|
follow = factories["federation.Follow"](approved=None)
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
"@context": jsonld.get_default_context_fw(),
|
"@context": jsonld.get_default_context(),
|
||||||
"id": follow.get_federation_id() + "/accept",
|
"id": follow.get_federation_id() + "/accept",
|
||||||
"type": "Accept",
|
"type": "Accept",
|
||||||
"actor": follow.target.fid,
|
"actor": follow.target.fid,
|
||||||
|
@ -301,7 +301,7 @@ def test_accept_follow_serializer_validates_on_context(factories):
|
||||||
follow = factories["federation.Follow"](approved=None)
|
follow = factories["federation.Follow"](approved=None)
|
||||||
impostor = factories["federation.Actor"]()
|
impostor = factories["federation.Actor"]()
|
||||||
data = {
|
data = {
|
||||||
"@context": jsonld.get_default_context_fw(),
|
"@context": jsonld.get_default_context(),
|
||||||
"id": follow.get_federation_id() + "/accept",
|
"id": follow.get_federation_id() + "/accept",
|
||||||
"type": "Accept",
|
"type": "Accept",
|
||||||
"actor": impostor.url,
|
"actor": impostor.url,
|
||||||
|
@ -337,7 +337,7 @@ def test_undo_follow_serializer_save(factories):
|
||||||
follow = factories["federation.Follow"](approved=True)
|
follow = factories["federation.Follow"](approved=True)
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
"@context": jsonld.get_default_context_fw(),
|
"@context": jsonld.get_default_context(),
|
||||||
"id": follow.get_federation_id() + "/undo",
|
"id": follow.get_federation_id() + "/undo",
|
||||||
"type": "Undo",
|
"type": "Undo",
|
||||||
"actor": follow.actor.fid,
|
"actor": follow.actor.fid,
|
||||||
|
@ -356,7 +356,7 @@ def test_undo_follow_serializer_validates_on_context(factories):
|
||||||
follow = factories["federation.Follow"](approved=True)
|
follow = factories["federation.Follow"](approved=True)
|
||||||
impostor = factories["federation.Actor"]()
|
impostor = factories["federation.Actor"]()
|
||||||
data = {
|
data = {
|
||||||
"@context": jsonld.get_default_context_fw(),
|
"@context": jsonld.get_default_context(),
|
||||||
"id": follow.get_federation_id() + "/undo",
|
"id": follow.get_federation_id() + "/undo",
|
||||||
"type": "Undo",
|
"type": "Undo",
|
||||||
"actor": impostor.url,
|
"actor": impostor.url,
|
||||||
|
@ -402,7 +402,7 @@ def test_paginated_collection_serializer(factories):
|
||||||
|
|
||||||
def test_paginated_collection_serializer_validation():
|
def test_paginated_collection_serializer_validation():
|
||||||
data = {
|
data = {
|
||||||
"@context": jsonld.get_default_context_fw(),
|
"@context": jsonld.get_default_context(),
|
||||||
"type": "Collection",
|
"type": "Collection",
|
||||||
"id": "https://test.federation/test",
|
"id": "https://test.federation/test",
|
||||||
"totalItems": 5,
|
"totalItems": 5,
|
||||||
|
@ -579,7 +579,7 @@ def test_music_library_serializer_from_private(factories, mocker):
|
||||||
"funkwhale_api.federation.utils.retrieve_ap_object", return_value=actor
|
"funkwhale_api.federation.utils.retrieve_ap_object", return_value=actor
|
||||||
)
|
)
|
||||||
data = {
|
data = {
|
||||||
"@context": jsonld.get_default_context_fw(),
|
"@context": jsonld.get_default_context(),
|
||||||
"audience": "",
|
"audience": "",
|
||||||
"name": "Hello",
|
"name": "Hello",
|
||||||
"summary": "World",
|
"summary": "World",
|
||||||
|
@ -1470,12 +1470,12 @@ def test_channel_upload_serializer(factories):
|
||||||
"url": [
|
"url": [
|
||||||
{
|
{
|
||||||
"type": "Link",
|
"type": "Link",
|
||||||
"mimeType": upload.mimetype,
|
"mediaType": upload.mimetype,
|
||||||
"href": utils.full_url(upload.listen_url_no_download),
|
"href": utils.full_url(upload.listen_url_no_download),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "Link",
|
"type": "Link",
|
||||||
"mimeType": "text/html",
|
"mediaType": "text/html",
|
||||||
"href": utils.full_url(upload.track.get_absolute_url()),
|
"href": utils.full_url(upload.track.get_absolute_url()),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -389,7 +389,7 @@ def test_fetch_success(factories, r_mock, mocker):
|
||||||
tasks.fetch(fetch_id=fetch.pk)
|
tasks.fetch(fetch_id=fetch.pk)
|
||||||
|
|
||||||
fetch.refresh_from_db()
|
fetch.refresh_from_db()
|
||||||
payload["@context"].append("https://funkwhale.audio/ns")
|
|
||||||
assert fetch.status == "finished"
|
assert fetch.status == "finished"
|
||||||
assert init.call_count == 1
|
assert init.call_count == 1
|
||||||
assert init.call_args[0][1] == artist
|
assert init.call_args[0][1] == artist
|
||||||
|
@ -421,7 +421,7 @@ def test_fetch_webfinger(factories, r_mock, mocker):
|
||||||
tasks.fetch(fetch_id=fetch.pk)
|
tasks.fetch(fetch_id=fetch.pk)
|
||||||
|
|
||||||
fetch.refresh_from_db()
|
fetch.refresh_from_db()
|
||||||
payload["@context"].append("https://funkwhale.audio/ns")
|
|
||||||
assert fetch.status == "finished"
|
assert fetch.status == "finished"
|
||||||
assert fetch.object == actor
|
assert fetch.object == actor
|
||||||
assert init.call_count == 1
|
assert init.call_count == 1
|
||||||
|
@ -451,7 +451,7 @@ def test_fetch_rel_alternate(factories, r_mock, mocker):
|
||||||
tasks.fetch(fetch_id=fetch.pk)
|
tasks.fetch(fetch_id=fetch.pk)
|
||||||
|
|
||||||
fetch.refresh_from_db()
|
fetch.refresh_from_db()
|
||||||
ap_payload["@context"].append("https://funkwhale.audio/ns")
|
|
||||||
assert fetch.status == "finished"
|
assert fetch.status == "finished"
|
||||||
assert fetch.object == actor
|
assert fetch.object == actor
|
||||||
assert init.call_count == 1
|
assert init.call_count == 1
|
||||||
|
@ -482,7 +482,7 @@ def test_fetch_url(factory_name, serializer_class, factories, r_mock, mocker):
|
||||||
tasks.fetch(fetch_id=fetch.pk)
|
tasks.fetch(fetch_id=fetch.pk)
|
||||||
|
|
||||||
fetch.refresh_from_db()
|
fetch.refresh_from_db()
|
||||||
payload["@context"].append("https://funkwhale.audio/ns")
|
|
||||||
assert fetch.status == "finished"
|
assert fetch.status == "finished"
|
||||||
assert fetch.object == obj
|
assert fetch.object == obj
|
||||||
assert init.call_count == 1
|
assert init.call_count == 1
|
||||||
|
|
|
@ -387,3 +387,103 @@ def test_music_library_retrieve_redirects_to_html_if_header_set(
|
||||||
)
|
)
|
||||||
assert response.status_code == 302
|
assert response.status_code == 302
|
||||||
assert response["Location"] == expected_url
|
assert response["Location"] == expected_url
|
||||||
|
|
||||||
|
|
||||||
|
def test_actor_retrieve_redirects_to_html_if_header_set(
|
||||||
|
factories, api_client, settings
|
||||||
|
):
|
||||||
|
actor = factories["federation.Actor"](local=True)
|
||||||
|
|
||||||
|
url = reverse(
|
||||||
|
"federation:actors-detail",
|
||||||
|
kwargs={"preferred_username": actor.preferred_username},
|
||||||
|
)
|
||||||
|
response = api_client.get(url, HTTP_ACCEPT="text/html")
|
||||||
|
expected_url = utils.join_url(
|
||||||
|
settings.FUNKWHALE_URL,
|
||||||
|
utils.spa_reverse(
|
||||||
|
"actor_detail", kwargs={"username": actor.preferred_username}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
assert response.status_code == 302
|
||||||
|
assert response["Location"] == expected_url
|
||||||
|
|
||||||
|
|
||||||
|
def test_channel_actor_retrieve_redirects_to_html_if_header_set(
|
||||||
|
factories, api_client, settings
|
||||||
|
):
|
||||||
|
channel = factories["audio.Channel"](local=True)
|
||||||
|
|
||||||
|
url = reverse(
|
||||||
|
"federation:actors-detail",
|
||||||
|
kwargs={"preferred_username": channel.actor.preferred_username},
|
||||||
|
)
|
||||||
|
response = api_client.get(url, HTTP_ACCEPT="text/html")
|
||||||
|
expected_url = utils.join_url(
|
||||||
|
settings.FUNKWHALE_URL,
|
||||||
|
utils.spa_reverse(
|
||||||
|
"channel_detail", kwargs={"username": channel.actor.preferred_username}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
assert response.status_code == 302
|
||||||
|
assert response["Location"] == expected_url
|
||||||
|
|
||||||
|
|
||||||
|
def test_upload_retrieve_redirects_to_html_if_header_set(
|
||||||
|
factories, api_client, settings
|
||||||
|
):
|
||||||
|
upload = factories["music.Upload"](library__local=True, playable=True)
|
||||||
|
|
||||||
|
url = reverse("federation:music:uploads-detail", kwargs={"uuid": upload.uuid},)
|
||||||
|
response = api_client.get(url, HTTP_ACCEPT="text/html")
|
||||||
|
expected_url = utils.join_url(
|
||||||
|
settings.FUNKWHALE_URL,
|
||||||
|
utils.spa_reverse("library_track", kwargs={"pk": upload.track.pk}),
|
||||||
|
)
|
||||||
|
assert response.status_code == 302
|
||||||
|
assert response["Location"] == expected_url
|
||||||
|
|
||||||
|
|
||||||
|
def test_track_retrieve_redirects_to_html_if_header_set(
|
||||||
|
factories, api_client, settings
|
||||||
|
):
|
||||||
|
track = factories["music.Track"](local=True)
|
||||||
|
|
||||||
|
url = reverse("federation:music:tracks-detail", kwargs={"uuid": track.uuid},)
|
||||||
|
response = api_client.get(url, HTTP_ACCEPT="text/html")
|
||||||
|
expected_url = utils.join_url(
|
||||||
|
settings.FUNKWHALE_URL,
|
||||||
|
utils.spa_reverse("library_track", kwargs={"pk": track.pk}),
|
||||||
|
)
|
||||||
|
assert response.status_code == 302
|
||||||
|
assert response["Location"] == expected_url
|
||||||
|
|
||||||
|
|
||||||
|
def test_album_retrieve_redirects_to_html_if_header_set(
|
||||||
|
factories, api_client, settings
|
||||||
|
):
|
||||||
|
album = factories["music.Album"](local=True)
|
||||||
|
|
||||||
|
url = reverse("federation:music:albums-detail", kwargs={"uuid": album.uuid},)
|
||||||
|
response = api_client.get(url, HTTP_ACCEPT="text/html")
|
||||||
|
expected_url = utils.join_url(
|
||||||
|
settings.FUNKWHALE_URL,
|
||||||
|
utils.spa_reverse("library_album", kwargs={"pk": album.pk}),
|
||||||
|
)
|
||||||
|
assert response.status_code == 302
|
||||||
|
assert response["Location"] == expected_url
|
||||||
|
|
||||||
|
|
||||||
|
def test_artist_retrieve_redirects_to_html_if_header_set(
|
||||||
|
factories, api_client, settings
|
||||||
|
):
|
||||||
|
artist = factories["music.Artist"](local=True)
|
||||||
|
|
||||||
|
url = reverse("federation:music:artists-detail", kwargs={"uuid": artist.uuid},)
|
||||||
|
response = api_client.get(url, HTTP_ACCEPT="text/html")
|
||||||
|
expected_url = utils.join_url(
|
||||||
|
settings.FUNKWHALE_URL,
|
||||||
|
utils.spa_reverse("library_artist", kwargs={"pk": artist.pk}),
|
||||||
|
)
|
||||||
|
assert response.status_code == 302
|
||||||
|
assert response["Location"] == expected_url
|
||||||
|
|
|
@ -268,6 +268,15 @@ export default {
|
||||||
this.isLoading = true
|
this.isLoading = true
|
||||||
let channelPromise = axios.get(`channels/${this.id}`).then(response => {
|
let channelPromise = axios.get(`channels/${this.id}`).then(response => {
|
||||||
self.object = response.data
|
self.object = response.data
|
||||||
|
if (self.id == response.data.uuid && response.data.actor) {
|
||||||
|
// replace with the pretty channel url if possible
|
||||||
|
let actor = response.data.actor
|
||||||
|
if (actor.is_local) {
|
||||||
|
self.$router.replace({name: 'channels.detail', params: {id: actor.preferred_username}})
|
||||||
|
} else {
|
||||||
|
self.$router.replace({name: 'channels.detail', params: {id: actor.full_username}})
|
||||||
|
}
|
||||||
|
}
|
||||||
let tracksPromise = axios.get("tracks", {params: {channel: response.data.uuid, page_size: 1, playable: true, include_channels: true}}).then(response => {
|
let tracksPromise = axios.get("tracks", {params: {channel: response.data.uuid, page_size: 1, playable: true, include_channels: true}}).then(response => {
|
||||||
self.totalTracks = response.data.count
|
self.totalTracks = response.data.count
|
||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
|
|
Loading…
Reference in New Issue