enhancement(plugin):make playlist detail reactive to upload upgrades from plugins
This commit is contained in:
parent
5f0414138b
commit
ad9a829af6
|
@ -307,7 +307,7 @@ class PlaylistTrackQuerySet(models.QuerySet, common_models.LocalFromFidQuerySet)
|
||||||
return self.annotate(is_playable_by_actor=subquery)
|
return self.annotate(is_playable_by_actor=subquery)
|
||||||
|
|
||||||
def playable_by(self, actor, include=True):
|
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:
|
if include:
|
||||||
return self.filter(track__pk__in=tracks).distinct()
|
return self.filter(track__pk__in=tracks).distinct()
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -67,8 +67,10 @@ class PlaylistSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
@extend_schema_field(OpenApiTypes.BOOL)
|
@extend_schema_field(OpenApiTypes.BOOL)
|
||||||
def get_library_followed(self, obj):
|
def get_library_followed(self, obj):
|
||||||
if self.context.get("request", False) and hasattr(
|
if (
|
||||||
self.context["request"], "user"
|
self.context.get("request", False)
|
||||||
|
and hasattr(self.context["request"], "user")
|
||||||
|
and hasattr(self.context["request"].user, "actor")
|
||||||
):
|
):
|
||||||
actor = self.context["request"].user.actor
|
actor = self.context["request"].user.actor
|
||||||
lib_qs = obj.library.received_follows.filter(actor=actor)
|
lib_qs = obj.library.received_follows.filter(actor=actor)
|
||||||
|
|
|
@ -130,14 +130,15 @@ class PlaylistViewSet(
|
||||||
@action(methods=["get"], detail=True)
|
@action(methods=["get"], detail=True)
|
||||||
def tracks(self, request, *args, **kwargs):
|
def tracks(self, request, *args, **kwargs):
|
||||||
playlist = self.get_object()
|
playlist = self.get_object()
|
||||||
plts = playlist.playlist_tracks.all().for_nested_serialization(
|
actor = music_utils.get_actor_from_request(request)
|
||||||
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)[
|
||||||
plts_without_upload = plts.filter(track__uploads__isnull=True)
|
: settings.THIRD_PARTY_UPLOAD_MAX_UPLOADS
|
||||||
for plt in plts_without_upload[: settings.THIRD_PARTY_UPLOAD_MAX_UPLOADS]:
|
]:
|
||||||
plugins.trigger_hook(
|
plugins.trigger_hook(
|
||||||
plugins.TRIGGER_THIRD_PARTY_UPLOAD,
|
plugins.TRIGGER_THIRD_PARTY_UPLOAD,
|
||||||
track=plt.track,
|
track=plt.track,
|
||||||
|
actor=actor,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Apply pagination
|
# Apply pagination
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Make playlist detail page reactive to plugin upload updates (#2464)
|
|
@ -250,7 +250,7 @@ const playlistLibraryFollowInfo = computed(() => {
|
||||||
<hr v-if="filterableArtist || Object.keys(getReportableObjects({ track, album, artist, playlist, account, channel })).length > 0">
|
<hr v-if="filterableArtist || Object.keys(getReportableObjects({ track, album, artist, playlist, account, channel })).length > 0">
|
||||||
|
|
||||||
<PopoverItem
|
<PopoverItem
|
||||||
v-if="filterableArtist"
|
v-if="filterableArtist && !props.playlist"
|
||||||
:disabled="!filterableArtist"
|
:disabled="!filterableArtist"
|
||||||
:title="labels.hideArtist"
|
:title="labels.hideArtist"
|
||||||
icon="bi-eye-slash"
|
icon="bi-eye-slash"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import type { Notification } from '~/types'
|
import type { Notification, Track } from '~/types'
|
||||||
|
|
||||||
import store from '~/store'
|
import store from '~/store'
|
||||||
import { tryOnScopeDispose } from '@vueuse/core'
|
import { tryOnScopeDispose } from '@vueuse/core'
|
||||||
|
@ -51,6 +51,7 @@ function useWebSocketHandler (eventName: 'mutation.updated', handler: (event: Pe
|
||||||
function useWebSocketHandler (eventName: 'import.status_updated', handler: (event: ImportStatusWS) => void): stopFn
|
function useWebSocketHandler (eventName: 'import.status_updated', handler: (event: ImportStatusWS) => void): stopFn
|
||||||
function useWebSocketHandler (eventName: 'user_request.created', handler: (event: PendingReviewRequests) => 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: '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 {
|
function useWebSocketHandler (eventName: string, handler: (event: any) => void): stopFn {
|
||||||
const id = `${+new Date() + Math.random()}`
|
const id = `${+new Date() + Math.random()}`
|
||||||
|
|
|
@ -12,7 +12,7 @@ import useLogger from '~/composables/useLogger'
|
||||||
type SupportedExtension = 'flac' | 'ogg' | 'mp3' | 'opus' | 'aac' | 'm4a' | 'aiff' | 'aif'
|
type SupportedExtension = 'flac' | 'ogg' | 'mp3' | 'opus' | 'aac' | 'm4a' | 'aiff' | 'aif'
|
||||||
|
|
||||||
export type WebSocketEventName = 'inbox.item_added' | 'import.status_updated' | 'mutation.created' | 'mutation.updated'
|
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'
|
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'
|
| 'applied_date' | 'followers_count' | 'uploads_count' | 'length' | 'items_count' | 'modification_date' | 'size'
|
||||||
|
@ -91,7 +91,8 @@ const store: Module<State, RootState> = {
|
||||||
'mutation.updated': {},
|
'mutation.updated': {},
|
||||||
'report.created': {},
|
'report.created': {},
|
||||||
'user_request.created': {},
|
'user_request.created': {},
|
||||||
Listen: {}
|
Listen: {},
|
||||||
|
'playlist.track_updated': {}
|
||||||
},
|
},
|
||||||
pageTitle: null,
|
pageTitle: null,
|
||||||
modalsOpen: new Set([]),
|
modalsOpen: new Set([]),
|
||||||
|
@ -273,6 +274,7 @@ const store: Module<State, RootState> = {
|
||||||
const handlers = state.websocketEventsHandlers[event.type]
|
const handlers = state.websocketEventsHandlers[event.type]
|
||||||
logger.log('Dispatching websocket event', event, handlers)
|
logger.log('Dispatching websocket event', event, handlers)
|
||||||
if (!handlers) {
|
if (!handlers) {
|
||||||
|
logger.log('No websocket handlers for this event', event, handlers)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ import Alert from '~/components/ui/Alert.vue'
|
||||||
import PlaylistDropdown from '~/components/playlists/PlaylistDropdown.vue'
|
import PlaylistDropdown from '~/components/playlists/PlaylistDropdown.vue'
|
||||||
|
|
||||||
import useErrorHandler from '~/composables/useErrorHandler'
|
import useErrorHandler from '~/composables/useErrorHandler'
|
||||||
|
import useWebSocketHandler from '~/composables/useWebSocketHandler'
|
||||||
|
|
||||||
// TODO: Is this event ever caught somewhere?
|
// TODO: Is this event ever caught somewhere?
|
||||||
// interface Events {
|
// interface Events {
|
||||||
|
@ -51,6 +52,15 @@ const fullPlaylistTracks = ref<FullPlaylistTrack[]>([])
|
||||||
|
|
||||||
const tracks = computed(() => fullPlaylistTracks.value.map(({ track }, index) => ({ ...track as Track, position: index + 1 })))
|
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 { t } = useI18n()
|
||||||
const labels = computed(() => ({
|
const labels = computed(() => ({
|
||||||
playlist: t('views.playlists.Detail.title')
|
playlist: t('views.playlists.Detail.title')
|
||||||
|
@ -203,6 +213,7 @@ const shuffle = () => {}
|
||||||
low-height
|
low-height
|
||||||
:is-playable="true"
|
:is-playable="true"
|
||||||
:tracks="tracks"
|
:tracks="tracks"
|
||||||
|
:playlist="playlist"
|
||||||
>
|
>
|
||||||
{{ t('views.playlists.Detail.button.playAll') }}
|
{{ t('views.playlists.Detail.button.playAll') }}
|
||||||
</PlayButton>
|
</PlayButton>
|
||||||
|
|
Loading…
Reference in New Issue