From 8e68e69f930fa93dea6ecaa1df46d16d2c971e0a Mon Sep 17 00:00:00 2001
From: Eliot Berriot <contact@eliotberriot.com>
Date: Tue, 24 Sep 2019 17:33:07 +0200
Subject: [PATCH] Added api endpoint, front-end code and plugin hook for
 handling now playing track

---
 api/funkwhale_api/history/serializers.py |  7 +++++++
 api/funkwhale_api/history/signals.py     |  4 ++++
 api/funkwhale_api/history/views.py       | 19 +++++++++++++++++--
 api/tests/conftest.py                    |  6 ++++++
 api/tests/history/test_views.py          | 19 +++++++++++++++++++
 front/src/components/audio/Player.vue    | 14 +++++++++++++-
 front/src/store/player.js                |  8 ++++++++
 7 files changed, 74 insertions(+), 3 deletions(-)

diff --git a/api/funkwhale_api/history/serializers.py b/api/funkwhale_api/history/serializers.py
index c61fda6620..41b498291f 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 ad33d878c2..bdfbd225f5 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 6cdbc8a80f..97c1fe0aaf 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 82f05e9f39..98313c663b 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 8ec9277103..675b18d9dd 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 7fda9520f4..7349220eec 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 6757f0beef..c02e7e45e6 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
-- 
GitLab