diff --git a/api/config/settings/common.py b/api/config/settings/common.py
index 60446e370..4759d7aab 100644
--- a/api/config/settings/common.py
+++ b/api/config/settings/common.py
@@ -125,7 +125,6 @@ LOCAL_APPS = (
"funkwhale_api.radios",
"funkwhale_api.history",
"funkwhale_api.playlists",
- "funkwhale_api.providers.audiofile",
"funkwhale_api.providers.acoustid",
"funkwhale_api.subsonic",
)
diff --git a/api/funkwhale_api/federation/actors.py b/api/funkwhale_api/federation/actors.py
index a91e9a5e9..f1b94809d 100644
--- a/api/funkwhale_api/federation/actors.py
+++ b/api/funkwhale_api/federation/actors.py
@@ -1,26 +1,16 @@
import datetime
import logging
-import xml
from django.conf import settings
-from django.urls import reverse
from django.utils import timezone
-from rest_framework.exceptions import PermissionDenied
from funkwhale_api.common import preferences, session
-from . import activity, keys, models, serializers, signing, utils
+from . import models, serializers
logger = logging.getLogger(__name__)
-def remove_tags(text):
- logger.debug("Removing tags from %s", text)
- return "".join(
- xml.etree.ElementTree.fromstring("
{}
".format(text)).itertext()
- )
-
-
def get_actor_data(actor_url):
response = session.get_session().get(
actor_url,
@@ -51,247 +41,3 @@ def get_actor(fid):
serializer.is_valid(raise_exception=True)
return serializer.save(last_fetch_date=timezone.now())
-
-
-class SystemActor(object):
- additional_attributes = {}
- manually_approves_followers = False
-
- def get_request_auth(self):
- actor = self.get_actor_instance()
- return signing.get_auth(actor.private_key, actor.private_key_id)
-
- def serialize(self):
- actor = self.get_actor_instance()
- serializer = serializers.ActorSerializer(actor)
- return serializer.data
-
- def get_actor_instance(self):
- try:
- return models.Actor.objects.get(fid=self.get_actor_id())
- except models.Actor.DoesNotExist:
- pass
- private, public = keys.get_key_pair()
- args = self.get_instance_argument(
- self.id, name=self.name, summary=self.summary, **self.additional_attributes
- )
- args["private_key"] = private.decode("utf-8")
- args["public_key"] = public.decode("utf-8")
- return models.Actor.objects.create(**args)
-
- def get_actor_id(self):
- return utils.full_url(
- reverse("federation:instance-actors-detail", kwargs={"actor": self.id})
- )
-
- def get_instance_argument(self, id, name, summary, **kwargs):
- p = {
- "preferred_username": id,
- "domain": settings.FEDERATION_HOSTNAME,
- "type": "Person",
- "name": name.format(host=settings.FEDERATION_HOSTNAME),
- "manually_approves_followers": True,
- "fid": self.get_actor_id(),
- "shared_inbox_url": utils.full_url(
- reverse("federation:instance-actors-inbox", kwargs={"actor": id})
- ),
- "inbox_url": utils.full_url(
- reverse("federation:instance-actors-inbox", kwargs={"actor": id})
- ),
- "outbox_url": utils.full_url(
- reverse("federation:instance-actors-outbox", kwargs={"actor": id})
- ),
- "summary": summary.format(host=settings.FEDERATION_HOSTNAME),
- }
- p.update(kwargs)
- return p
-
- def get_inbox(self, data, actor=None):
- raise NotImplementedError
-
- def post_inbox(self, data, actor=None):
- return self.handle(data, actor=actor)
-
- def get_outbox(self, data, actor=None):
- raise NotImplementedError
-
- def post_outbox(self, data, actor=None):
- raise NotImplementedError
-
- def handle(self, data, actor=None):
- """
- Main entrypoint for handling activities posted to the
- actor's inbox
- """
- logger.info("Received activity on %s inbox", self.id)
-
- if actor is None:
- raise PermissionDenied("Actor not authenticated")
-
- serializer = serializers.ActivitySerializer(data=data, context={"actor": actor})
- serializer.is_valid(raise_exception=True)
-
- ac = serializer.data
- try:
- handler = getattr(self, "handle_{}".format(ac["type"].lower()))
- except (KeyError, AttributeError):
- logger.debug("No handler for activity %s", ac["type"])
- return
-
- return handler(data, actor)
-
- def handle_follow(self, ac, sender):
- serializer = serializers.FollowSerializer(
- data=ac, context={"follow_actor": sender}
- )
- if not serializer.is_valid():
- return logger.info("Invalid follow payload")
- approved = True if not self.manually_approves_followers else None
- follow = serializer.save(approved=approved)
- if follow.approved:
- return activity.accept_follow(follow)
-
- def handle_accept(self, ac, sender):
- system_actor = self.get_actor_instance()
- serializer = serializers.AcceptFollowSerializer(
- data=ac, context={"follow_target": sender, "follow_actor": system_actor}
- )
- if not serializer.is_valid(raise_exception=True):
- return logger.info("Received invalid payload")
-
- return serializer.save()
-
- def handle_undo_follow(self, ac, sender):
- system_actor = self.get_actor_instance()
- serializer = serializers.UndoFollowSerializer(
- data=ac, context={"actor": sender, "target": system_actor}
- )
- if not serializer.is_valid():
- return logger.info("Received invalid payload")
- serializer.save()
-
- def handle_undo(self, ac, sender):
- if ac["object"]["type"] != "Follow":
- return
-
- if ac["object"]["actor"] != sender.fid:
- # not the same actor, permission issue
- return
-
- self.handle_undo_follow(ac, sender)
-
-
-class TestActor(SystemActor):
- id = "test"
- name = "{host}'s test account"
- summary = (
- "Bot account to test federation with {host}. "
- "Send me /ping and I'll answer you."
- )
- additional_attributes = {"manually_approves_followers": False}
- manually_approves_followers = False
-
- def get_outbox(self, data, actor=None):
- return {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://w3id.org/security/v1",
- {},
- ],
- "id": utils.full_url(
- reverse("federation:instance-actors-outbox", kwargs={"actor": self.id})
- ),
- "type": "OrderedCollection",
- "totalItems": 0,
- "orderedItems": [],
- }
-
- def parse_command(self, message):
- """
- Remove any links or fancy markup to extract /command from
- a note message.
- """
- raw = remove_tags(message)
- try:
- return raw.split("/")[1]
- except IndexError:
- return
-
- def handle_create(self, ac, sender):
- if ac["object"]["type"] != "Note":
- return
-
- # we received a toot \o/
- command = self.parse_command(ac["object"]["content"])
- logger.debug("Parsed command: %s", command)
- if command != "ping":
- return
-
- now = timezone.now()
- test_actor = self.get_actor_instance()
- reply_url = "https://{}/activities/note/{}".format(
- settings.FEDERATION_HOSTNAME, now.timestamp()
- )
- reply_activity = {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://w3id.org/security/v1",
- {},
- ],
- "type": "Create",
- "actor": test_actor.fid,
- "id": "{}/activity".format(reply_url),
- "published": now.isoformat(),
- "to": ac["actor"],
- "cc": [],
- "object": {
- "type": "Note",
- "content": "Pong!",
- "summary": None,
- "published": now.isoformat(),
- "id": reply_url,
- "inReplyTo": ac["object"]["id"],
- "sensitive": False,
- "url": reply_url,
- "to": [ac["actor"]],
- "attributedTo": test_actor.fid,
- "cc": [],
- "attachment": [],
- "tag": [
- {
- "type": "Mention",
- "href": ac["actor"],
- "name": sender.full_username,
- }
- ],
- },
- }
- activity.deliver(reply_activity, to=[ac["actor"]], on_behalf_of=test_actor)
-
- def handle_follow(self, ac, sender):
- super().handle_follow(ac, sender)
- # also, we follow back
- test_actor = self.get_actor_instance()
- follow_back = models.Follow.objects.get_or_create(
- actor=test_actor, target=sender, approved=None
- )[0]
- activity.deliver(
- serializers.FollowSerializer(follow_back).data,
- to=[follow_back.target.fid],
- on_behalf_of=follow_back.actor,
- )
-
- def handle_undo_follow(self, ac, sender):
- super().handle_undo_follow(ac, sender)
- actor = self.get_actor_instance()
- # we also unfollow the sender, if possible
- try:
- follow = models.Follow.objects.get(target=sender, actor=actor)
- except models.Follow.DoesNotExist:
- return
- undo = serializers.UndoFollowSerializer(follow).data
- follow.delete()
- activity.deliver(undo, to=[sender.fid], on_behalf_of=actor)
-
-
-SYSTEM_ACTORS = {"test": TestActor()}
diff --git a/api/funkwhale_api/federation/library.py b/api/funkwhale_api/federation/library.py
index 467220799..e7f8373fa 100644
--- a/api/funkwhale_api/federation/library.py
+++ b/api/funkwhale_api/federation/library.py
@@ -1,74 +1,9 @@
-import json
-
import requests
from django.conf import settings
from funkwhale_api.common import session
-from . import actors, models, serializers, signing, webfinger
-
-
-def scan_from_account_name(account_name):
- """
- Given an account name such as library@test.library, will:
-
- 1. Perform the webfinger lookup
- 2. Perform the actor lookup
- 3. Perform the library's collection lookup
-
- and return corresponding data in a dictionary.
- """
- data = {}
- try:
- username, domain = webfinger.clean_acct(account_name, ensure_local=False)
- except serializers.ValidationError:
- return {"webfinger": {"errors": ["Invalid account string"]}}
- system_library = actors.SYSTEM_ACTORS["library"].get_actor_instance()
- data["local"] = {"following": False, "awaiting_approval": False}
- try:
- follow = models.Follow.objects.get(
- target__preferred_username=username,
- target__domain=username,
- actor=system_library,
- )
- data["local"]["awaiting_approval"] = not bool(follow.approved)
- data["local"]["following"] = True
- except models.Follow.DoesNotExist:
- pass
-
- try:
- data["webfinger"] = webfinger.get_resource("acct:{}".format(account_name))
- except requests.ConnectionError:
- return {"webfinger": {"errors": ["This webfinger resource is not reachable"]}}
- except requests.HTTPError as e:
- return {
- "webfinger": {
- "errors": [
- "Error {} during webfinger request".format(e.response.status_code)
- ]
- }
- }
- except json.JSONDecodeError as e:
- return {"webfinger": {"errors": ["Could not process webfinger response"]}}
-
- try:
- data["actor"] = actors.get_actor_data(data["webfinger"]["actor_url"])
- except requests.ConnectionError:
- data["actor"] = {"errors": ["This actor is not reachable"]}
- return data
- except requests.HTTPError as e:
- data["actor"] = {
- "errors": ["Error {} during actor request".format(e.response.status_code)]
- }
- return data
-
- serializer = serializers.LibraryActorSerializer(data=data["actor"])
- if not serializer.is_valid():
- data["actor"] = {"errors": ["Invalid ActivityPub actor"]}
- return data
- data["library"] = get_library_data(serializer.validated_data["library_url"])
-
- return data
+from . import serializers, signing
def get_library_data(library_url, actor):
diff --git a/api/funkwhale_api/federation/urls.py b/api/funkwhale_api/federation/urls.py
index cfec4a237..f8347d1eb 100644
--- a/api/funkwhale_api/federation/urls.py
+++ b/api/funkwhale_api/federation/urls.py
@@ -5,9 +5,7 @@ from . import views
router = routers.SimpleRouter(trailing_slash=False)
music_router = routers.SimpleRouter(trailing_slash=False)
-router.register(
- r"federation/instance/actors", views.InstanceActorViewSet, "instance-actors"
-)
+
router.register(r"federation/shared", views.SharedViewSet, "shared")
router.register(r"federation/actors", views.ActorViewSet, "actors")
router.register(r".well-known", views.WellKnownViewSet, "well-known")
diff --git a/api/funkwhale_api/federation/views.py b/api/funkwhale_api/federation/views.py
index 510c672fd..a12d5e5b5 100644
--- a/api/funkwhale_api/federation/views.py
+++ b/api/funkwhale_api/federation/views.py
@@ -1,6 +1,6 @@
from django import forms
from django.core import paginator
-from django.http import HttpResponse, Http404
+from django.http import HttpResponse
from django.urls import reverse
from rest_framework import exceptions, mixins, response, viewsets
from rest_framework.decorators import detail_route, list_route
@@ -8,16 +8,7 @@ from rest_framework.decorators import detail_route, list_route
from funkwhale_api.common import preferences
from funkwhale_api.music import models as music_models
-from . import (
- activity,
- actors,
- authentication,
- models,
- renderers,
- serializers,
- utils,
- webfinger,
-)
+from . import activity, authentication, models, renderers, serializers, utils, webfinger
class FederationMixin(object):
@@ -78,47 +69,6 @@ class ActorViewSet(FederationMixin, mixins.RetrieveModelMixin, viewsets.GenericV
return response.Response({})
-class InstanceActorViewSet(FederationMixin, viewsets.GenericViewSet):
- lookup_field = "actor"
- lookup_value_regex = "[a-z]*"
- authentication_classes = [authentication.SignatureAuthentication]
- permission_classes = []
- renderer_classes = [renderers.ActivityPubRenderer]
-
- def get_object(self):
- try:
- return actors.SYSTEM_ACTORS[self.kwargs["actor"]]
- except KeyError:
- raise Http404
-
- def retrieve(self, request, *args, **kwargs):
- system_actor = self.get_object()
- actor = system_actor.get_actor_instance()
- data = actor.system_conf.serialize()
- return response.Response(data, status=200)
-
- @detail_route(methods=["get", "post"])
- def inbox(self, request, *args, **kwargs):
- system_actor = self.get_object()
- handler = getattr(system_actor, "{}_inbox".format(request.method.lower()))
-
- try:
- handler(request.data, actor=request.actor)
- except NotImplementedError:
- return response.Response(status=405)
- return response.Response({}, status=200)
-
- @detail_route(methods=["get", "post"])
- def outbox(self, request, *args, **kwargs):
- system_actor = self.get_object()
- handler = getattr(system_actor, "{}_outbox".format(request.method.lower()))
- try:
- handler(request.data, actor=request.actor)
- except NotImplementedError:
- return response.Response(status=405)
- return response.Response({}, status=200)
-
-
class WellKnownViewSet(viewsets.GenericViewSet):
authentication_classes = []
permission_classes = []
@@ -160,13 +110,10 @@ class WellKnownViewSet(viewsets.GenericViewSet):
def handler_acct(self, clean_result):
username, hostname = clean_result
- if username in actors.SYSTEM_ACTORS:
- actor = actors.SYSTEM_ACTORS[username].get_actor_instance()
- else:
- try:
- actor = models.Actor.objects.local().get(preferred_username=username)
- except models.Actor.DoesNotExist:
- raise forms.ValidationError("Invalid username")
+ try:
+ actor = models.Actor.objects.local().get(preferred_username=username)
+ except models.Actor.DoesNotExist:
+ raise forms.ValidationError("Invalid username")
return serializers.ActorWebfingerSerializer(actor).data
diff --git a/api/funkwhale_api/providers/audiofile/management/commands/import_files.py b/api/funkwhale_api/music/management/commands/import_files.py
similarity index 100%
rename from api/funkwhale_api/providers/audiofile/management/commands/import_files.py
rename to api/funkwhale_api/music/management/commands/import_files.py
diff --git a/api/funkwhale_api/music/tasks.py b/api/funkwhale_api/music/tasks.py
index b78cabaae..d96471b96 100644
--- a/api/funkwhale_api/music/tasks.py
+++ b/api/funkwhale_api/music/tasks.py
@@ -11,10 +11,8 @@ from musicbrainzngs import ResponseError
from requests.exceptions import RequestException
from funkwhale_api.common import channels
-from funkwhale_api.common import preferences
-from funkwhale_api.federation import activity, actors, routes
+from funkwhale_api.federation import routes
from funkwhale_api.federation import library as lb
-from funkwhale_api.federation import library as federation_serializers
from funkwhale_api.taskapp import celery
from . import lyrics as lyrics_utils
@@ -78,45 +76,6 @@ def fetch_content(lyrics):
lyrics.save(update_fields=["content"])
-@celery.app.task(name="music.import_batch_notify_followers")
-@celery.require_instance(
- models.ImportBatch.objects.filter(status="finished"), "import_batch"
-)
-def import_batch_notify_followers(import_batch):
- if not preferences.get("federation__enabled"):
- return
-
- if import_batch.source == "federation":
- return
-
- library_actor = actors.SYSTEM_ACTORS["library"].get_actor_instance()
- followers = library_actor.get_approved_followers()
- jobs = import_batch.jobs.filter(
- status="finished", library_track__isnull=True, upload__isnull=False
- ).select_related("upload__track__artist", "upload__track__album__artist")
- uploads = [job.upload for job in jobs]
- collection = federation_serializers.CollectionSerializer(
- {
- "actor": library_actor,
- "id": import_batch.get_federation_id(),
- "items": uploads,
- "item_serializer": federation_serializers.AudioSerializer,
- }
- ).data
- for f in followers:
- create = federation_serializers.ActivitySerializer(
- {
- "type": "Create",
- "id": collection["id"],
- "object": collection,
- "actor": library_actor.fid,
- "to": [f.url],
- }
- ).data
-
- activity.deliver(create, on_behalf_of=library_actor, to=[f.url])
-
-
@celery.app.task(name="music.start_library_scan")
@celery.require_instance(
models.LibraryScan.objects.select_related().filter(status="pending"), "library_scan"
diff --git a/api/funkwhale_api/providers/audiofile/__init__.py b/api/funkwhale_api/providers/audiofile/__init__.py
deleted file mode 100644
index 18e2c469a..000000000
--- a/api/funkwhale_api/providers/audiofile/__init__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-"""
-This module is responsible from importing existing audiofiles from the
-filesystem into funkwhale.
-"""
diff --git a/api/funkwhale_api/providers/audiofile/management/__init__.py b/api/funkwhale_api/providers/audiofile/management/__init__.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/api/funkwhale_api/providers/audiofile/management/commands/__init__.py b/api/funkwhale_api/providers/audiofile/management/commands/__init__.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/api/funkwhale_api/providers/audiofile/tasks.py b/api/funkwhale_api/providers/audiofile/tasks.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/api/tests/federation/test_actors.py b/api/tests/federation/test_actors.py
index 71c1b047b..a416cd78f 100644
--- a/api/tests/federation/test_actors.py
+++ b/api/tests/federation/test_actors.py
@@ -1,8 +1,4 @@
-import pytest
-from django.urls import reverse
-from rest_framework import exceptions
-
-from funkwhale_api.federation import actors, models, serializers, utils
+from funkwhale_api.federation import actors, serializers
def test_actor_fetching(r_mock):
@@ -50,120 +46,3 @@ def test_get_actor_refresh(factories, preferences, mocker):
assert new_actor == actor
assert new_actor.last_fetch_date > actor.last_fetch_date
assert new_actor.preferred_username == "New me"
-
-
-def test_get_test(db, mocker, settings):
- mocker.patch(
- "funkwhale_api.federation.keys.get_key_pair",
- return_value=(b"private", b"public"),
- )
- expected = {
- "preferred_username": "test",
- "domain": settings.FEDERATION_HOSTNAME,
- "type": "Person",
- "name": "{}'s test account".format(settings.FEDERATION_HOSTNAME),
- "manually_approves_followers": False,
- "public_key": "public",
- "fid": utils.full_url(
- reverse("federation:instance-actors-detail", kwargs={"actor": "test"})
- ),
- "shared_inbox_url": utils.full_url(
- reverse("federation:instance-actors-inbox", kwargs={"actor": "test"})
- ),
- "inbox_url": utils.full_url(
- reverse("federation:instance-actors-inbox", kwargs={"actor": "test"})
- ),
- "outbox_url": utils.full_url(
- reverse("federation:instance-actors-outbox", kwargs={"actor": "test"})
- ),
- "summary": "Bot account to test federation with {}. Send me /ping and I'll answer you.".format(
- settings.FEDERATION_HOSTNAME
- ),
- }
- actor = actors.SYSTEM_ACTORS["test"].get_actor_instance()
- for key, value in expected.items():
- assert getattr(actor, key) == value
-
-
-def test_test_get_outbox():
- expected = {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://w3id.org/security/v1",
- {},
- ],
- "id": utils.full_url(
- reverse("federation:instance-actors-outbox", kwargs={"actor": "test"})
- ),
- "type": "OrderedCollection",
- "totalItems": 0,
- "orderedItems": [],
- }
-
- data = actors.SYSTEM_ACTORS["test"].get_outbox({}, actor=None)
-
- assert data == expected
-
-
-def test_test_post_inbox_requires_authenticated_actor():
- with pytest.raises(exceptions.PermissionDenied):
- actors.SYSTEM_ACTORS["test"].post_inbox({}, actor=None)
-
-
-def test_test_post_outbox_validates_actor(nodb_factories):
- actor = nodb_factories["federation.Actor"]()
- data = {"actor": "noop"}
- with pytest.raises(exceptions.ValidationError) as exc_info:
- actors.SYSTEM_ACTORS["test"].post_inbox(data, actor=actor)
- msg = "The actor making the request do not match"
- assert msg in exc_info.value
-
-
-def test_getting_actor_instance_persists_in_db(db):
- test = actors.SYSTEM_ACTORS["test"].get_actor_instance()
- from_db = models.Actor.objects.get(fid=test.fid)
-
- for f in test._meta.fields:
- assert getattr(from_db, f.name) == getattr(test, f.name)
-
-
-@pytest.mark.parametrize(
- "username,domain,expected",
- [("test", "wrongdomain.com", False), ("notsystem", "", False), ("test", "", True)],
-)
-def test_actor_is_system(username, domain, expected, nodb_factories, settings):
- if not domain:
- domain = settings.FEDERATION_HOSTNAME
-
- actor = nodb_factories["federation.Actor"](
- preferred_username=username, domain=domain
- )
- assert actor.is_system is expected
-
-
-@pytest.mark.parametrize(
- "username,domain,expected",
- [
- ("test", "wrongdomain.com", None),
- ("notsystem", "", None),
- ("test", "", actors.SYSTEM_ACTORS["test"]),
- ],
-)
-def test_actor_system_conf(username, domain, expected, nodb_factories, settings):
- if not domain:
- domain = settings.FEDERATION_HOSTNAME
- actor = nodb_factories["federation.Actor"](
- preferred_username=username, domain=domain
- )
- assert actor.system_conf == expected
-
-
-@pytest.mark.skip("Refactoring in progress")
-def test_system_actor_handle(mocker, nodb_factories):
- handler = mocker.patch("funkwhale_api.federation.actors.TestActor.handle_create")
- actor = nodb_factories["federation.Actor"]()
- activity = nodb_factories["federation.Activity"](type="Create", actor=actor)
- serializer = serializers.ActivitySerializer(data=activity)
- assert serializer.is_valid()
- actors.SYSTEM_ACTORS["test"].handle(activity, actor)
- handler.assert_called_once_with(activity, actor)
diff --git a/api/tests/federation/test_views.py b/api/tests/federation/test_views.py
index ac359eac6..2caa7856a 100644
--- a/api/tests/federation/test_views.py
+++ b/api/tests/federation/test_views.py
@@ -2,20 +2,7 @@ import pytest
from django.core.paginator import Paginator
from django.urls import reverse
-from funkwhale_api.federation import actors, serializers, webfinger
-
-
-@pytest.mark.parametrize("system_actor", actors.SYSTEM_ACTORS.keys())
-def test_instance_actors(system_actor, db, api_client):
- actor = actors.SYSTEM_ACTORS[system_actor].get_actor_instance()
- url = reverse("federation:instance-actors-detail", kwargs={"actor": system_actor})
- response = api_client.get(url)
- serializer = serializers.ActorSerializer(actor)
-
- if system_actor == "library":
- response.data.pop("url")
- assert response.status_code == 200
- assert response.data == serializer.data
+from funkwhale_api.federation import serializers, webfinger
def test_wellknown_webfinger_validates_resource(db, api_client, settings, mocker):
@@ -29,22 +16,6 @@ def test_wellknown_webfinger_validates_resource(db, api_client, settings, mocker
assert response.data["errors"]["resource"] == ("Missing webfinger resource type")
-@pytest.mark.parametrize("system_actor", actors.SYSTEM_ACTORS.keys())
-def test_wellknown_webfinger_system(system_actor, db, api_client, settings, mocker):
- actor = actors.SYSTEM_ACTORS[system_actor].get_actor_instance()
- url = reverse("federation:well-known-webfinger")
- response = api_client.get(
- url,
- data={"resource": "acct:{}".format(actor.webfinger_subject)},
- HTTP_ACCEPT="application/jrd+json",
- )
- serializer = serializers.ActorWebfingerSerializer(actor)
-
- assert response.status_code == 200
- assert response["Content-Type"] == "application/jrd+json"
- assert response.data == serializer.data
-
-
def test_wellknown_nodeinfo(db, preferences, api_client, settings):
expected = {
"links": [
diff --git a/api/tests/music/test_models.py b/api/tests/music/test_models.py
index aa618aa2d..d045a04c2 100644
--- a/api/tests/music/test_models.py
+++ b/api/tests/music/test_models.py
@@ -452,11 +452,6 @@ def test_get_audio_data(factories):
assert result == {"duration": 229, "bitrate": 128000, "size": 3459481}
-@pytest.mark.skip(reason="Refactoring in progress")
-def test_library_viewable_by():
- assert False
-
-
def test_library_queryset_with_follows(factories):
library1 = factories["music.Library"]()
library2 = factories["music.Library"]()
diff --git a/api/tests/requests/test_models.py b/api/tests/requests/test_models.py
deleted file mode 100644
index 9b9100b5c..000000000
--- a/api/tests/requests/test_models.py
+++ /dev/null
@@ -1,22 +0,0 @@
-import pytest
-
-
-@pytest.mark.skip(reason="Refactoring in progress")
-def test_can_bind_import_batch_to_request(factories):
- request = factories["requests.ImportRequest"]()
-
- assert request.status == "pending"
-
- # when we create the import, we consider the request as accepted
- batch = factories["music.ImportBatch"](import_request=request)
- request.refresh_from_db()
-
- assert request.status == "accepted"
-
- # now, the batch is finished, therefore the request status should be
- # imported
- batch.status = "finished"
- batch.save(update_fields=["status"])
- request.refresh_from_db()
-
- assert request.status == "imported"
diff --git a/api/tests/test_import_audio_file.py b/api/tests/test_import_audio_file.py
index ad4b4be0e..ce6aebbc3 100644
--- a/api/tests/test_import_audio_file.py
+++ b/api/tests/test_import_audio_file.py
@@ -33,7 +33,7 @@ def test_import_with_multiple_argument(factories, mocker):
path1 = os.path.join(DATA_DIR, "dummy_file.ogg")
path2 = os.path.join(DATA_DIR, "utf8-éà◌.ogg")
mocked_filter = mocker.patch(
- "funkwhale_api.providers.audiofile.management.commands.import_files.Command.filter_matching",
+ "funkwhale_api.music.management.commands.import_files.Command.filter_matching",
return_value=({"new": [], "skipped": []}),
)
call_command("import_files", str(library.uuid), path1, path2, interactive=False)
diff --git a/per-user-libraries b/per-user-libraries
deleted file mode 100644
index d2905d88a..000000000
--- a/per-user-libraries
+++ /dev/null
@@ -1,51 +0,0 @@
-Todo:
-
-- upload utilisateur
-- gestion des doublons ? Si piste uploadée deux fois, on fait quoi:
- - On rajoute un lien entre la piste existante et la bibliothèque de l'utilisateur
- - L'utilisateur peut forcer l'upload de SA piste
-- Comment on gère l'affichage : une piste peut ne pas être jouable
-- more tests about:
- - replacing
- - deletion
- - permissions
-
-- un utilisateur envoie une piste:
- - pas de problème: au pire les métadonnées ne sont pas bonnes mais ce n'est pas très grave
- - on incite à tagguer avec picard / musicbrainz
- - en cas de conflit (piste sans tag, mais le nom de l'artiste existe avec un ID musicbrainz, par exemple):
- on fournit à l'utilisateur le choix (binder à l'artiste musicbrainz), ou créer un artiste séparé
- on peut aussi tenter des trucs plus intelligents à base de matching sur les noms de pistes, mais dans un second temps
-
-- Un créateur envoie une piste:
- - il crée un profil avec son nom d'artiste, des liens vers ses différents profils (youtube, etc).
- - on peut lui fournir un snippet à inclure sur ses profils "Funkwhale: http://creator.url" pour servir à valider
- - les instances fédérées pourront donc faire la vérification elles-mêmes
- - à l'upload, il a un formulaire spécial ou il déclare bien être le créateur des pistes et avoir les droits
- - on ne tente pas d'être smart : il faut que les données soient fiables et éditables par le créateur avant publication !
-
-
-Jour 2:
-
-- on bind les fichiers aux bibliothèques
-- on ne dédoublonne pas, trop compliqué
-- en fédé, quand on scanne, on crée les track files, du coup plus besoin d'import manuel
-- dans le script de migration, gérer le cas dess trucs importés via la fédé
-
-
-Todo:
-
-- tester le remplacement
-- skipper les tracks qui sont déjà dans une autre bibliothèque
-- gestion d'erreur plus poussée
-- gérer les radios
-- tester permission sur la fédé
-- tester qu'on ne sert que les bibliothèques locales
-- virer au maximum la logique custom pour les acteurs systèmes
-- utiliser le vrai champs durée d'activitystream pour l'audio
-- shared inbox url
-- vue pour servir les bibliothèques:
-
-- logique de scan:
- - pouvoir lancer, mettre en pause, interrompre un scan
- - avoir des infos sur le déroulement du scan