From ef7e6730890db4467c3812a0a0572b775a4f31ce Mon Sep 17 00:00:00 2001 From: "Brian C. Arnold" Date: Fri, 25 Nov 2022 16:55:30 -0500 Subject: [PATCH] feat(playlist) Got playlists working for the most part. (the skeleton display isn't quite right, though.) --- frontend/components/Item/CollectionTabs.vue | 7 +- frontend/components/Item/PlaylistItems.vue | 183 ++++++++++++++++++++ frontend/pages/item/_itemId/index.vue | 8 +- frontend/store/items.ts | 135 ++++++++++++++- 4 files changed, 323 insertions(+), 10 deletions(-) create mode 100644 frontend/components/Item/PlaylistItems.vue diff --git a/frontend/components/Item/CollectionTabs.vue b/frontend/components/Item/CollectionTabs.vue index 80f07dce5c9..28b41589341 100644 --- a/frontend/components/Item/CollectionTabs.vue +++ b/frontend/components/Item/CollectionTabs.vue @@ -42,8 +42,11 @@ export default Vue.extend({ computed: { ...mapStores(itemsStore), children(): Record | undefined { - if (this.items.getChildrenOfParent(this.item.Id)?.length) { - return groupBy(this.items.getChildrenOfParent(this.item.Id), 'Type'); + if (this.items.getChildrenOfParentCollection(this.item.Id)?.length) { + return groupBy( + this.items.getChildrenOfParentCollection(this.item.Id), + 'Type' + ); } } }, diff --git a/frontend/components/Item/PlaylistItems.vue b/frontend/components/Item/PlaylistItems.vue new file mode 100644 index 00000000000..ccbb7954cdb --- /dev/null +++ b/frontend/components/Item/PlaylistItems.vue @@ -0,0 +1,183 @@ + + + + + diff --git a/frontend/pages/item/_itemId/index.vue b/frontend/pages/item/_itemId/index.vue index c716ebf148c..4ad0a45c4ed 100644 --- a/frontend/pages/item/_itemId/index.vue +++ b/frontend/pages/item/_itemId/index.vue @@ -241,12 +241,12 @@ - + + + + diff --git a/frontend/store/items.ts b/frontend/store/items.ts index 31a47f54244..4c414322147 100644 --- a/frontend/store/items.ts +++ b/frontend/store/items.ts @@ -1,18 +1,20 @@ import Vue from 'vue'; -import { BaseItemDto, ItemFields } from '@jellyfin/client-axios'; +import { BaseItemDto, ImageType, ItemFields } from '@jellyfin/client-axios'; import { defineStore } from 'pinia'; import { authStore } from '.'; export interface ItemsState { byId: Record; collectionById: Record; + playlistById: Record; } export const itemsStore = defineStore('items', { state: () => { return { byId: {}, - collectionById: {} + collectionById: {}, + playlistById: {} } as ItemsState; }, actions: { @@ -69,6 +71,113 @@ export const itemsStore = defineStore('items', { * @param children * @returns - The children of the item */ + async movePlaylistItem( + parent: BaseItemDto, + localChild: BaseItemDto, + index: number + ): Promise { + const auth = authStore(); + + // You're probably asking "... but why?" + + // Because when the Playback manager is playing these tracks, + // it seems to erase the PlaylistItemId from each of the items. + // So... I just get a new bunch of them to move things. + + // Probably a better way to do it, but... + // ... I didn't feel like figuring that out right now. + + // If you try to fix this, make sure that you can click "Play" + // on the playlist, then move tracks. + + const children = await this.$nuxt.$api.playlists.getPlaylistItems({ + userId: auth.currentUserId, + playlistId: parent.Id as string, + fields: [ItemFields.PrimaryImageAspectRatio], + enableImageTypes: [ + ImageType.Primary, + ImageType.Backdrop, + ImageType.Banner, + ImageType.Thumb + ] + }); + const child = children.data.Items?.find( + (i) => i.Id == localChild.Id + ) as BaseItemDto; + await this.$nuxt.$api.playlists.moveItem({ + playlistId: parent.Id as string, + itemId: child.PlaylistItemId as string, + newIndex: index + }); + return (await this.fetchAndAddPlaylist( + parent.Id as string + )) as BaseItemDto[]; + }, + addPlaylist(parent: BaseItemDto, children: BaseItemDto[]): BaseItemDto[] { + if (!parent.Id) { + throw new Error("Parent item doesn't have an Id"); + } + + const childIds = []; + + for (const child of children) { + if (child.Id) { + if (!this.getItemById(child.Id)) { + this.add(child); + } + + childIds.push(child.Id); + } + } + + Vue.set(this.playlistById, parent.Id, childIds); + + return this.getChildrenOfParentPlaylist(parent.Id) as BaseItemDto[]; + }, + async fetchAndAddPlaylist( + parentId: string | undefined + ): Promise { + const auth = authStore(); + + if (parentId && !this.getItemById(parentId)) { + const parentItem = ( + await this.$nuxt.$api.items.getItems({ + userId: auth.currentUserId, + ids: [parentId], + fields: Object.values(ItemFields) + }) + ).data; + + if (!parentItem.Items?.[0]) { + throw new Error("This parent doesn't exist"); + } + + this.add(parentItem.Items[0]); + } + + const childItems = ( + await this.$nuxt.$api.playlists.getPlaylistItems({ + userId: auth.currentUserId, + playlistId: parentId as string, + fields: [ItemFields.PrimaryImageAspectRatio], + enableImageTypes: [ + ImageType.Primary, + ImageType.Backdrop, + ImageType.Banner, + ImageType.Thumb + ] + }) + ).data; + + if (childItems.Items) { + const parent = this.getItemById(parentId); + + return this.addPlaylist(parent as BaseItemDto, childItems.Items); + } else { + // I think this just means it's an empty playlist...? + return this.addPlaylist(parent as BaseItemDto, []); + } + }, addCollection(parent: BaseItemDto, children: BaseItemDto[]): BaseItemDto[] { if (!parent.Id) { throw new Error("Parent item doesn't have an Id"); @@ -88,7 +197,7 @@ export const itemsStore = defineStore('items', { Vue.set(this.collectionById, parent.Id, childIds); - return this.getChildrenOfParent(parent.Id) as BaseItemDto[]; + return this.getChildrenOfParentCollection(parent.Id) as BaseItemDto[]; }, /** * Fetches a parent and its children and adds thecollection to the store @@ -156,7 +265,7 @@ export const itemsStore = defineStore('items', { return res; }; }, - getChildrenOfParent: (state) => { + getChildrenOfParentCollection: (state) => { return (id: string | undefined): BaseItemDto[] | undefined => { if (!id) { throw new Error('No itemId provided'); @@ -165,6 +274,24 @@ export const itemsStore = defineStore('items', { const res = [] as BaseItemDto[]; const ids = state.collectionById[id]; + if (ids?.length) { + for (const _id of ids) { + res.push(state.byId[_id]); + } + + return res; + } + }; + }, + getChildrenOfParentPlaylist: (state) => { + return (id: string | undefined): BaseItemDto[] | undefined => { + if (!id) { + throw new Error('No itemId provided'); + } + + const res = [] as BaseItemDto[]; + const ids = state.playlistById[id]; + if (ids?.length) { for (const _id of ids) { res.push(state.byId[_id]);