From 0f4226e06fa3ce888df04666dd02c150d4380837 Mon Sep 17 00:00:00 2001 From: petitminion Date: Fri, 15 Jul 2022 09:07:15 +0000 Subject: [PATCH] Resolve "Add "play all" button in tag search result page" --- .../migrations/0006_radiosession_config.py | 20 ++++ .../migrations/0007_merge_20220715_0801.py | 14 +++ api/funkwhale_api/radios/models.py | 2 + api/funkwhale_api/radios/radios.py | 34 +++++++ api/funkwhale_api/radios/serializers.py | 1 + api/tests/radios/test_radios.py | 16 ++++ api/tests/radios/test_serializers.py | 1 + changes/changelog.d/1563-enhancement | 1 + front/src/components/radios/Button.vue | 36 ++++--- front/src/store/radios.js | 7 +- front/src/views/Search.vue | 93 ++++++++++++++----- 11 files changed, 187 insertions(+), 38 deletions(-) create mode 100644 api/funkwhale_api/radios/migrations/0006_radiosession_config.py create mode 100644 api/funkwhale_api/radios/migrations/0007_merge_20220715_0801.py create mode 100644 changes/changelog.d/1563-enhancement diff --git a/api/funkwhale_api/radios/migrations/0006_radiosession_config.py b/api/funkwhale_api/radios/migrations/0006_radiosession_config.py new file mode 100644 index 000000000..944886b9b --- /dev/null +++ b/api/funkwhale_api/radios/migrations/0006_radiosession_config.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.10 on 2022-01-21 11:56 + +import django.contrib.postgres.fields.jsonb +import django.core.serializers.json +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('radios', '0005_auto_20200803_1222'), + ] + + operations = [ + migrations.AddField( + model_name='radiosession', + name='config', + field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, encoder=django.core.serializers.json.DjangoJSONEncoder, null=True), + ), + ] diff --git a/api/funkwhale_api/radios/migrations/0007_merge_20220715_0801.py b/api/funkwhale_api/radios/migrations/0007_merge_20220715_0801.py new file mode 100644 index 000000000..15c05ac9f --- /dev/null +++ b/api/funkwhale_api/radios/migrations/0007_merge_20220715_0801.py @@ -0,0 +1,14 @@ +# Generated by Django 3.2.13 on 2022-07-15 08:01 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('radios', '0006_alter_radio_config'), + ('radios', '0006_radiosession_config'), + ] + + operations = [ + ] diff --git a/api/funkwhale_api/radios/models.py b/api/funkwhale_api/radios/models.py index 8cb56cb92..6439e25db 100644 --- a/api/funkwhale_api/radios/models.py +++ b/api/funkwhale_api/radios/models.py @@ -51,6 +51,8 @@ class RadioSession(models.Model): related_object = GenericForeignKey( "related_object_content_type", "related_object_id" ) + CONFIG_VERSION = 0 + config = JSONField(encoder=DjangoJSONEncoder, blank=True, null=True) def save(self, **kwargs): self.radio.clean(self) diff --git a/api/funkwhale_api/radios/radios.py b/api/funkwhale_api/radios/radios.py index 065d51848..126dac672 100644 --- a/api/funkwhale_api/radios/radios.py +++ b/api/funkwhale_api/radios/radios.py @@ -1,4 +1,5 @@ import datetime +import logging import random from django.core.exceptions import ValidationError @@ -15,6 +16,8 @@ from funkwhale_api.tags.models import Tag from . import filters, models from .registries import registry +logger = logging.getLogger(__name__) + class SimpleRadio(object): related_object_field = None @@ -148,6 +151,37 @@ class CustomRadio(SessionRadio): return data +@registry.register(name="custom_multiple") +class CustomMultiple(SessionRadio): + """ + Receive a vuejs generated config and use it to launch a radio session + """ + + config = serializers.JSONField(required=True) + + def get_config(self, data): + return data["config"] + + def get_queryset_kwargs(self): + kwargs = super().get_queryset_kwargs() + kwargs["config"] = self.session.config + return kwargs + + def validate_session(self, data, **context): + data = super().validate_session(data, **context) + try: + data["config"] is not None + except KeyError: + raise serializers.ValidationError( + "You must provide a configuration for this radio" + ) + return data + + def get_queryset(self, **kwargs): + qs = super().get_queryset(**kwargs) + return filters.run(kwargs["config"], candidates=qs) + + class RelatedObjectRadio(SessionRadio): """Abstract radio related to an object (tag, artist, user...)""" diff --git a/api/funkwhale_api/radios/serializers.py b/api/funkwhale_api/radios/serializers.py index 65e48449a..dc5d6c908 100644 --- a/api/funkwhale_api/radios/serializers.py +++ b/api/funkwhale_api/radios/serializers.py @@ -66,6 +66,7 @@ class RadioSessionSerializer(serializers.ModelSerializer): "user", "creation_date", "custom_radio", + "config", ) def validate(self, data): diff --git a/api/tests/radios/test_radios.py b/api/tests/radios/test_radios.py index f64fcac5f..0e2b47f60 100644 --- a/api/tests/radios/test_radios.py +++ b/api/tests/radios/test_radios.py @@ -413,3 +413,19 @@ def test_get_choices_for_custom_radio_exclude_tag(factories): expected = [u.track.pk for u in included_uploads] assert list(choices.values_list("id", flat=True)) == expected + + +def test_can_start_custom_multiple_radio_from_api(api_client, factories): + tracks = factories["music.Track"].create_batch(5) + url = reverse("api:v1:radios:sessions-list") + map_filters_to_type = {"tags": "names", "artists": "ids"} + for (key, value) in map_filters_to_type.items(): + attr = value[:-1] + track_filter_key = [getattr(a.artist, attr) for a in tracks] + config = {"filters": [{"type": key, value: track_filter_key}]} + response = api_client.post( + url, + {"radio_type": "custom_multiple", "config": config}, + format="json", + ) + assert response.status_code == 201 diff --git a/api/tests/radios/test_serializers.py b/api/tests/radios/test_serializers.py index 748bd993a..ef52ad8a6 100644 --- a/api/tests/radios/test_serializers.py +++ b/api/tests/radios/test_serializers.py @@ -40,5 +40,6 @@ def test_tag_radio_repr(factories, to_api_date): "user": session.user.pk, "related_object_id": tag.name, "creation_date": to_api_date(session.creation_date), + "config": None, } assert serializers.RadioSessionSerializer(session).data == expected diff --git a/changes/changelog.d/1563-enhancement b/changes/changelog.d/1563-enhancement new file mode 100644 index 000000000..dd31a1c79 --- /dev/null +++ b/changes/changelog.d/1563-enhancement @@ -0,0 +1 @@ +Adding support for play all radio in search result page (#1563) \ No newline at end of file diff --git a/front/src/components/radios/Button.vue b/front/src/components/radios/Button.vue index 5f36ec488..1d63b5528 100644 --- a/front/src/components/radios/Button.vue +++ b/front/src/components/radios/Button.vue @@ -7,16 +7,7 @@ class="ui feed icon" role="button" /> - - + {{ buttonLabel }} @@ -28,7 +19,8 @@ export default { customRadioId: { type: Number, required: false, default: null }, type: { type: String, required: false, default: '' }, clientOnly: { type: Boolean, default: false }, - objectId: { type: [String, Number, Object], default: null } + objectId: { type: [String, Number, Object], default: null }, + config: { type: [Array, Object], required: false, default: null } }, computed: { running () { @@ -39,6 +31,25 @@ export default { } else { return current.type === this.type && lodash.isEqual(current.objectId, this.objectId) && current.customRadioId === this.customRadioId } + }, + label () { + return this.config?.[0]?.type ?? null + }, + buttonLabel () { + switch (this.label) { + case 'tag': + return this.running + ? this.$pgettext('*/Player/Button.Label/Short, Verb', 'Stop tags radio') + : this.$pgettext('*/Player/Button.Label/Short, Verb', 'Start tags radio') + case 'artist': + return this.running + ? this.$pgettext('*/Player/Button.Label/Short, Verb', 'Stop artists radio') + : this.$pgettext('*/Player/Button.Label/Short, Verb', 'Start artists radio') + default: + return this.running + ? this.$pgettext('*/Player/Button.Label/Short, Verb', 'Stop radio') + : this.$pgettext('*/Queue/Button.Label/Short, Verb', 'Play radio') + } } }, methods: { @@ -50,7 +61,8 @@ export default { type: this.type, objectId: this.objectId, customRadioId: this.customRadioId, - clientOnly: this.clientOnly + clientOnly: this.clientOnly, + config: this.config }) } } diff --git a/front/src/store/radios.js b/front/src/store/radios.js index 3c24f7ed4..6bc5834a4 100644 --- a/front/src/store/radios.js +++ b/front/src/store/radios.js @@ -48,14 +48,15 @@ export default { } }, actions: { - start ({ commit, dispatch }, { type, objectId, customRadioId, clientOnly }) { + start ({ commit, dispatch }, { type, objectId, customRadioId, clientOnly, config }) { const params = { radio_type: type, related_object_id: objectId, - custom_radio: customRadioId + custom_radio: customRadioId, + config: config } if (clientOnly) { - commit('current', { type, objectId, customRadioId, clientOnly }) + commit('current', { type, objectId, customRadioId, clientOnly, config }) commit('running', true) dispatch('populateQueue', true) return diff --git a/front/src/views/Search.vue b/front/src/views/Search.vue index 3ac5152d2..b0c9e08ad 100644 --- a/front/src/views/Search.vue +++ b/front/src/views/Search.vue @@ -23,29 +23,41 @@ Search -
-