diff --git a/api/funkwhale_api/music/mutations.py b/api/funkwhale_api/music/mutations.py
index d95d35702..c26feaec7 100644
--- a/api/funkwhale_api/music/mutations.py
+++ b/api/funkwhale_api/music/mutations.py
@@ -9,6 +9,8 @@ from funkwhale_api.tags import serializers as tags_serializers
from . import models
+NOOP = object()
+
def can_suggest(obj, actor):
return obj.is_local
@@ -34,9 +36,10 @@ class TagMutation(mutations.UpdateMutationSerializer):
return handlers
def update(self, instance, validated_data):
- tags = validated_data.pop("tags", [])
+ tags = validated_data.pop("tags", NOOP)
r = super().update(instance, validated_data)
- tags_models.set_tags(instance, *tags)
+ if tags != NOOP:
+ tags_models.set_tags(instance, *tags)
return r
@@ -53,9 +56,10 @@ class DescriptionMutation(mutations.UpdateMutationSerializer):
return handlers
def update(self, instance, validated_data):
- description = validated_data.pop("description", None)
+ description = validated_data.pop("description", NOOP)
r = super().update(instance, validated_data)
- common_utils.attach_content(instance, "description", description)
+ if description != NOOP:
+ common_utils.attach_content(instance, "description", description)
return r
diff --git a/api/funkwhale_api/playlists/filters.py b/api/funkwhale_api/playlists/filters.py
index 43029a360..f49e9bd1a 100644
--- a/api/funkwhale_api/playlists/filters.py
+++ b/api/funkwhale_api/playlists/filters.py
@@ -2,6 +2,7 @@ from django.db.models import Count
from django_filters import rest_framework as filters
from funkwhale_api.common import filters as common_filters
+from funkwhale_api.music import models as music_models
from funkwhale_api.music import utils
from . import models
@@ -10,6 +11,21 @@ from . import models
class PlaylistFilter(filters.FilterSet):
q = filters.CharFilter(field_name="_", method="filter_q")
playable = filters.BooleanFilter(field_name="_", method="filter_playable")
+ track = filters.ModelChoiceFilter(
+ "playlist_tracks__track",
+ queryset=music_models.Track.objects.all(),
+ distinct=True,
+ )
+ album = filters.ModelChoiceFilter(
+ "playlist_tracks__track__album",
+ queryset=music_models.Album.objects.all(),
+ distinct=True,
+ )
+ artist = filters.ModelChoiceFilter(
+ "playlist_tracks__track__artist",
+ queryset=music_models.Artist.objects.all(),
+ distinct=True,
+ )
scope = common_filters.ActorScopeFilter(actor_field="user__actor", distinct=True)
class Meta:
diff --git a/api/tests/music/test_mutations.py b/api/tests/music/test_mutations.py
index 14e71494c..93e77d114 100644
--- a/api/tests/music/test_mutations.py
+++ b/api/tests/music/test_mutations.py
@@ -220,3 +220,40 @@ def test_album_mutation_description(factory_name, factories, mocker):
mutation.previous_state["description"]
== common_serializers.ContentSerializer(content).data
)
+
+
+@pytest.mark.parametrize(
+ "factory_name", ["music.Track", "music.Album", "music.Artist"],
+)
+def test_mutation_description_keep_tags(factory_name, factories, mocker):
+ mocker.patch("funkwhale_api.federation.routes.outbox.dispatch")
+ content = factories["common.Content"]()
+ obj = factories[factory_name](description=content, set_tags=["punk", "rock"])
+ mutation = factories["common.Mutation"](
+ type="update",
+ target=obj,
+ payload={"description": {"content_type": "text/plain", "text": "hello there"}},
+ )
+ mutation.apply()
+ obj.refresh_from_db()
+
+ assert obj.description.content_type == "text/plain"
+ assert obj.description.text == "hello there"
+ assert obj.get_tags() == ["punk", "rock"]
+
+
+@pytest.mark.parametrize(
+ "factory_name", ["music.Track", "music.Album", "music.Artist"],
+)
+def test_mutation_tags_keep_descriptions(factory_name, factories, mocker):
+ mocker.patch("funkwhale_api.federation.routes.outbox.dispatch")
+ content = factories["common.Content"]()
+ obj = factories[factory_name](description=content)
+ mutation = factories["common.Mutation"](
+ type="update", target=obj, payload={"tags": ["punk", "rock"]},
+ )
+ mutation.apply()
+ obj.refresh_from_db()
+
+ assert obj.description == content
+ assert obj.get_tags() == ["punk", "rock"]
diff --git a/api/tests/playlists/test_filters.py b/api/tests/playlists/test_filters.py
new file mode 100644
index 000000000..ad6a7e2f6
--- /dev/null
+++ b/api/tests/playlists/test_filters.py
@@ -0,0 +1,29 @@
+from funkwhale_api.playlists import filters
+from funkwhale_api.playlists import models
+
+
+def test_playlist_filter_track(factories, queryset_equal_list):
+ plt = factories["playlists.PlaylistTrack"]()
+ factories["playlists.PlaylistTrack"]()
+ qs = models.Playlist.objects.all()
+ filterset = filters.PlaylistFilter({"track": plt.track.pk}, queryset=qs)
+
+ assert filterset.qs == [plt.playlist]
+
+
+def test_playlist_filter_album(factories, queryset_equal_list):
+ plt = factories["playlists.PlaylistTrack"]()
+ factories["playlists.PlaylistTrack"]()
+ qs = models.Playlist.objects.all()
+ filterset = filters.PlaylistFilter({"album": plt.track.album.pk}, queryset=qs)
+
+ assert filterset.qs == [plt.playlist]
+
+
+def test_playlist_filter_artist(factories, queryset_equal_list):
+ plt = factories["playlists.PlaylistTrack"]()
+ factories["playlists.PlaylistTrack"]()
+ qs = models.Playlist.objects.all()
+ filterset = filters.PlaylistFilter({"artist": plt.track.artist.pk}, queryset=qs)
+
+ assert filterset.qs == [plt.playlist]
diff --git a/front/package.json b/front/package.json
index e631ead69..99c143852 100644
--- a/front/package.json
+++ b/front/package.json
@@ -28,6 +28,7 @@
"register-service-worker": "^1.6.2",
"sanitize-html": "^1.20.1",
"showdown": "^1.8.6",
+ "text-clipper": "^1.3.0",
"vue": "^2.6.10",
"vue-gettext": "^2.1.0",
"vue-lazyload": "^1.2.6",
diff --git a/front/src/components/audio/PlayButton.vue b/front/src/components/audio/PlayButton.vue
index b8c1e42fe..62f72bc95 100644
--- a/front/src/components/audio/PlayButton.vue
+++ b/front/src/components/audio/PlayButton.vue
@@ -7,7 +7,7 @@
:disabled="!playable"
:class="buttonClasses.concat(['ui', {loading: isLoading}, {'mini': discrete}, {disabled: !playable}])">
-