diff --git a/api/funkwhale_api/playlists/models.py b/api/funkwhale_api/playlists/models.py
index 2370f53c0..968d34273 100644
--- a/api/funkwhale_api/playlists/models.py
+++ b/api/funkwhale_api/playlists/models.py
@@ -307,7 +307,7 @@ class PlaylistTrackQuerySet(models.QuerySet, common_models.LocalFromFidQuerySet)
return self.annotate(is_playable_by_actor=subquery)
def playable_by(self, actor, include=True):
- tracks = music_models.Track.objects.playable_by(actor, include)
+ tracks = music_models.Track.objects.playable_by(actor)
if include:
return self.filter(track__pk__in=tracks).distinct()
else:
diff --git a/api/funkwhale_api/playlists/serializers.py b/api/funkwhale_api/playlists/serializers.py
index c1c87bf35..2435b3973 100644
--- a/api/funkwhale_api/playlists/serializers.py
+++ b/api/funkwhale_api/playlists/serializers.py
@@ -67,8 +67,10 @@ class PlaylistSerializer(serializers.ModelSerializer):
@extend_schema_field(OpenApiTypes.BOOL)
def get_library_followed(self, obj):
- if self.context.get("request", False) and hasattr(
- self.context["request"], "user"
+ if (
+ self.context.get("request", False)
+ and hasattr(self.context["request"], "user")
+ and hasattr(self.context["request"].user, "actor")
):
actor = self.context["request"].user.actor
lib_qs = obj.library.received_follows.filter(actor=actor)
diff --git a/api/funkwhale_api/playlists/views.py b/api/funkwhale_api/playlists/views.py
index 18bcaa2a2..d1a08b792 100644
--- a/api/funkwhale_api/playlists/views.py
+++ b/api/funkwhale_api/playlists/views.py
@@ -130,14 +130,15 @@ class PlaylistViewSet(
@action(methods=["get"], detail=True)
def tracks(self, request, *args, **kwargs):
playlist = self.get_object()
- plts = playlist.playlist_tracks.all().for_nested_serialization(
- music_utils.get_actor_from_request(request)
- )
- plts_without_upload = plts.filter(track__uploads__isnull=True)
- for plt in plts_without_upload[: settings.THIRD_PARTY_UPLOAD_MAX_UPLOADS]:
+ actor = music_utils.get_actor_from_request(request)
+ plts = playlist.playlist_tracks.all().for_nested_serialization(actor)
+ for plt in plts.playable_by(actor, include=False)[
+ : settings.THIRD_PARTY_UPLOAD_MAX_UPLOADS
+ ]:
plugins.trigger_hook(
plugins.TRIGGER_THIRD_PARTY_UPLOAD,
track=plt.track,
+ actor=actor,
)
# Apply pagination
diff --git a/changes/changelog.d/2464.enhancement b/changes/changelog.d/2464.enhancement
new file mode 100644
index 000000000..aecaf3b99
--- /dev/null
+++ b/changes/changelog.d/2464.enhancement
@@ -0,0 +1 @@
+Make playlist detail page reactive to plugin upload updates (#2464)
diff --git a/front/src/components/audio/PlayButton.vue b/front/src/components/audio/PlayButton.vue
index 523cbce71..b1debb5d3 100644
--- a/front/src/components/audio/PlayButton.vue
+++ b/front/src/components/audio/PlayButton.vue
@@ -250,7 +250,7 @@ const playlistLibraryFollowInfo = computed(() => {
void): stopFn
function useWebSocketHandler (eventName: 'user_request.created', handler: (event: PendingReviewRequests) => void): stopFn
function useWebSocketHandler (eventName: 'Listen', handler: (event: unknown) => void): stopFn
+function useWebSocketHandler (eventName: 'playlist.track_updated', handler: (event: {track: Track}) => void): stopFn
function useWebSocketHandler (eventName: string, handler: (event: any) => void): stopFn {
const id = `${+new Date() + Math.random()}`
diff --git a/front/src/store/ui.ts b/front/src/store/ui.ts
index 38eb21701..ad098ee37 100644
--- a/front/src/store/ui.ts
+++ b/front/src/store/ui.ts
@@ -12,7 +12,7 @@ import useLogger from '~/composables/useLogger'
type SupportedExtension = 'flac' | 'ogg' | 'mp3' | 'opus' | 'aac' | 'm4a' | 'aiff' | 'aif'
export type WebSocketEventName = 'inbox.item_added' | 'import.status_updated' | 'mutation.created' | 'mutation.updated'
- | 'report.created' | 'user_request.created' | 'Listen'
+ | 'report.created' | 'user_request.created' | 'Listen' | 'playlist.track_updated'
export type OrderingField = 'creation_date' | 'title' | 'album__title' | 'artist__name' | 'release_date' | 'name'
| 'applied_date' | 'followers_count' | 'uploads_count' | 'length' | 'items_count' | 'modification_date' | 'size'
@@ -91,7 +91,8 @@ const store: Module = {
'mutation.updated': {},
'report.created': {},
'user_request.created': {},
- Listen: {}
+ Listen: {},
+ 'playlist.track_updated': {}
},
pageTitle: null,
modalsOpen: new Set([]),
@@ -273,6 +274,7 @@ const store: Module = {
const handlers = state.websocketEventsHandlers[event.type]
logger.log('Dispatching websocket event', event, handlers)
if (!handlers) {
+ logger.log('No websocket handlers for this event', event, handlers)
return
}
diff --git a/front/src/views/playlists/Detail.vue b/front/src/views/playlists/Detail.vue
index 188b1393d..c98fcc91f 100644
--- a/front/src/views/playlists/Detail.vue
+++ b/front/src/views/playlists/Detail.vue
@@ -24,6 +24,7 @@ import Alert from '~/components/ui/Alert.vue'
import PlaylistDropdown from '~/components/playlists/PlaylistDropdown.vue'
import useErrorHandler from '~/composables/useErrorHandler'
+import useWebSocketHandler from '~/composables/useWebSocketHandler'
// TODO: Is this event ever caught somewhere?
// interface Events {
@@ -51,6 +52,15 @@ const fullPlaylistTracks = ref([])
const tracks = computed(() => fullPlaylistTracks.value.map(({ track }, index) => ({ ...track as Track, position: index + 1 })))
+const updateTrack = (updatedTrack: Track) => {
+ fullPlaylistTracks.value = fullPlaylistTracks.value.map((item) =>
+ item.track.id === updatedTrack.id ? { ...item, track: updatedTrack } : item
+ );
+};
+useWebSocketHandler('playlist.track_updated', async (event) => {
+ updateTrack(event.track);
+});
+
const { t } = useI18n()
const labels = computed(() => ({
playlist: t('views.playlists.Detail.title')
@@ -203,6 +213,7 @@ const shuffle = () => {}
low-height
:is-playable="true"
:tracks="tracks"
+ :playlist="playlist"
>
{{ t('views.playlists.Detail.button.playAll') }}