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') }}