diff --git a/api/funkwhale_api/common/schema.yml b/api/funkwhale_api/common/schema.yml index 3dd623900..9970d42fd 100644 --- a/api/funkwhale_api/common/schema.yml +++ b/api/funkwhale_api/common/schema.yml @@ -1,7 +1,7 @@ openapi: 3.0.3 info: title: Funkwhale API - version: 1.4.0 + version: 2.0.0a1 description: | # Funkwhale API diff --git a/api/funkwhale_api/favorites/migrations/0003_trackfavorite_actor_trackfavorite_fid_and_more.py b/api/funkwhale_api/favorites/migrations/0003_trackfavorite_actor_trackfavorite_fid_and_more.py index b204ba79c..3a14b447d 100644 --- a/api/funkwhale_api/favorites/migrations/0003_trackfavorite_actor_trackfavorite_fid_and_more.py +++ b/api/funkwhale_api/favorites/migrations/0003_trackfavorite_actor_trackfavorite_fid_and_more.py @@ -56,12 +56,8 @@ class Migration(migrations.Migration): model_name="trackfavorite", name="fid", field=models.URLField( - db_index=True, - default="https://default.fid", max_length=500, - unique=True, ), - preserve_default=False, ), migrations.AddField( model_name="trackfavorite", @@ -90,6 +86,15 @@ class Migration(migrations.Migration): related_name="track_favorites", to="federation.actor", ), ), + migrations.AlterField( + model_name="trackfavorite", + name="fid", + field=models.URLField( + db_index=True, + max_length=500, + unique=True, + ), + ), migrations.AlterUniqueTogether( name="trackfavorite", unique_together={("track", "actor")}, diff --git a/api/funkwhale_api/playlists/views.py b/api/funkwhale_api/playlists/views.py index 03e5e378d..5e04c237e 100644 --- a/api/funkwhale_api/playlists/views.py +++ b/api/funkwhale_api/playlists/views.py @@ -20,6 +20,7 @@ from funkwhale_api.music import utils as music_utils from funkwhale_api.users.oauth import permissions as oauth_permissions from . import filters, models, parsers, renderers, serializers +from rest_framework.pagination import PageNumberPagination logger = logging.getLogger(__name__) @@ -138,9 +139,15 @@ class PlaylistViewSet( plugins.TRIGGER_THIRD_PARTY_UPLOAD, track=plt.track, ) - serializer = serializers.PlaylistTrackSerializer(plts, many=True) - data = {"count": len(plts), "results": serializer.data} - return Response(data, status=200) + + # Apply pagination + paginator = PageNumberPagination() + paginator.page_size = 100 # Set the page size (number of items per page) + paginated_plts = paginator.paginate_queryset(plts, request) + + # Serialize the paginated data + serializer = serializers.PlaylistTrackSerializer(paginated_plts, many=True) + return paginator.get_paginated_response(serializer.data) @extend_schema( operation_id="add_to_playlist", request=serializers.PlaylistAddManySerializer diff --git a/front/src/components/audio/track/MobileRow.vue b/front/src/components/audio/track/MobileRow.vue index 39494a996..879338758 100644 --- a/front/src/components/audio/track/MobileRow.vue +++ b/front/src/components/audio/track/MobileRow.vue @@ -25,7 +25,6 @@ interface Props extends PlayOptionsProps { // TODO(wvffle): Remove after https://github.com/vuejs/core/pull/4512 is merged isPlayable?: boolean - tracks?: Track[] artist?: Artist | null album?: Album | null playlist?: Playlist | null @@ -39,7 +38,6 @@ const props = withDefaults(defineProps(), { isArtist: false, isAlbum: false, - tracks: () => [], artist: null, album: null, playlist: null, diff --git a/front/src/components/audio/track/Table.vue b/front/src/components/audio/track/Table.vue index 1e32ea901..1b7051836 100644 --- a/front/src/components/audio/track/Table.vue +++ b/front/src/components/audio/track/Table.vue @@ -245,7 +245,7 @@ const updatePage = (page: number) => { - + (), { }) const store = useStore() - +const isLoadingMoreTracks = ref(false) const edit = ref(props.defaultEdit) const playlist = ref(null) const playlistTracks = ref([]) @@ -57,17 +57,24 @@ const labels = computed(() => ({ })) const isLoading = ref(false) +const nextPage = ref(null) // Tracks the next page URL +const previousPage = ref(null) // Tracks the previous page URL +const totalTracks = ref(0) // Total number of tracks + const fetchData = async () => { isLoading.value = true try { const [playlistResponse, tracksResponse] = await Promise.all([ axios.get(`playlists/${props.id}/`), - axios.get(`playlists/${props.id}/tracks/`) + axios.get(`playlists/${props.id}/tracks?page=1`) ]) playlist.value = playlistResponse.data fullPlaylistTracks.value = tracksResponse.data.results + nextPage.value = tracksResponse.data.next + previousPage.value = tracksResponse.data.previous + totalTracks.value = tracksResponse.data.count } catch (error) { useErrorHandler(error as Error) } @@ -75,6 +82,25 @@ const fetchData = async () => { isLoading.value = false } +const loadMoreTracks = async () => { + if (nextPage.value) { + isLoadingMoreTracks.value = true; // Set loading state for the button + try { + const response = await axios.get(nextPage.value); + + // Append new tracks to the existing list + fullPlaylistTracks.value = [...fullPlaylistTracks.value, ...response.data.results]; + + // Update pagination metadata + nextPage.value = response.data.next; + } catch (error) { + useErrorHandler(error as Error) + } finally { + isLoadingMoreTracks.value = false; // Reset loading state + } + } +}; + fetchData() const images = computed(() => { @@ -113,17 +139,6 @@ const randomizedColors = computed(() => shuffleArray(bgcolors.value)) // return t('components.audio.ChannelCard.title', { date }) // }) -// TODO: Check if this function is still needed -// const deletePlaylist = async () => { -// try { -// await axios.delete(`playlists/${props.id}/`) -// store.dispatch('playlists/fetchOwn') -// return router.push({ path: '/library' }) -// } catch (error) { -// useErrorHandler(error as Error) -// } -// } - // TODO: Implement shuffle const shuffle = () => {} @@ -178,7 +193,6 @@ const shuffle = () => {} {} :tracks="tracks" :unique="false" /> +