From e271851f67b4af60f39ae2972c0f3af12a624086 Mon Sep 17 00:00:00 2001 From: Agate Date: Mon, 3 Aug 2020 15:47:14 +0200 Subject: [PATCH] See #1100: clean compat and XXX in the code --- api/config/api_urls.py | 5 --- api/config/routing.py | 7 +--- api/config/settings/common.py | 19 ---------- .../common/dynamic_preferences_registry.py | 8 ++--- api/funkwhale_api/common/fields.py | 3 +- api/funkwhale_api/common/middleware.py | 2 +- api/funkwhale_api/common/serializers.py | 14 -------- api/funkwhale_api/favorites/filters.py | 3 +- .../dynamic_preferences_registry.py | 14 ++++---- api/funkwhale_api/federation/serializers.py | 19 +++------- api/funkwhale_api/federation/utils.py | 1 - .../migrations/0006_auto_20200803_1222.py | 22 ++++++++++++ api/funkwhale_api/music/models.py | 5 --- api/funkwhale_api/music/serializers.py | 5 +-- api/funkwhale_api/music/tasks.py | 4 +-- api/funkwhale_api/music/views.py | 35 +++++-------------- .../playlists/dynamic_preferences_registry.py | 6 ++-- api/funkwhale_api/playlists/filters.py | 1 - api/funkwhale_api/playlists/models.py | 1 - .../migrations/0005_auto_20200803_1222.py | 20 +++++++++++ .../migrations/0002_auto_20200803_1222.py | 25 +++++++++++++ api/tests/channels/test_auth.py | 30 ---------------- api/tests/common/test_routers.py | 2 +- api/tests/common/test_serializers.py | 20 ----------- api/tests/favorites/test_favorites.py | 3 +- api/tests/federation/test_serializers.py | 33 +++++++---------- api/tests/federation/test_views.py | 5 --- api/tests/music/test_serializers.py | 9 ----- api/tests/music/test_tasks.py | 6 ++-- api/tests/music/test_views.py | 10 ++---- docs/api/definitions.yml | 28 ++------------- docs/swagger.yml | 2 +- front/src/EmbedFrame.vue | 2 +- front/src/components/Queue.vue | 8 ++--- front/src/components/Sidebar.vue | 2 +- front/src/components/audio/ArtistLabel.vue | 2 +- front/src/components/audio/ChannelCard.vue | 2 +- .../src/components/audio/ChannelEntryCard.vue | 8 ++--- .../src/components/audio/ChannelSerieCard.vue | 8 ++--- front/src/components/audio/Player.vue | 24 ++++++------- front/src/components/audio/album/Card.vue | 6 ++-- front/src/components/audio/artist/Card.vue | 12 +++---- front/src/components/audio/track/Row.vue | 4 +-- front/src/components/audio/track/Widget.vue | 2 +- front/src/components/auth/Settings.vue | 8 ++--- .../src/components/auth/SubsonicTokenForm.vue | 6 ++-- front/src/components/channels/UploadForm.vue | 6 ++-- front/src/components/common/ActorAvatar.vue | 2 +- front/src/components/common/UserLink.vue | 4 +-- front/src/components/library/AlbumBase.vue | 12 +++---- front/src/components/library/ArtistBase.vue | 8 ++--- front/src/components/library/FileUpload.vue | 2 +- front/src/components/library/TrackBase.vue | 4 +-- front/src/components/library/TrackDetail.vue | 4 +-- front/src/components/playlists/Editor.vue | 2 +- front/src/store/auth.js | 2 +- front/src/store/playlists.js | 2 +- front/src/views/Notifications.vue | 2 +- front/src/views/admin/ChannelDetail.vue | 2 +- front/src/views/admin/library/AlbumDetail.vue | 2 +- .../src/views/admin/library/ArtistDetail.vue | 2 +- front/src/views/admin/library/TrackDetail.vue | 2 +- front/src/views/auth/ProfileBase.vue | 4 +-- front/src/views/auth/ProfileOverview.vue | 2 +- front/src/views/channels/DetailBase.vue | 2 +- front/src/views/content/libraries/Quota.vue | 2 +- front/tests/unit/specs/store/auth.spec.js | 2 +- 67 files changed, 203 insertions(+), 328 deletions(-) create mode 100644 api/funkwhale_api/moderation/migrations/0006_auto_20200803_1222.py create mode 100644 api/funkwhale_api/radios/migrations/0005_auto_20200803_1222.py create mode 100644 api/funkwhale_api/tags/migrations/0002_auto_20200803_1222.py delete mode 100644 api/tests/channels/test_auth.py diff --git a/api/config/api_urls.py b/api/config/api_urls.py index 909dff45e..73df6fc4e 100644 --- a/api/config/api_urls.py +++ b/api/config/api_urls.py @@ -74,11 +74,6 @@ v1_patterns += [ include(("funkwhale_api.history.urls", "history"), namespace="history"), ), url(r"^", include(("funkwhale_api.users.api_urls", "users"), namespace="users"),), - # XXX: 1.0: remove this - url( - r"^users/", - include(("funkwhale_api.users.api_urls", "users"), namespace="users-nested"), - ), url( r"^oauth/", include(("funkwhale_api.users.oauth.urls", "oauth"), namespace="oauth"), diff --git a/api/config/routing.py b/api/config/routing.py index d0858e243..ce293ff76 100644 --- a/api/config/routing.py +++ b/api/config/routing.py @@ -2,18 +2,13 @@ from channels.auth import AuthMiddlewareStack from channels.routing import ProtocolTypeRouter, URLRouter from django.conf.urls import url -from funkwhale_api.common.auth import TokenAuthMiddleware from funkwhale_api.instance import consumers application = ProtocolTypeRouter( { # Empty for now (http->django views is added by default) "websocket": AuthMiddlewareStack( - TokenAuthMiddleware( - URLRouter( - [url("^api/v1/activity$", consumers.InstanceActivityConsumer)] - ) - ) + URLRouter([url("^api/v1/activity$", consumers.InstanceActivityConsumer)]) ) } ) diff --git a/api/config/settings/common.py b/api/config/settings/common.py index 646345544..9f5a551ed 100644 --- a/api/config/settings/common.py +++ b/api/config/settings/common.py @@ -169,17 +169,7 @@ os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = env.bool( "DJANGO_ALLOW_ASYNC_UNSAFE", default="true" ) -# XXX: deprecated, see #186 -FEDERATION_ENABLED = env.bool("FEDERATION_ENABLED", default=True) FEDERATION_HOSTNAME = env("FEDERATION_HOSTNAME", default=FUNKWHALE_HOSTNAME).lower() -# XXX: deprecated, see #186 -FEDERATION_COLLECTION_PAGE_SIZE = env.int("FEDERATION_COLLECTION_PAGE_SIZE", default=50) -# XXX: deprecated, see #186 -FEDERATION_MUSIC_NEEDS_APPROVAL = env.bool( - "FEDERATION_MUSIC_NEEDS_APPROVAL", default=True -) -# XXX: deprecated, see #186 -FEDERATION_ACTOR_FETCH_DELAY = env.int("FEDERATION_ACTOR_FETCH_DELAY", default=60 * 12) FEDERATION_SERVICE_ACTOR_USERNAME = env( "FEDERATION_SERVICE_ACTOR_USERNAME", default="service" ) @@ -1129,11 +1119,6 @@ Exemples: CSRF_USE_SESSIONS = True SESSION_ENGINE = "django.contrib.sessions.backends.cache" -# Playlist settings -# XXX: deprecated, see #186 -PLAYLISTS_MAX_TRACKS = env.int("PLAYLISTS_MAX_TRACKS", default=250) - - ACCOUNT_USERNAME_BLACKLIST = [ "funkwhale", "library", @@ -1170,8 +1155,6 @@ EXTERNAL_REQUESTS_TIMEOUT = env.int("EXTERNAL_REQUESTS_TIMEOUT", default=10) """ Default timeout for external requests. """ -# XXX: deprecated, see #186 -API_AUTHENTICATION_REQUIRED = env.bool("API_AUTHENTICATION_REQUIRED", True) MUSIC_DIRECTORY_PATH = env("MUSIC_DIRECTORY_PATH", default=None) """ @@ -1285,8 +1268,6 @@ FUNKWHALE_SUPPORT_MESSAGE_DELAY = env.int("FUNKWHALE_SUPPORT_MESSAGE_DELAY", def """ Delay in days after signup before we show the "support Funkwhale" message """ -# XXX Stable release: remove -USE_FULL_TEXT_SEARCH = env.bool("USE_FULL_TEXT_SEARCH", default=True) MIN_DELAY_BETWEEN_DOWNLOADS_COUNT = env.int( "MIN_DELAY_BETWEEN_DOWNLOADS_COUNT", default=60 * 60 * 6 diff --git a/api/funkwhale_api/common/dynamic_preferences_registry.py b/api/funkwhale_api/common/dynamic_preferences_registry.py index d6dfed783..6d15fcd8f 100644 --- a/api/funkwhale_api/common/dynamic_preferences_registry.py +++ b/api/funkwhale_api/common/dynamic_preferences_registry.py @@ -1,19 +1,15 @@ from dynamic_preferences import types from dynamic_preferences.registries import global_preferences_registry -from funkwhale_api.common import preferences - common = types.Section("common") @global_preferences_registry.register -class APIAutenticationRequired( - preferences.DefaultFromSettingMixin, types.BooleanPreference -): +class APIAutenticationRequired(types.BooleanPreference): section = common name = "api_authentication_required" verbose_name = "API Requires authentication" - setting = "API_AUTHENTICATION_REQUIRED" + default = True help_text = ( "If disabled, anonymous users will be able to query the API" "and access music data (as well as other data exposed in the API " diff --git a/api/funkwhale_api/common/fields.py b/api/funkwhale_api/common/fields.py index ffc8e5f95..d354c93eb 100644 --- a/api/funkwhale_api/common/fields.py +++ b/api/funkwhale_api/common/fields.py @@ -1,6 +1,5 @@ import django_filters from django import forms -from django.conf import settings from django.core.serializers.json import DjangoJSONEncoder from django.db import models @@ -40,7 +39,7 @@ class SearchFilter(django_filters.CharFilter): def filter(self, qs, value): if not value: return qs - if settings.USE_FULL_TEXT_SEARCH and self.fts_search_fields: + if self.fts_search_fields: query = search.get_fts_query( value, self.fts_search_fields, model=self.parent.Meta.model ) diff --git a/api/funkwhale_api/common/middleware.py b/api/funkwhale_api/common/middleware.py index b085a18fe..475215910 100644 --- a/api/funkwhale_api/common/middleware.py +++ b/api/funkwhale_api/common/middleware.py @@ -113,7 +113,7 @@ def get_spa_html(spa_url): def get_spa_file(spa_url, name): if spa_url.startswith("/"): - # XXX: spa_url is an absolute path to index.html, on the local disk. + # spa_url is an absolute path to index.html, on the local disk. # However, we may want to access manifest.json or other files as well, so we # strip the filename path = os.path.join(os.path.dirname(spa_url), name) diff --git a/api/funkwhale_api/common/serializers.py b/api/funkwhale_api/common/serializers.py index 76e698c5f..9210c0603 100644 --- a/api/funkwhale_api/common/serializers.py +++ b/api/funkwhale_api/common/serializers.py @@ -299,20 +299,6 @@ class AttachmentSerializer(serializers.Serializer): urls["medium_square_crop"] = o.download_url_medium_square_crop return urls - def to_representation(self, o): - repr = super().to_representation(o) - # XXX: BACKWARD COMPATIBILITY - # having the attachment urls in a nested JSON obj is better, - # but we can't do this without breaking clients - # So we extract the urls and include these in the parent payload - repr.update({k: v for k, v in repr["urls"].items() if k != "source"}) - # also, our legacy images had lots of variations (400x400, 200x200, 50x50) - # but we removed some of these, so we emulate these by hand (by redirecting) - # to actual, existing attachment variations - repr["square_crop"] = repr["medium_square_crop"] - repr["small_square_crop"] = repr["medium_square_crop"] - return repr - def create(self, validated_data): return models.Attachment.objects.create( file=validated_data["file"], actor=validated_data["actor"] diff --git a/api/funkwhale_api/favorites/filters.py b/api/funkwhale_api/favorites/filters.py index 0b76a4dd0..32c07a646 100644 --- a/api/funkwhale_api/favorites/filters.py +++ b/api/funkwhale_api/favorites/filters.py @@ -13,8 +13,7 @@ class TrackFavoriteFilter(moderation_filters.HiddenContentFilterSet): class Meta: model = models.TrackFavorite - # XXX: 1.0 remove the user filter, we have scope=me now - fields = ["user"] + fields = [] hidden_content_fields_mapping = moderation_filters.USER_FILTER_CONFIG[ "TRACK_FAVORITE" ] diff --git a/api/funkwhale_api/federation/dynamic_preferences_registry.py b/api/funkwhale_api/federation/dynamic_preferences_registry.py index 6f7d71719..c696b9578 100644 --- a/api/funkwhale_api/federation/dynamic_preferences_registry.py +++ b/api/funkwhale_api/federation/dynamic_preferences_registry.py @@ -1,8 +1,6 @@ from dynamic_preferences import types from dynamic_preferences.registries import global_preferences_registry -from funkwhale_api.common import preferences - federation = types.Section("federation") @@ -22,10 +20,10 @@ class MusicCacheDuration(types.IntPreference): @global_preferences_registry.register -class Enabled(preferences.DefaultFromSettingMixin, types.BooleanPreference): +class Enabled(types.BooleanPreference): section = federation name = "enabled" - setting = "FEDERATION_ENABLED" + default = True verbose_name = "Federation enabled" help_text = ( "Use this setting to enable or disable federation logic and API" " globally." @@ -33,20 +31,20 @@ class Enabled(preferences.DefaultFromSettingMixin, types.BooleanPreference): @global_preferences_registry.register -class CollectionPageSize(preferences.DefaultFromSettingMixin, types.IntPreference): +class CollectionPageSize(types.IntPreference): section = federation name = "collection_page_size" - setting = "FEDERATION_COLLECTION_PAGE_SIZE" + default = 50 verbose_name = "Federation collection page size" help_text = "How many items to display in ActivityPub collections." field_kwargs = {"required": False} @global_preferences_registry.register -class ActorFetchDelay(preferences.DefaultFromSettingMixin, types.IntPreference): +class ActorFetchDelay(types.IntPreference): section = federation name = "actor_fetch_delay" - setting = "FEDERATION_ACTOR_FETCH_DELAY" + default = 60 * 12 verbose_name = "Federation actor fetch delay" help_text = ( "How many minutes to wait before refetching actors on " diff --git a/api/funkwhale_api/federation/serializers.py b/api/funkwhale_api/federation/serializers.py index 00d5d1e4d..9f68a2375 100644 --- a/api/funkwhale_api/federation/serializers.py +++ b/api/funkwhale_api/federation/serializers.py @@ -938,8 +938,6 @@ class PaginatedCollectionSerializer(jsonld.JsonLdSerializer): last = common_utils.set_query_parameter(conf["id"], page=paginator.num_pages) d = { "id": conf["id"], - # XXX Stable release: remove the obsolete actor field - "actor": conf["actor"].fid, "attributedTo": conf["actor"].fid, "totalItems": paginator.count, "type": conf.get("type", "Collection"), @@ -1004,9 +1002,8 @@ class LibrarySerializer(PaginatedCollectionSerializer): "name": library.name, "summary": library.description, "page_size": 100, - # XXX Stable release: remove the obsolete actor field - "actor": library.actor, "attributedTo": library.actor, + "actor": library.actor, "items": library.uploads.for_federation(), "type": "Library", } @@ -1108,8 +1105,6 @@ class CollectionPageSerializer(jsonld.JsonLdSerializer): ], } if conf["actor"]: - # XXX Stable release: remove the obsolete actor field - d["actor"] = conf["actor"].fid d["attributedTo"] = conf["actor"].fid if page.has_previous(): @@ -1297,8 +1292,7 @@ class AlbumSerializer(MusicEntitySerializer): child=MultipleSerializer(allowed=[BasicActorSerializer, ArtistSerializer]), min_length=1, ) - # XXX: 1.0 rename to image - cover = ImageSerializer( + image = ImageSerializer( allowed_mimetypes=["image/*"], allow_null=True, required=False, @@ -1306,7 +1300,7 @@ class AlbumSerializer(MusicEntitySerializer): ) updateable_fields = [ ("name", "title"), - ("cover", "attachment_cover"), + ("image", "attachment_cover"), ("musicbrainzId", "mbid"), ("attributedTo", "attributed_to"), ("released", "release_date"), @@ -1320,7 +1314,7 @@ class AlbumSerializer(MusicEntitySerializer): { "released": jsonld.first_val(contexts.FW.released), "artists": jsonld.first_attr(contexts.FW.artists, "@list"), - "cover": jsonld.first_obj(contexts.FW.cover), + "image": jsonld.first_obj(contexts.AS.image), }, ) @@ -1354,11 +1348,6 @@ class AlbumSerializer(MusicEntitySerializer): ] include_content(d, instance.description) if instance.attachment_cover: - d["cover"] = { - "type": "Link", - "href": instance.attachment_cover.download_url_original, - "mediaType": instance.attachment_cover.mimetype or "image/jpeg", - } include_image(d, instance.attachment_cover) if self.context.get("include_ap_context", self.parent is None): diff --git a/api/funkwhale_api/federation/utils.py b/api/funkwhale_api/federation/utils.py index d6a35df28..2bac8daf8 100644 --- a/api/funkwhale_api/federation/utils.py +++ b/api/funkwhale_api/federation/utils.py @@ -218,7 +218,6 @@ def should_redirect_ap_to_html(accept_header, default=True): "text/html", ] no_redirect_headers = [ - "*/*", # XXX backward compat with older Funkwhale instances that don't send the Accept header "application/json", "application/activity+json", "application/ld+json", diff --git a/api/funkwhale_api/moderation/migrations/0006_auto_20200803_1222.py b/api/funkwhale_api/moderation/migrations/0006_auto_20200803_1222.py new file mode 100644 index 000000000..ca1cfc9ed --- /dev/null +++ b/api/funkwhale_api/moderation/migrations/0006_auto_20200803_1222.py @@ -0,0 +1,22 @@ +# Generated by Django 3.0.8 on 2020-08-03 12:22 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('moderation', '0005_auto_20200317_0820'), + ] + + operations = [ + migrations.RemoveField( + model_name='userrequest', + name='url', + ), + migrations.AlterField( + model_name='userrequest', + name='status', + field=models.CharField(choices=[('pending', 'Pending'), ('refused', 'Refused'), ('approved', 'Approved')], default='pending', max_length=40), + ), + ] diff --git a/api/funkwhale_api/music/models.py b/api/funkwhale_api/music/models.py index 1d32a01bd..121c0f3ff 100644 --- a/api/funkwhale_api/music/models.py +++ b/api/funkwhale_api/music/models.py @@ -20,7 +20,6 @@ from django.db.models.signals import post_save, pre_save from django.dispatch import receiver from django.urls import reverse from django.utils import timezone -from versatileimagefield.fields import VersatileImageField from funkwhale_api import musicbrainz from funkwhale_api.common import fields @@ -325,10 +324,6 @@ class Album(APIModelMixin): artist = models.ForeignKey(Artist, related_name="albums", on_delete=models.CASCADE) release_date = models.DateField(null=True, blank=True, db_index=True) release_group_id = models.UUIDField(null=True, blank=True) - # XXX: 1.0 clean this uneeded field in favor of attachment_cover - cover = VersatileImageField( - upload_to="albums/covers/%Y/%m/%d", null=True, blank=True - ) attachment_cover = models.ForeignKey( "common.Attachment", null=True, diff --git a/api/funkwhale_api/music/serializers.py b/api/funkwhale_api/music/serializers.py index c5e4336a6..1dd17b2bf 100644 --- a/api/funkwhale_api/music/serializers.py +++ b/api/funkwhale_api/music/serializers.py @@ -32,10 +32,7 @@ COVER_WRITE_FIELD = common_serializers.RelatedField( from funkwhale_api.audio import serializers as audio_serializers # NOQA -class CoverField( - common_serializers.NullToEmptDict, common_serializers.AttachmentSerializer -): - # XXX: BACKWARD COMPATIBILITY +class CoverField(common_serializers.AttachmentSerializer): pass diff --git a/api/funkwhale_api/music/tasks.py b/api/funkwhale_api/music/tasks.py index 2e633925b..7f47b402b 100644 --- a/api/funkwhale_api/music/tasks.py +++ b/api/funkwhale_api/music/tasks.py @@ -364,7 +364,7 @@ def federation_audio_track_to_metadata(payload, references): "mbid": str(payload["album"]["musicbrainzId"]) if payload["album"].get("musicbrainzId") else None, - "cover_data": get_cover(payload["album"], "cover"), + "cover_data": get_cover(payload["album"], "image"), "release_date": payload["album"].get("released"), "tags": [t["name"] for t in payload["album"].get("tags", []) or []], "artists": [ @@ -896,8 +896,6 @@ UPDATE_CONFIG = { @transaction.atomic def update_track_metadata(audio_metadata, track): - # XXX: implement this to support updating metadata when an imported files - # is updated by an outside tool (e.g beets). serializer = metadata.TrackMetadataSerializer(data=audio_metadata) serializer.is_valid(raise_exception=True) new_data = serializer.validated_data diff --git a/api/funkwhale_api/music/views.py b/api/funkwhale_api/music/views.py index 18c2b30aa..0df0d4640 100644 --- a/api/funkwhale_api/music/views.py +++ b/api/funkwhale_api/music/views.py @@ -797,20 +797,11 @@ class Search(views.APIView): return Response(results, status=200) def get_tracks(self, query): - search_fields = [ - "mbid", - "title__unaccent", - "album__title__unaccent", - "artist__name__unaccent", - ] - if settings.USE_FULL_TEXT_SEARCH: - query_obj = utils.get_fts_query( - query, - fts_fields=["body_text", "album__body_text", "artist__body_text"], - model=models.Track, - ) - else: - query_obj = utils.get_query(query, search_fields) + query_obj = utils.get_fts_query( + query, + fts_fields=["body_text", "album__body_text", "artist__body_text"], + model=models.Track, + ) qs = ( models.Track.objects.all() .filter(query_obj) @@ -828,13 +819,9 @@ class Search(views.APIView): return common_utils.order_for_search(qs, "title")[: self.max_results] def get_albums(self, query): - search_fields = ["mbid", "title__unaccent", "artist__name__unaccent"] - if settings.USE_FULL_TEXT_SEARCH: - query_obj = utils.get_fts_query( - query, fts_fields=["body_text", "artist__body_text"], model=models.Album - ) - else: - query_obj = utils.get_query(query, search_fields) + query_obj = utils.get_fts_query( + query, fts_fields=["body_text", "artist__body_text"], model=models.Album + ) qs = ( models.Album.objects.all() .filter(query_obj) @@ -844,11 +831,7 @@ class Search(views.APIView): return common_utils.order_for_search(qs, "title")[: self.max_results] def get_artists(self, query): - search_fields = ["mbid", "name__unaccent"] - if settings.USE_FULL_TEXT_SEARCH: - query_obj = utils.get_fts_query(query, model=models.Artist) - else: - query_obj = utils.get_query(query, search_fields) + query_obj = utils.get_fts_query(query, model=models.Artist) qs = ( models.Artist.objects.all() .filter(query_obj) diff --git a/api/funkwhale_api/playlists/dynamic_preferences_registry.py b/api/funkwhale_api/playlists/dynamic_preferences_registry.py index 5a2043452..ee1a33dea 100644 --- a/api/funkwhale_api/playlists/dynamic_preferences_registry.py +++ b/api/funkwhale_api/playlists/dynamic_preferences_registry.py @@ -1,16 +1,14 @@ from dynamic_preferences import types from dynamic_preferences.registries import global_preferences_registry -from funkwhale_api.common import preferences - playlists = types.Section("playlists") @global_preferences_registry.register -class MaxTracks(preferences.DefaultFromSettingMixin, types.IntegerPreference): +class MaxTracks(types.IntegerPreference): show_in_api = True section = playlists name = "max_tracks" + default = 250 verbose_name = "Max tracks per playlist" - setting = "PLAYLISTS_MAX_TRACKS" field_kwargs = {"required": False} diff --git a/api/funkwhale_api/playlists/filters.py b/api/funkwhale_api/playlists/filters.py index e12813b38..5270eee80 100644 --- a/api/funkwhale_api/playlists/filters.py +++ b/api/funkwhale_api/playlists/filters.py @@ -31,7 +31,6 @@ class PlaylistFilter(filters.FilterSet): class Meta: model = models.Playlist fields = { - "user": ["exact"], "name": ["exact", "icontains"], } diff --git a/api/funkwhale_api/playlists/models.py b/api/funkwhale_api/playlists/models.py index a20dbc937..b227a545d 100644 --- a/api/funkwhale_api/playlists/models.py +++ b/api/funkwhale_api/playlists/models.py @@ -227,7 +227,6 @@ class PlaylistTrack(models.Model): class Meta: ordering = ("-playlist", "index") - unique_together = ("playlist", "index") def delete(self, *args, **kwargs): playlist = self.playlist diff --git a/api/funkwhale_api/radios/migrations/0005_auto_20200803_1222.py b/api/funkwhale_api/radios/migrations/0005_auto_20200803_1222.py new file mode 100644 index 000000000..8e4cd6770 --- /dev/null +++ b/api/funkwhale_api/radios/migrations/0005_auto_20200803_1222.py @@ -0,0 +1,20 @@ +# Generated by Django 3.0.8 on 2020-08-03 12:22 + +import django.contrib.postgres.fields.jsonb +import django.core.serializers.json +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('radios', '0004_auto_20180107_1813'), + ] + + operations = [ + migrations.AlterField( + model_name='radio', + name='config', + field=django.contrib.postgres.fields.jsonb.JSONField(encoder=django.core.serializers.json.DjangoJSONEncoder), + ), + ] diff --git a/api/funkwhale_api/tags/migrations/0002_auto_20200803_1222.py b/api/funkwhale_api/tags/migrations/0002_auto_20200803_1222.py new file mode 100644 index 000000000..382f24b4c --- /dev/null +++ b/api/funkwhale_api/tags/migrations/0002_auto_20200803_1222.py @@ -0,0 +1,25 @@ +# Generated by Django 3.0.8 on 2020-08-03 12:22 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ('tags', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='taggeditem', + name='content_type', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tagged_items', to='contenttypes.ContentType', verbose_name='Content type'), + ), + migrations.AlterField( + model_name='taggeditem', + name='tag', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tagged_items', to='tags.Tag'), + ), + ] diff --git a/api/tests/channels/test_auth.py b/api/tests/channels/test_auth.py deleted file mode 100644 index 505bef1c0..000000000 --- a/api/tests/channels/test_auth.py +++ /dev/null @@ -1,30 +0,0 @@ -import pytest -from rest_framework_jwt.settings import api_settings - -from funkwhale_api.common.auth import TokenAuthMiddleware - -jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER -jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER - - -@pytest.mark.parametrize("query_string", [b"token=wrong", b""]) -def test_header_anonymous(query_string, factories): - def callback(scope): - assert scope["user"].is_anonymous - - scope = {"query_string": query_string} - consumer = TokenAuthMiddleware(callback) - consumer(scope) - - -def test_header_correct_token(factories): - user = factories["users.User"]() - payload = jwt_payload_handler(user) - token = jwt_encode_handler(payload) - - def callback(scope): - assert scope["user"] == user - - scope = {"query_string": "token={}".format(token).encode("utf-8")} - consumer = TokenAuthMiddleware(callback) - consumer(scope) diff --git a/api/tests/common/test_routers.py b/api/tests/common/test_routers.py index 3bd5c4e47..5d1710f09 100644 --- a/api/tests/common/test_routers.py +++ b/api/tests/common/test_routers.py @@ -17,7 +17,7 @@ from django import urls "/api/v1/auth/registration/account-confirm-email/key", "/api/v1/history/listenings", "/api/v1/radios/sessions", - "/api/v1/users/users/me", + "/api/v1/users/me", "/api/v1/federation/follows/library", "/api/v1/manage/accounts", "/api/v1/oauth/apps", diff --git a/api/tests/common/test_serializers.py b/api/tests/common/test_serializers.py index 8fdb21edb..1d23efb66 100644 --- a/api/tests/common/test_serializers.py +++ b/api/tests/common/test_serializers.py @@ -201,15 +201,6 @@ def test_attachment_serializer_existing_file(factories, to_api_date): attachment.file.crop["200x200"].url ), }, - # XXX: BACKWARD COMPATIBILITY - "original": federation_utils.full_url(attachment.file.url), - "medium_square_crop": federation_utils.full_url( - attachment.file.crop["200x200"].url - ), - "small_square_crop": federation_utils.full_url( - attachment.file.crop["200x200"].url - ), - "square_crop": federation_utils.full_url(attachment.file.crop["200x200"].url), } serializer = serializers.AttachmentSerializer(attachment) @@ -237,17 +228,6 @@ def test_attachment_serializer_remote_file(factories, to_api_date): proxy_url + "?next=medium_square_crop" ), }, - # XXX: BACKWARD COMPATIBILITY - "original": federation_utils.full_url(proxy_url + "?next=original"), - "medium_square_crop": federation_utils.full_url( - proxy_url + "?next=medium_square_crop" - ), - "square_crop": federation_utils.full_url( - proxy_url + "?next=medium_square_crop" - ), - "small_square_crop": federation_utils.full_url( - proxy_url + "?next=medium_square_crop" - ), } serializer = serializers.AttachmentSerializer(attachment) diff --git a/api/tests/favorites/test_favorites.py b/api/tests/favorites/test_favorites.py index 48d1857c6..06d4caa61 100644 --- a/api/tests/favorites/test_favorites.py +++ b/api/tests/favorites/test_favorites.py @@ -20,10 +20,11 @@ def test_user_can_get_his_favorites( api_request, factories, logged_in_api_client, client ): request = api_request.get("/") + logged_in_api_client.user.create_actor() favorite = factories["favorites.TrackFavorite"](user=logged_in_api_client.user) factories["favorites.TrackFavorite"]() url = reverse("api:v1:favorites:tracks-list") - response = logged_in_api_client.get(url, {"user": logged_in_api_client.user.pk}) + response = logged_in_api_client.get(url, {"scope": "me"}) expected = [ serializers.UserTrackFavoriteSerializer( favorite, context={"request": request} diff --git a/api/tests/federation/test_serializers.py b/api/tests/federation/test_serializers.py index addf74b4a..6617ba07d 100644 --- a/api/tests/federation/test_serializers.py +++ b/api/tests/federation/test_serializers.py @@ -388,7 +388,6 @@ def test_paginated_collection_serializer(factories): "@context": jsonld.get_default_context(), "type": "Collection", "id": conf["id"], - "actor": actor.fid, "attributedTo": actor.fid, "totalItems": len(uploads), "current": conf["id"] + "?page=1", @@ -486,7 +485,6 @@ def test_collection_page_serializer(factories): "@context": jsonld.get_default_context(), "type": "CollectionPage", "id": conf["id"] + "?page=2", - "actor": actor.fid, "attributedTo": actor.fid, "totalItems": len(uploads), "partOf": conf["id"], @@ -521,7 +519,6 @@ def test_music_library_serializer_to_ap(factories): "id": library.fid, "name": library.name, "summary": library.description, - "actor": library.actor.fid, "attributedTo": library.actor.fid, "totalItems": 0, "current": library.fid + "?page=1", @@ -764,11 +761,6 @@ def test_activity_pub_album_serializer_to_ap(factories): "type": "Album", "id": album.fid, "name": album.title, - "cover": { - "type": "Link", - "mediaType": "image/jpeg", - "href": utils.full_url(album.attachment_cover.file.url), - }, "image": { "type": "Image", "mediaType": "image/jpeg", @@ -815,7 +807,7 @@ def test_activity_pub_album_serializer_from_ap_create(factories, faker, now): "type": "Album", "id": "https://album.example", "name": faker.sentence(), - "cover": {"type": "Link", "mediaType": "image/jpeg", "href": faker.url()}, + "image": {"type": "Link", "mediaType": "image/jpeg", "href": faker.url()}, "musicbrainzId": faker.uuid4(), "published": now.isoformat(), "released": released.isoformat(), @@ -839,8 +831,8 @@ def test_activity_pub_album_serializer_from_ap_create(factories, faker, now): assert str(album.mbid) == payload["musicbrainzId"] assert album.release_date == released assert album.artist == artist - assert album.attachment_cover.url == payload["cover"]["href"] - assert album.attachment_cover.mimetype == payload["cover"]["mediaType"] + assert album.attachment_cover.url == payload["image"]["href"] + assert album.attachment_cover.mimetype == payload["image"]["mediaType"] assert sorted(album.tagged_items.values_list("tag__name", flat=True)) == [ "Punk", "Rock", @@ -879,7 +871,7 @@ def test_activity_pub_album_serializer_from_ap_update(factories, faker): "type": "Album", "id": album.fid, "name": faker.sentence(), - "cover": {"type": "Link", "mediaType": "image/jpeg", "href": faker.url()}, + "image": {"type": "Link", "mediaType": "image/jpeg", "href": faker.url()}, "musicbrainzId": faker.uuid4(), "published": album.creation_date.isoformat(), "released": released.isoformat(), @@ -904,8 +896,8 @@ def test_activity_pub_album_serializer_from_ap_update(factories, faker): assert album.title == payload["name"] assert str(album.mbid) == payload["musicbrainzId"] assert album.release_date == released - assert album.attachment_cover.url == payload["cover"]["href"] - assert album.attachment_cover.mimetype == payload["cover"]["mediaType"] + assert album.attachment_cover.url == payload["image"]["href"] + assert album.attachment_cover.mimetype == payload["image"]["mediaType"] assert sorted(album.tagged_items.values_list("tag__name", flat=True)) == [ "Punk", "Rock", @@ -996,7 +988,7 @@ def test_activity_pub_track_serializer_from_ap(factories, r_mock, mocker): "content": "Album summary", "mediaType": "text/markdown", "attributedTo": album_attributed_to.fid, - "cover": { + "image": { "type": "Link", "href": "https://cover.image/test.png", "mediaType": "image/png", @@ -1066,8 +1058,8 @@ def test_activity_pub_track_serializer_from_ap(factories, r_mock, mocker): assert track.attachment_cover.mimetype == data["image"]["mediaType"] assert album.from_activity == activity - assert album.attachment_cover.url == data["album"]["cover"]["href"] - assert album.attachment_cover.mimetype == data["album"]["cover"]["mediaType"] + assert album.attachment_cover.url == data["album"]["image"]["href"] + assert album.attachment_cover.mimetype == data["album"]["image"]["mediaType"] assert album.title == data["album"]["name"] assert album.fid == data["album"]["id"] assert str(album.mbid) == data["album"]["musicbrainzId"] @@ -1196,7 +1188,7 @@ def test_activity_pub_upload_serializer_from_ap(factories, mocker, r_mock): "musicbrainzId": str(uuid.uuid4()), "published": published.isoformat(), "released": released.isoformat(), - "cover": { + "image": { "type": "Link", "href": "https://cover.image/test.png", "mediaType": "image/png", @@ -1222,7 +1214,7 @@ def test_activity_pub_upload_serializer_from_ap(factories, mocker, r_mock): ], }, } - r_mock.get(data["track"]["album"]["cover"]["href"], body=io.BytesIO(b"coucou")) + r_mock.get(data["track"]["album"]["image"]["href"], body=io.BytesIO(b"coucou")) serializer = serializers.UploadSerializer(data=data, context={"activity": activity}) assert serializer.is_valid(raise_exception=True) @@ -1266,7 +1258,7 @@ def test_activity_pub_upload_serializer_from_ap_update(factories, mocker, now, r "library": library.fid, "track": serializers.TrackSerializer(upload.track).data, } - r_mock.get(data["track"]["album"]["cover"]["href"], body=io.BytesIO(b"coucou")) + r_mock.get(data["track"]["album"]["image"]["url"], body=io.BytesIO(b"coucou")) serializer = serializers.UploadSerializer(upload, data=data) assert serializer.is_valid(raise_exception=True) @@ -1628,7 +1620,6 @@ def test_channel_actor_outbox_serializer(factories): "@context": jsonld.get_default_context(), "type": "OrderedCollection", "id": channel.actor.outbox_url, - "actor": channel.actor.fid, "attributedTo": channel.actor.fid, "totalItems": len(uploads), "first": channel.actor.outbox_url + "?page=1", diff --git a/api/tests/federation/test_views.py b/api/tests/federation/test_views.py index f6528bab2..8b3ec8725 100644 --- a/api/tests/federation/test_views.py +++ b/api/tests/federation/test_views.py @@ -384,11 +384,6 @@ def test_music_upload_detail_private_approved_follow( ("text/html,application/xhtml+xml", True, True), ("text/html,application/json", True, True), ("", True, False), - ( - "*/*", - True, - False, - ), # XXX: compat with older versions of Funkwhale that miss the Accept header (None, True, False), ("application/json", True, False), ("application/activity+json", True, False), diff --git a/api/tests/music/test_serializers.py b/api/tests/music/test_serializers.py index 1fb5ac120..802f7169c 100644 --- a/api/tests/music/test_serializers.py +++ b/api/tests/music/test_serializers.py @@ -196,15 +196,6 @@ def test_album_serializer(factories, to_api_date): assert serializer.data == expected -def test_album_serializer_empty_cover(factories, to_api_date): - # XXX: BACKWARD COMPATIBILITY - album = factories["music.Album"](attachment_cover=None) - - serializer = serializers.AlbumSerializer(album) - - assert serializer.data["cover"] == {} - - def test_track_serializer(factories, to_api_date): actor = factories["federation.Actor"]() upload = factories["music.Upload"]( diff --git a/api/tests/music/test_tasks.py b/api/tests/music/test_tasks.py index edb937d81..573f04f1f 100644 --- a/api/tests/music/test_tasks.py +++ b/api/tests/music/test_tasks.py @@ -660,7 +660,7 @@ def test_federation_audio_track_to_metadata(now, mocker): }, } ], - "cover": { + "image": { "type": "Link", "href": "http://cover.test", "mediaType": "image/png", @@ -713,8 +713,8 @@ def test_federation_audio_track_to_metadata(now, mocker): "tags": ["AlbumTag"], "description": {"content_type": "text/plain", "text": "album desc"}, "cover_data": { - "mimetype": serializer.validated_data["album"]["cover"]["mediaType"], - "url": serializer.validated_data["album"]["cover"]["href"], + "mimetype": serializer.validated_data["album"]["image"]["mediaType"], + "url": serializer.validated_data["album"]["image"]["href"], }, "artists": [ { diff --git a/api/tests/music/test_views.py b/api/tests/music/test_views.py index 7e96f46c0..81988a6b3 100644 --- a/api/tests/music/test_views.py +++ b/api/tests/music/test_views.py @@ -1307,9 +1307,7 @@ def test_get_upload_audio_metadata(logged_in_api_client, factories): assert response.data == serializer.validated_data -@pytest.mark.parametrize("use_fts", [True, False]) -def test_search_get(use_fts, settings, logged_in_api_client, factories): - settings.USE_FULL_TEXT_SEARCH = use_fts +def test_search_get(logged_in_api_client, factories): artist = factories["music.Artist"](name="Foo Fighters") album = factories["music.Album"](title="Foo Bar") track = factories["music.Track"](title="Foo Baz") @@ -1332,8 +1330,7 @@ def test_search_get(use_fts, settings, logged_in_api_client, factories): assert response.data == expected -def test_search_get_fts_advanced(settings, logged_in_api_client, factories): - settings.USE_FULL_TEXT_SEARCH = True +def test_search_get_fts_advanced(logged_in_api_client, factories): artist1 = factories["music.Artist"](name="Foo Bighters") artist2 = factories["music.Artist"](name="Bar Fighter") factories["music.Artist"]() @@ -1353,8 +1350,7 @@ def test_search_get_fts_advanced(settings, logged_in_api_client, factories): assert response.data == expected -def test_search_get_fts_stop_words(settings, logged_in_api_client, factories): - settings.USE_FULL_TEXT_SEARCH = True +def test_search_get_fts_stop_words(logged_in_api_client, factories): artist = factories["music.Artist"](name="she") factories["music.Artist"](name="something else") diff --git a/docs/api/definitions.yml b/docs/api/definitions.yml index 249eb7a75..71b9893c9 100644 --- a/docs/api/definitions.yml +++ b/docs/api/definitions.yml @@ -720,7 +720,7 @@ User: type: "string" example: "Alice Kingsley" avatar: - $ref: "#/Avatar" + $ref: "#/Attachment" Me: type: "object" @@ -767,30 +767,8 @@ Me: via request headers isn't possible. The token expires after 3 days by default. -Avatar: - type: "object" - properties: - original: - type: "string" - format: "uri" - description: "Original image URL" - example: "http://yourinstance/media/users/avatars/92/49/60/b3c-4736-43b3-bb5c-ed7a99ac6996.jpg" - square_crop: - type: "string" - format: "uri" - description: "400x400 thumbnail URL" - example: "http://yourinstance/media/__sized__/users/avatars/92/49/60/b3c-4736-43b3-bb5c-ed7a99ac6996-crop-c0-5__0-5-400x400-70.jpg" - small_square_crop: - type: "string" - format: "uri" - description: "50x50 thumbnail URL" - example: "http://yourinstance/media/__sized__/users/avatars/92/49/60/b3c-4736-43b3-bb5c-ed7a99ac6996-crop-c0-5__0-5-50x50-70.jpg" - medium_square_crop: - type: "string" - format: "uri" - description: "200x200 thumbnail URL" - example: "http://yourinstance/media/__sized__/users/avatars/92/49/60/b3c-4736-43b3-bb5c-ed7a99ac6996-crop-c0-5__0-5-200x200-70.jpg" -QuotaStatus: + + QuotaStatus: type: "object" properties: max: diff --git a/docs/swagger.yml b/docs/swagger.yml index 0c3a0b1d9..4da3a52b4 100644 --- a/docs/swagger.yml +++ b/docs/swagger.yml @@ -296,7 +296,7 @@ paths: responses: 200: $ref: "#/responses/200" - /api/v1/users/users/me/: + /api/v1/users/me/: get: summary: Retrive profile information description: | diff --git a/front/src/EmbedFrame.vue b/front/src/EmbedFrame.vue index 67dc02303..93d04d5e7 100644 --- a/front/src/EmbedFrame.vue +++ b/front/src/EmbedFrame.vue @@ -337,7 +337,7 @@ export default { }, getCover(albumCover) { if (albumCover) { - return albumCover.medium_square_crop + return albumCover.urls.medium_square_crop } }, getSources (uploads) { diff --git a/front/src/components/Queue.vue b/front/src/components/Queue.vue index 4df47954e..8966c889c 100644 --- a/front/src/components/Queue.vue +++ b/front/src/components/Queue.vue @@ -6,8 +6,8 @@