diff --git a/api/funkwhale_api/history/serializers.py b/api/funkwhale_api/history/serializers.py index c61fda662..41b498291 100644 --- a/api/funkwhale_api/history/serializers.py +++ b/api/funkwhale_api/history/serializers.py @@ -4,6 +4,7 @@ from funkwhale_api import plugins from funkwhale_api.activity import serializers as activity_serializers from funkwhale_api.common import utils from funkwhale_api.federation import serializers as federation_serializers +from funkwhale_api.music import models as music_models from funkwhale_api.music.serializers import TrackActivitySerializer, TrackSerializer from funkwhale_api.users.serializers import UserActivitySerializer, UserBasicSerializer @@ -64,3 +65,9 @@ class ListeningWriteSerializer(serializers.ModelSerializer): plugins_conf=plugins_conf, ) return instance + + +class NowSerializer(serializers.Serializer): + track = serializers.PrimaryKeyRelatedField( + queryset=music_models.Track.objects.all() + ) diff --git a/api/funkwhale_api/history/signals.py b/api/funkwhale_api/history/signals.py index ad33d878c..bdfbd225f 100644 --- a/api/funkwhale_api/history/signals.py +++ b/api/funkwhale_api/history/signals.py @@ -4,3 +4,7 @@ from funkwhale_api import plugins plugins.hooks.register( plugins.Hook("history.listening.created", providing_args=["listening"]) ) + +plugins.hooks.register( + plugins.Hook("history.listening.now", providing_args=["track", "user"]) +) diff --git a/api/funkwhale_api/history/views.py b/api/funkwhale_api/history/views.py index 6cdbc8a80..97c1fe0aa 100644 --- a/api/funkwhale_api/history/views.py +++ b/api/funkwhale_api/history/views.py @@ -1,9 +1,11 @@ -from rest_framework import mixins, viewsets +from rest_framework import mixins, viewsets, response +from rest_framework.decorators import action from django.db.models import Prefetch +from funkwhale_api import plugins from funkwhale_api.activity import record -from funkwhale_api.common import fields, permissions +from funkwhale_api.common import fields, permissions, utils from funkwhale_api.music.models import Track from funkwhale_api.music import utils as music_utils from . import filters, models, serializers @@ -54,3 +56,16 @@ class ListeningViewSet( context = super().get_serializer_context() context["user"] = self.request.user return context + + @action(methods=["post"], detail=False) + def now(self, request, *args, **kwargs): + serializer = serializers.NowSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + utils.on_commit( + plugins.hooks.dispatch, + "history.listening.now", + user=request.user, + track=serializer.validated_data["track"], + plugins_conf=request.plugins_conf, + ) + return response.Response({}, status=204) diff --git a/api/tests/conftest.py b/api/tests/conftest.py index 82f05e9f3..98313c663 100644 --- a/api/tests/conftest.py +++ b/api/tests/conftest.py @@ -437,3 +437,9 @@ def plugin_class(): def plugin(plugin_class): return plugin_class("test", "test") + + +@pytest.fixture +def plugins_conf(mocker): + plugins_conf = mocker.patch("funkwhale_api.plugins.generate_plugins_conf") + return plugins_conf.return_value diff --git a/api/tests/history/test_views.py b/api/tests/history/test_views.py index 8ec927710..675b18d9d 100644 --- a/api/tests/history/test_views.py +++ b/api/tests/history/test_views.py @@ -2,6 +2,8 @@ import pytest from django.urls import reverse +from funkwhale_api import plugins + @pytest.mark.parametrize("level", ["instance", "me", "followers"]) def test_privacy_filter(preferences, level, factories, api_client): @@ -11,3 +13,20 @@ def test_privacy_filter(preferences, level, factories, api_client): response = api_client.get(url) assert response.status_code == 200 assert response.data["count"] == 0 + + +def test_now(factories, logged_in_api_client, plugins_conf, mocker): + track = factories["music.Track"]() + url = reverse("api:v1:history:listenings-now") + on_commit = mocker.patch("funkwhale_api.common.utils.on_commit") + response = logged_in_api_client.post(url, {"track": track.pk}) + + on_commit.assert_called_once_with( + plugins.hooks.dispatch, + "history.listening.now", + track=track, + user=logged_in_api_client.user, + plugins_conf=plugins_conf, + ) + + assert response.status_code == 204 diff --git a/front/src/components/audio/Player.vue b/front/src/components/audio/Player.vue index 7fda9520f..7349220ee 100644 --- a/front/src/components/audio/Player.vue +++ b/front/src/components/audio/Player.vue @@ -266,7 +266,8 @@ export default { soundsCache: [], soundId: null, playTimeout: null, - nextTrackPreloaded: false + nextTrackPreloaded: false, + nowPlayingTimeout: null, } }, mounted() { @@ -408,6 +409,11 @@ export default { }) }, onplay: function () { + if (trackData.id === self.currentTrack.id) { + self.nowPlayingTimeout = setTimeout(() => { + self.$store.dispatch('player/nowPlaying', trackData) + }, 5000) + } self.$store.commit('player/isLoadingAudio', false) self.$store.commit('player/resetErrorCount') self.$store.commit('player/errored', false) @@ -713,6 +719,9 @@ export default { watch: { currentTrack: { async handler (newValue, oldValue) { + if (this.nowPlayingTimeout) { + clearTimeout(this.nowPlayingTimeout) + } if (newValue === oldValue) { return } @@ -746,6 +755,9 @@ export default { if (newValue === true) { this.soundId = this.currentSound.play(this.soundId) } else { + if (this.nowPlayingTimeout) { + clearTimeout(this.nowPlayingTimeout) + } this.currentSound.pause(this.soundId) } } else { diff --git a/front/src/store/player.js b/front/src/store/player.js index 6757f0bee..c02e7e45e 100644 --- a/front/src/store/player.js +++ b/front/src/store/player.js @@ -126,6 +126,14 @@ export default { logger.default.error('Could not record track in history') }) }, + nowPlaying ({commit, rootState}, trackData) { + if (!rootState.auth.authenticated) { + return + } + return axios.post('history/listenings/now/', {'track': trackData.id}).then((response) => {}, (response) => { + logger.default.error('Could not set track as now playing') + }) + }, trackEnded ({dispatch, rootState}, track) { dispatch('trackListened', track) let queueState = rootState.queue