diff --git a/api/funkwhale_api/activity/serializers.py b/api/funkwhale_api/activity/serializers.py
index 4b40bb0d2804622772aeb5af9d445a75a570779e..325d1e820db5699abca69b57b3421b0e0ca1d68b 100644
--- a/api/funkwhale_api/activity/serializers.py
+++ b/api/funkwhale_api/activity/serializers.py
@@ -3,6 +3,7 @@ from rest_framework import serializers
class ModelSerializer(serializers.ModelSerializer):
id = serializers.CharField(source='get_activity_url')
+ local_id = serializers.IntegerField(source='id')
# url = serializers.SerializerMethodField()
def get_url(self, obj):
diff --git a/api/funkwhale_api/favorites/serializers.py b/api/funkwhale_api/favorites/serializers.py
index 01ad2e475a9165253876a029f38d85ebee3725d0..276b0f6bde6d19120998a21f58f44a5dd3ffbd01 100644
--- a/api/funkwhale_api/favorites/serializers.py
+++ b/api/funkwhale_api/favorites/serializers.py
@@ -4,17 +4,15 @@ from rest_framework import serializers
from funkwhale_api.activity import serializers as activity_serializers
from funkwhale_api.music.serializers import TrackSerializerNested
+from funkwhale_api.music.serializers import TrackActivitySerializer
from funkwhale_api.users.serializers import UserActivitySerializer
from . import models
-
-
-
class TrackFavoriteActivitySerializer(activity_serializers.ModelSerializer):
type = serializers.SerializerMethodField()
- object = serializers.CharField(source='track.get_activity_url')
+ object = TrackActivitySerializer(source='track')
actor = UserActivitySerializer(source='user')
published = serializers.DateTimeField(source='creation_date')
@@ -22,6 +20,7 @@ class TrackFavoriteActivitySerializer(activity_serializers.ModelSerializer):
model = models.TrackFavorite
fields = [
'id',
+ 'local_id',
'object',
'type',
'actor',
@@ -34,9 +33,6 @@ class TrackFavoriteActivitySerializer(activity_serializers.ModelSerializer):
def get_type(self, obj):
return 'Like'
- def get_object(self, obj):
- return obj.track.get_activity_url()
-
class UserTrackFavoriteSerializer(serializers.ModelSerializer):
# track = TrackSerializerNested(read_only=True)
diff --git a/api/funkwhale_api/history/activities.py b/api/funkwhale_api/history/activities.py
new file mode 100644
index 0000000000000000000000000000000000000000..e478f9b7f677a4d4e524e343e84b0a08420bbcb3
--- /dev/null
+++ b/api/funkwhale_api/history/activities.py
@@ -0,0 +1,19 @@
+from funkwhale_api.common import channels
+from funkwhale_api.activity import record
+
+from . import serializers
+
+record.registry.register_serializer(
+ serializers.ListeningActivitySerializer)
+
+
+@record.registry.register_consumer('history.Listening')
+def broadcast_listening_to_instance_activity(data, obj):
+ if obj.user.privacy_level not in ['instance', 'everyone']:
+ return
+
+ channels.group_send('instance_activity', {
+ 'type': 'event.send',
+ 'text': '',
+ 'data': data
+ })
diff --git a/api/funkwhale_api/history/models.py b/api/funkwhale_api/history/models.py
index f7f62de62a2f59a08edd9f0a9be28bed65c102fb..56310ddc0d2546784bed02952fe6142a2d139858 100644
--- a/api/funkwhale_api/history/models.py
+++ b/api/funkwhale_api/history/models.py
@@ -25,3 +25,8 @@ class Listening(models.Model):
raise ValidationError('Cannot have both session_key and user empty for listening')
super().save(**kwargs)
+
+
+ def get_activity_url(self):
+ return '{}/listenings/tracks/{}'.format(
+ self.user.get_activity_url(), self.pk)
diff --git a/api/funkwhale_api/history/serializers.py b/api/funkwhale_api/history/serializers.py
index 64bdf41c67164b3e8cd18551be557771909845f2..7a2280cea2a236357982f309b7218c3e0d073299 100644
--- a/api/funkwhale_api/history/serializers.py
+++ b/api/funkwhale_api/history/serializers.py
@@ -1,9 +1,37 @@
from rest_framework import serializers
+from funkwhale_api.activity import serializers as activity_serializers
from funkwhale_api.music.serializers import TrackSerializerNested
+from funkwhale_api.music.serializers import TrackActivitySerializer
+from funkwhale_api.users.serializers import UserActivitySerializer
+
from . import models
+class ListeningActivitySerializer(activity_serializers.ModelSerializer):
+ type = serializers.SerializerMethodField()
+ object = TrackActivitySerializer(source='track')
+ actor = UserActivitySerializer(source='user')
+ published = serializers.DateTimeField(source='end_date')
+
+ class Meta:
+ model = models.Listening
+ fields = [
+ 'id',
+ 'local_id',
+ 'object',
+ 'type',
+ 'actor',
+ 'published'
+ ]
+
+ def get_actor(self, obj):
+ return UserActivitySerializer(obj.user).data
+
+ def get_type(self, obj):
+ return 'Listen'
+
+
class ListeningSerializer(serializers.ModelSerializer):
class Meta:
diff --git a/api/funkwhale_api/history/views.py b/api/funkwhale_api/history/views.py
index 59dcbd26b8449abef324273219a961d44f39f489..d5cbe316ba88b455755cdbaf843086f7c99c09f3 100644
--- a/api/funkwhale_api/history/views.py
+++ b/api/funkwhale_api/history/views.py
@@ -3,8 +3,9 @@ from rest_framework import status
from rest_framework.response import Response
from rest_framework.decorators import detail_route
-from funkwhale_api.music.serializers import TrackSerializerNested
+from funkwhale_api.activity import record
from funkwhale_api.common.permissions import ConditionalAuthentication
+from funkwhale_api.music.serializers import TrackSerializerNested
from . import models
from . import serializers
@@ -17,6 +18,12 @@ class ListeningViewSet(mixins.CreateModelMixin,
queryset = models.Listening.objects.all()
permission_classes = [ConditionalAuthentication]
+ def perform_create(self, serializer):
+ r = super().perform_create(serializer)
+ if self.request.user.is_authenticated:
+ record.send(serializer.instance)
+ return r
+
def get_queryset(self):
queryset = super().get_queryset()
if self.request.user.is_authenticated:
diff --git a/api/funkwhale_api/music/serializers.py b/api/funkwhale_api/music/serializers.py
index db6298a9e446eddc64bc17f95e3a6b75bf126573..48419bbe45675aad4e6ad271e9be420219f7d671 100644
--- a/api/funkwhale_api/music/serializers.py
+++ b/api/funkwhale_api/music/serializers.py
@@ -1,6 +1,8 @@
from rest_framework import serializers
from taggit.models import Tag
+from funkwhale_api.activity import serializers as activity_serializers
+
from . import models
@@ -127,3 +129,24 @@ class ImportBatchSerializer(serializers.ModelSerializer):
model = models.ImportBatch
fields = ('id', 'jobs', 'status', 'creation_date', 'import_request')
read_only_fields = ('creation_date',)
+
+
+class TrackActivitySerializer(activity_serializers.ModelSerializer):
+ type = serializers.SerializerMethodField()
+ name = serializers.CharField(source='title')
+ artist = serializers.CharField(source='artist.name')
+ album = serializers.CharField(source='album.title')
+
+ class Meta:
+ model = models.Track
+ fields = [
+ 'id',
+ 'local_id',
+ 'name',
+ 'type',
+ 'artist',
+ 'album',
+ ]
+
+ def get_type(self, obj):
+ return 'Audio'
diff --git a/api/funkwhale_api/users/serializers.py b/api/funkwhale_api/users/serializers.py
index 2e873d94c6c3e961a3dbad8129ab1473fc55728e..e8adf9edac8e436c8741dbbff9cbff500fd90064 100644
--- a/api/funkwhale_api/users/serializers.py
+++ b/api/funkwhale_api/users/serializers.py
@@ -8,11 +8,13 @@ from . import models
class UserActivitySerializer(activity_serializers.ModelSerializer):
type = serializers.SerializerMethodField()
name = serializers.CharField(source='username')
+ local_id = serializers.CharField(source='username')
class Meta:
model = models.User
fields = [
'id',
+ 'local_id',
'name',
'type'
]
diff --git a/api/tests/favorites/test_activity.py b/api/tests/favorites/test_activity.py
index 74695ed8dbbf1c6fdf3e288e34eb9482b14b3016..63174f9e2693a3fb0923c1aa0c1b682a06b2226a 100644
--- a/api/tests/favorites/test_activity.py
+++ b/api/tests/favorites/test_activity.py
@@ -1,4 +1,5 @@
from funkwhale_api.users.serializers import UserActivitySerializer
+from funkwhale_api.music.serializers import TrackActivitySerializer
from funkwhale_api.favorites import serializers
from funkwhale_api.favorites import activities
@@ -18,9 +19,10 @@ def test_activity_favorite_serializer(factories):
field = serializers.serializers.DateTimeField()
expected = {
"type": "Like",
+ "local_id": favorite.pk,
"id": favorite.get_activity_url(),
"actor": actor,
- "object": favorite.track.get_activity_url(),
+ "object": TrackActivitySerializer(favorite.track).data,
"published": field.to_representation(favorite.creation_date),
}
@@ -48,7 +50,8 @@ def test_broadcast_track_favorite_to_instance_activity(
data = serializers.TrackFavoriteActivitySerializer(favorite).data
consumer = activities.broadcast_track_favorite_to_instance_activity
message = {
- "type": 'event',
+ "type": 'event.send',
+ "text": '',
"data": data
}
consumer(data=data, obj=favorite)
@@ -64,7 +67,8 @@ def test_broadcast_track_favorite_to_instance_activity_private(
data = serializers.TrackFavoriteActivitySerializer(favorite).data
consumer = activities.broadcast_track_favorite_to_instance_activity
message = {
- "type": 'event',
+ "type": 'event.send',
+ "text": '',
"data": data
}
consumer(data=data, obj=favorite)
diff --git a/api/tests/history/test_activity.py b/api/tests/history/test_activity.py
new file mode 100644
index 0000000000000000000000000000000000000000..b5ab07b8235f12045f5ee1b9fba3fce0c3da57d5
--- /dev/null
+++ b/api/tests/history/test_activity.py
@@ -0,0 +1,75 @@
+from funkwhale_api.users.serializers import UserActivitySerializer
+from funkwhale_api.music.serializers import TrackActivitySerializer
+from funkwhale_api.history import serializers
+from funkwhale_api.history import activities
+
+
+def test_get_listening_activity_url(settings, factories):
+ listening = factories['history.Listening']()
+ user_url = listening.user.get_activity_url()
+ expected = '{}/listenings/tracks/{}'.format(
+ user_url, listening.pk)
+ assert listening.get_activity_url() == expected
+
+
+def test_activity_listening_serializer(factories):
+ listening = factories['history.Listening']()
+
+ actor = UserActivitySerializer(listening.user).data
+ field = serializers.serializers.DateTimeField()
+ expected = {
+ "type": "Listen",
+ "local_id": listening.pk,
+ "id": listening.get_activity_url(),
+ "actor": actor,
+ "object": TrackActivitySerializer(listening.track).data,
+ "published": field.to_representation(listening.end_date),
+ }
+
+ data = serializers.ListeningActivitySerializer(listening).data
+
+ assert data == expected
+
+
+def test_track_listening_serializer_is_connected(activity_registry):
+ conf = activity_registry['history.Listening']
+ assert conf['serializer'] == serializers.ListeningActivitySerializer
+
+
+def test_track_listening_serializer_instance_activity_consumer(
+ activity_registry):
+ conf = activity_registry['history.Listening']
+ consumer = activities.broadcast_listening_to_instance_activity
+ assert consumer in conf['consumers']
+
+
+def test_broadcast_listening_to_instance_activity(
+ factories, mocker):
+ p = mocker.patch('funkwhale_api.common.channels.group_send')
+ listening = factories['history.Listening']()
+ data = serializers.ListeningActivitySerializer(listening).data
+ consumer = activities.broadcast_listening_to_instance_activity
+ message = {
+ "type": 'event.send',
+ "text": '',
+ "data": data
+ }
+ consumer(data=data, obj=listening)
+ p.assert_called_once_with('instance_activity', message)
+
+
+def test_broadcast_listening_to_instance_activity_private(
+ factories, mocker):
+ p = mocker.patch('funkwhale_api.common.channels.group_send')
+ listening = factories['history.Listening'](
+ user__privacy_level='me'
+ )
+ data = serializers.ListeningActivitySerializer(listening).data
+ consumer = activities.broadcast_listening_to_instance_activity
+ message = {
+ "type": 'event.send',
+ "text": '',
+ "data": data
+ }
+ consumer(data=data, obj=listening)
+ p.assert_not_called()
diff --git a/api/tests/test_history.py b/api/tests/history/test_history.py
similarity index 70%
rename from api/tests/test_history.py
rename to api/tests/history/test_history.py
index 113e5ff6405f60665305f04b379997e8b9343d9c..ec8689e9637ca2686452e72d9628a581251e014f 100644
--- a/api/tests/test_history.py
+++ b/api/tests/history/test_history.py
@@ -28,7 +28,8 @@ def test_anonymous_user_can_create_listening_via_api(client, factories, settings
assert listening.session_key == client.session.session_key
-def test_logged_in_user_can_create_listening_via_api(logged_in_client, factories):
+def test_logged_in_user_can_create_listening_via_api(
+ logged_in_client, factories, activity_muted):
track = factories['music.Track']()
url = reverse('api:v1:history:listenings-list')
@@ -40,3 +41,17 @@ def test_logged_in_user_can_create_listening_via_api(logged_in_client, factories
assert listening.track == track
assert listening.user == logged_in_client.user
+
+
+def test_adding_listening_calls_activity_record(
+ factories, logged_in_client, activity_muted):
+ track = factories['music.Track']()
+
+ url = reverse('api:v1:history:listenings-list')
+ response = logged_in_client.post(url, {
+ 'track': track.pk,
+ })
+
+ listening = models.Listening.objects.latest('id')
+
+ activity_muted.assert_called_once_with(listening)
diff --git a/api/tests/users/test_activity.py b/api/tests/users/test_activity.py
index 3cee4fb4092699125d7ab3bb4a3d32923cb12769..26d0b11f8ab42ff7644e688a92e4693f755ff054 100644
--- a/api/tests/users/test_activity.py
+++ b/api/tests/users/test_activity.py
@@ -13,6 +13,7 @@ def test_activity_user_serializer(factories):
expected = {
"type": "Person",
"id": user.get_activity_url(),
+ "local_id": user.username,
"name": user.username,
}