- {{ t('components.playlists.PlaylistModal.table.edit.header.edit') }} |
+
+ {{ t('components.playlists.PlaylistModal.table.edit.header.edit')
+ }}
+ |
{{ t('components.playlists.PlaylistModal.table.edit.header.name') }}
|
@@ -173,7 +176,7 @@ store.dispatch('playlists/fetchOwn')
solid
secondary
square-small
- :to="{name: 'library.playlists.detail', params: {id: playlist.id }, query: {mode: 'edit'}}"
+ :to="{ name: 'library.playlists.detail', params: { id: playlist.uuid }, query: { mode: 'edit' } }"
icon="bi-pencil-fill"
>
{{ t('components.playlists.PlaylistModal.button.edit') }}
@@ -181,7 +184,7 @@ store.dispatch('playlists/fetchOwn')
{{ playlist.name }}
@@ -196,7 +199,7 @@ store.dispatch('playlists/fetchOwn')
primary
:title="labels.addToPlaylist"
icon="bi-plus"
- @click.prevent="addToPlaylist(playlist.id, false)"
+ @click.prevent="addToPlaylist(playlist.uuid, false)"
>
{{ t('components.playlists.PlaylistModal.button.addTrack') }}
diff --git a/front/src/components/playlists/Widget.vue b/front/src/components/playlists/Widget.vue
index 31a729e63..988d83a36 100644
--- a/front/src/components/playlists/Widget.vue
+++ b/front/src/components/playlists/Widget.vue
@@ -100,7 +100,7 @@ watch(
diff --git a/front/src/composables/audio/usePlayOptions.ts b/front/src/composables/audio/usePlayOptions.ts
index ab9ead3a5..108571f33 100644
--- a/front/src/composables/audio/usePlayOptions.ts
+++ b/front/src/composables/audio/usePlayOptions.ts
@@ -129,7 +129,7 @@ export default (props: PlayOptionsProps) => {
tracks.push(response.data as Track)
}
} else if (props.playlist) {
- const response = await axios.get(`playlists/${props.playlist.id}/tracks/`)
+ const response = await axios.get(`playlists/${props.playlist.uuid}/tracks/`)
const playlistTracks = (response.data.results as Array<{ track: Track }>).map(({ track }) => track as Track)
const artistIds = store.getters['moderation/artistFilters']().map((filter: ContentFilter) => filter.target.id)
@@ -200,6 +200,29 @@ export default (props: PlayOptionsProps) => {
return replacePlay(index)
}
+ const requestPlaylistUploadsAccess = async (playlist: Playlist) => {
+ const libraryUrl = playlist.library;
+ if (!libraryUrl) {
+ throw new Error("Playlist library URL is missing.");
+ }
+ const libResponse = await axios.get(libraryUrl);
+ const id = libResponse.data?.id || libResponse.data?.results?.id;
+ if (!id) {
+ throw new Error("Library id not found in response.");
+ }
+ const fetchResponse = await axios.post('federation/fetches',
+ { object: id }
+ );
+
+ const response = await axios.post(
+ 'federation/follows/library',
+ { target: fetchResponse.data.object.uuid }
+ );
+
+ return response;
+};
+
+
return {
playable,
filterableArtist,
@@ -208,6 +231,7 @@ export default (props: PlayOptionsProps) => {
enqueueNext,
replacePlay,
activateTrack,
- isLoading
+ isLoading,
+ requestPlaylistUploadsAccess
}
}
diff --git a/front/src/composables/moderation/useReport.ts b/front/src/composables/moderation/useReport.ts
index c29737101..c0a89de57 100644
--- a/front/src/composables/moderation/useReport.ts
+++ b/front/src/composables/moderation/useReport.ts
@@ -112,7 +112,7 @@ const getReportableObjects = ({ track, album, artist, artistCredit, playlist, ac
label: t('composables.moderation.useReport.playlist.label'),
target: {
type: 'playlist',
- id: playlist.id,
+ uuid: playlist.uuid,
label: playlist.name,
_obj: playlist,
typeLabel: t('composables.moderation.useReport.playlist.typeLabel')
diff --git a/front/src/generated/types.ts b/front/src/generated/types.ts
index 4bd0b65c4..03e6c53af 100644
--- a/front/src/generated/types.ts
+++ b/front/src/generated/types.ts
@@ -2233,7 +2233,7 @@ export interface paths {
patch?: never;
trace?: never;
};
- "/api/v1/playlists/{id}/": {
+ "/api/v1/playlists/{uuid}/": {
parameters: {
query?: never;
header?: never;
@@ -2249,7 +2249,7 @@ export interface paths {
patch: operations["partial_update_playlist"];
trace?: never;
};
- "/api/v1/playlists/{id}/add/": {
+ "/api/v1/playlists/{uuid}/add/": {
parameters: {
query?: never;
header?: never;
@@ -2265,7 +2265,7 @@ export interface paths {
patch?: never;
trace?: never;
};
- "/api/v1/playlists/{id}/albums/": {
+ "/api/v1/playlists/{uuid}/albums/": {
parameters: {
query?: never;
header?: never;
@@ -2281,14 +2281,14 @@ export interface paths {
patch?: never;
trace?: never;
};
- "/api/v1/playlists/{id}/artists/": {
+ "/api/v1/playlists/{uuid}/artists/": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
- get: operations["get_playlist_artits"];
+ get: operations["get_playlist_artists"];
put?: never;
post?: never;
delete?: never;
@@ -2297,7 +2297,7 @@ export interface paths {
patch?: never;
trace?: never;
};
- "/api/v1/playlists/{id}/clear/": {
+ "/api/v1/playlists/{uuid}/clear/": {
parameters: {
query?: never;
header?: never;
@@ -2313,7 +2313,7 @@ export interface paths {
patch?: never;
trace?: never;
};
- "/api/v1/playlists/{id}/move/": {
+ "/api/v1/playlists/{uuid}/move/": {
parameters: {
query?: never;
header?: never;
@@ -2329,7 +2329,7 @@ export interface paths {
patch?: never;
trace?: never;
};
- "/api/v1/playlists/{id}/remove/": {
+ "/api/v1/playlists/{uuid}/remove/": {
parameters: {
query?: never;
header?: never;
@@ -2345,7 +2345,7 @@ export interface paths {
patch?: never;
trace?: never;
};
- "/api/v1/playlists/{id}/tracks/": {
+ "/api/v1/playlists/{uuid}/tracks/": {
parameters: {
query?: never;
header?: never;
@@ -5226,7 +5226,7 @@ export interface paths {
patch?: never;
trace?: never;
};
- "/api/v2/playlists/{id}/": {
+ "/api/v2/playlists/{uuid}/": {
parameters: {
query?: never;
header?: never;
@@ -5242,7 +5242,7 @@ export interface paths {
patch: operations["partial_update_playlist_2"];
trace?: never;
};
- "/api/v2/playlists/{id}/add/": {
+ "/api/v2/playlists/{uuid}/add/": {
parameters: {
query?: never;
header?: never;
@@ -5258,7 +5258,7 @@ export interface paths {
patch?: never;
trace?: never;
};
- "/api/v2/playlists/{id}/albums/": {
+ "/api/v2/playlists/{uuid}/albums/": {
parameters: {
query?: never;
header?: never;
@@ -5274,14 +5274,14 @@ export interface paths {
patch?: never;
trace?: never;
};
- "/api/v2/playlists/{id}/artists/": {
+ "/api/v2/playlists/{uuid}/artists/": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
- get: operations["get_playlist_artits_2"];
+ get: operations["get_playlist_artists_2"];
put?: never;
post?: never;
delete?: never;
@@ -5290,7 +5290,7 @@ export interface paths {
patch?: never;
trace?: never;
};
- "/api/v2/playlists/{id}/clear/": {
+ "/api/v2/playlists/{uuid}/clear/": {
parameters: {
query?: never;
header?: never;
@@ -5306,7 +5306,7 @@ export interface paths {
patch?: never;
trace?: never;
};
- "/api/v2/playlists/{id}/move/": {
+ "/api/v2/playlists/{uuid}/move/": {
parameters: {
query?: never;
header?: never;
@@ -5322,7 +5322,7 @@ export interface paths {
patch?: never;
trace?: never;
};
- "/api/v2/playlists/{id}/remove/": {
+ "/api/v2/playlists/{uuid}/remove/": {
parameters: {
query?: never;
header?: never;
@@ -5338,7 +5338,7 @@ export interface paths {
patch?: never;
trace?: never;
};
- "/api/v2/playlists/{id}/tracks/": {
+ "/api/v2/playlists/{uuid}/tracks/": {
parameters: {
query?: never;
header?: never;
@@ -8497,7 +8497,10 @@ export interface components {
summary?: components["schemas"]["ContentRequest"] | null;
};
Playlist: {
- readonly id: number;
+ /** Format: uuid */
+ readonly uuid: string;
+ /** Format: uri */
+ readonly fid: string;
name: string;
readonly actor: components["schemas"]["APIActor"];
/** Format: date-time */
@@ -8510,6 +8513,9 @@ export interface components {
readonly duration: number;
readonly is_playable: boolean;
description?: string | null;
+ /** Format: uri */
+ readonly library: string;
+ readonly library_followed: boolean;
};
PlaylistAddManyRequest: {
tracks: number[];
@@ -14142,8 +14148,7 @@ export interface operations {
};
header?: never;
path: {
- /** @description A unique integer value identifying this playlist. */
- id: number;
+ uuid: string;
};
cookie?: never;
};
@@ -14167,8 +14172,7 @@ export interface operations {
};
header?: never;
path: {
- /** @description A unique integer value identifying this playlist. */
- id: number;
+ uuid: string;
};
cookie?: never;
};
@@ -14199,8 +14203,7 @@ export interface operations {
};
header?: never;
path: {
- /** @description A unique integer value identifying this playlist. */
- id: number;
+ uuid: string;
};
cookie?: never;
};
@@ -14222,8 +14225,7 @@ export interface operations {
};
header?: never;
path: {
- /** @description A unique integer value identifying this playlist. */
- id: number;
+ uuid: string;
};
cookie?: never;
};
@@ -14254,8 +14256,7 @@ export interface operations {
};
header?: never;
path: {
- /** @description A unique integer value identifying this playlist. */
- id: number;
+ uuid: string;
};
cookie?: never;
};
@@ -14286,8 +14287,7 @@ export interface operations {
};
header?: never;
path: {
- /** @description A unique integer value identifying this playlist. */
- id: number;
+ uuid: string;
};
cookie?: never;
};
@@ -14304,15 +14304,14 @@ export interface operations {
};
};
};
- get_playlist_artits: {
+ get_playlist_artists: {
parameters: {
query?: {
format?: "json" | "xspf";
};
header?: never;
path: {
- /** @description A unique integer value identifying this playlist. */
- id: number;
+ uuid: string;
};
cookie?: never;
};
@@ -14336,8 +14335,7 @@ export interface operations {
};
header?: never;
path: {
- /** @description A unique integer value identifying this playlist. */
- id: number;
+ uuid: string;
};
cookie?: never;
};
@@ -14359,8 +14357,7 @@ export interface operations {
};
header?: never;
path: {
- /** @description A unique integer value identifying this playlist. */
- id: number;
+ uuid: string;
};
cookie?: never;
};
@@ -14391,8 +14388,7 @@ export interface operations {
};
header?: never;
path: {
- /** @description A unique integer value identifying this playlist. */
- id: number;
+ uuid: string;
};
cookie?: never;
};
@@ -14423,8 +14419,7 @@ export interface operations {
};
header?: never;
path: {
- /** @description A unique integer value identifying this playlist. */
- id: number;
+ uuid: string;
};
cookie?: never;
};
@@ -14460,8 +14455,7 @@ export interface operations {
};
header?: never;
path: {
- /** @description A unique integer value identifying this playlist. */
- id: number;
+ uuid: string;
};
cookie?: never;
};
@@ -21155,8 +21149,7 @@ export interface operations {
};
header?: never;
path: {
- /** @description A unique integer value identifying this playlist. */
- id: number;
+ uuid: string;
};
cookie?: never;
};
@@ -21180,8 +21173,7 @@ export interface operations {
};
header?: never;
path: {
- /** @description A unique integer value identifying this playlist. */
- id: number;
+ uuid: string;
};
cookie?: never;
};
@@ -21212,8 +21204,7 @@ export interface operations {
};
header?: never;
path: {
- /** @description A unique integer value identifying this playlist. */
- id: number;
+ uuid: string;
};
cookie?: never;
};
@@ -21235,8 +21226,7 @@ export interface operations {
};
header?: never;
path: {
- /** @description A unique integer value identifying this playlist. */
- id: number;
+ uuid: string;
};
cookie?: never;
};
@@ -21267,8 +21257,7 @@ export interface operations {
};
header?: never;
path: {
- /** @description A unique integer value identifying this playlist. */
- id: number;
+ uuid: string;
};
cookie?: never;
};
@@ -21299,8 +21288,7 @@ export interface operations {
};
header?: never;
path: {
- /** @description A unique integer value identifying this playlist. */
- id: number;
+ uuid: string;
};
cookie?: never;
};
@@ -21317,15 +21305,14 @@ export interface operations {
};
};
};
- get_playlist_artits_2: {
+ get_playlist_artists_2: {
parameters: {
query?: {
format?: "json" | "xspf";
};
header?: never;
path: {
- /** @description A unique integer value identifying this playlist. */
- id: number;
+ uuid: string;
};
cookie?: never;
};
@@ -21349,8 +21336,7 @@ export interface operations {
};
header?: never;
path: {
- /** @description A unique integer value identifying this playlist. */
- id: number;
+ uuid: string;
};
cookie?: never;
};
@@ -21372,8 +21358,7 @@ export interface operations {
};
header?: never;
path: {
- /** @description A unique integer value identifying this playlist. */
- id: number;
+ uuid: string;
};
cookie?: never;
};
@@ -21404,8 +21389,7 @@ export interface operations {
};
header?: never;
path: {
- /** @description A unique integer value identifying this playlist. */
- id: number;
+ uuid: string;
};
cookie?: never;
};
@@ -21436,8 +21420,7 @@ export interface operations {
};
header?: never;
path: {
- /** @description A unique integer value identifying this playlist. */
- id: number;
+ uuid: string;
};
cookie?: never;
};
@@ -21473,8 +21456,7 @@ export interface operations {
};
header?: never;
path: {
- /** @description A unique integer value identifying this playlist. */
- id: number;
+ uuid: string;
};
cookie?: never;
};
diff --git a/front/src/locales/en_US.json b/front/src/locales/en_US.json
index b90bbb0aa..4a4232dcd 100644
--- a/front/src/locales/en_US.json
+++ b/front/src/locales/en_US.json
@@ -483,6 +483,10 @@
"playArtist": "Play artist",
"playNext": "Play next",
"playNow": "Play now",
+ "PlaylistUploadGranted": "The owner of the playlist granted you access to his playlist's files",
+ "PlaylistUploadTooltip": "This only applies to files owned by the playlist actor. If you want to get access to all the files, think to buy them to support your favorites artists",
+ "PlaylistUploadPending": "You've already requested access to the playlist files",
+ "PlaylistUploadNotRequest": "Request access to the files from this playlist",
"playPlaylist": "Play playlist",
"playTrack": "Play track",
"playTracks": "Play tracks",
@@ -3216,7 +3220,7 @@
},
"useErrorHandler": {
"errorReportMessage": "To help us understand why it happened, please attach a detailed description of what you did that has triggered the error.",
- "errorReportTitle": "An unexpected error occured.",
+ "errorReportTitle": "An unexpected error occurred.",
"leaveFeedback": "Leave feedback",
"unexpectedError": "An unexpected error occurred."
},
@@ -4279,7 +4283,7 @@
"showStatus": "Show information about the upload status for this track"
},
"empty": {
- "noTracks": "No tracks have been added to this libray yet"
+ "noTracks": "No tracks have been added to this library yet"
},
"label": {
"importStatus": "Import status",
diff --git a/front/src/ui/modals/Search.vue b/front/src/ui/modals/Search.vue
index 1fbc4d95e..38c49e767 100644
--- a/front/src/ui/modals/Search.vue
+++ b/front/src/ui/modals/Search.vue
@@ -413,9 +413,9 @@ const radioConfig = computed(() =>
})
: count({ type: 'playlists' }) > 0
? ({
- type: 'playlist',
- ids: resultsPerCategory({ type: 'playlists' }).map(({ id }) => id.toString())
- })
+ type: 'playlist',
+ ids: resultsPerCategory({ type: 'playlists' }).map(({ uuid }) => uuid.toString())
+ })
: count({ type: 'artists' }) > 0
? ({
type: 'artist',
diff --git a/front/src/views/playlists/Detail.vue b/front/src/views/playlists/Detail.vue
index 03f043fdb..aa3943a5c 100644
--- a/front/src/views/playlists/Detail.vue
+++ b/front/src/views/playlists/Detail.vue
@@ -44,16 +44,12 @@ const store = useStore()
const edit = ref(props.defaultEdit)
const playlist = ref(null)
const playlistTracks = ref([])
-
const showEmbedModal = ref(false)
-// TODO: Compute `tracks` with `track`. In the new types, `track` is just a string.
-// We probably have to load each track before loading it into `tracks`.
-// Original line: const tracks = computed(() => playlistTracks.value.map(({ track }, index) => ({ ...track, position: index + 1 })))
-const tracks = computed(() => playlistTracks.value.map(({ track }, index) => (
- // @i-would-expect-ts-to-error because this typecasting is evil
- { position: index + 1 } as Track
-)))
+type FullPlaylistTrack = Omit & { track: Track }
+const fullPlaylistTracks = ref([])
+
+const tracks = computed(() => fullPlaylistTracks.value.map(({ track }, index) => ({ ...track as Track, position: index + 1 })))
const { t } = useI18n()
const labels = computed(() => ({
@@ -71,7 +67,7 @@ const fetchData = async () => {
])
playlist.value = playlistResponse.data
- playlistTracks.value = tracksResponse.data.results
+ fullPlaylistTracks.value = tracksResponse.data.results
} catch (error) {
useErrorHandler(error as Error)
}
@@ -277,7 +273,7 @@ const shuffle = () => {}
diff --git a/front/src/views/playlists/List.vue b/front/src/views/playlists/List.vue
index 39f8ffef6..af8f0b11f 100644
--- a/front/src/views/playlists/List.vue
+++ b/front/src/views/playlists/List.vue
@@ -254,7 +254,7 @@ const paginateOptions = computed(() => sortedUniq([12, 30, 50, paginateBy.value]
/>
|