Skip to content

Commit

Permalink
feat(playlist): (Flattened Vue 2.x via rebase)
Browse files Browse the repository at this point in the history
Temporary use of CollectionTabs for playlist for testing.
Need to create new component for playlist
 * Need to display in list
 * Need to be able to drag/reorder

Got playlists working for the most part. (the skeleton display isn't quite right, though.)

Remove showing of "loading" skel during moving items.

Removed code smells.

Fixed some linting issues.

Caused an issue while trying to clean things up. Fixed now.

Got saving play queue to playlist working. :-)

Fixed lint issues.
  • Loading branch information
BrianCArnold committed Nov 7, 2023
1 parent 05a2126 commit 46ab70b
Show file tree
Hide file tree
Showing 5 changed files with 381 additions and 12 deletions.
49 changes: 43 additions & 6 deletions frontend/components/Buttons/QueueButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,25 @@
<draggable-queue v-if="menu || !destroy" class="ml-4" />
</v-list>
<v-spacer />
<v-card-actions class="d-flex justify-space-between">
<v-card-text v-if="creatingPlaylist" class="py-1">
<v-form @submit.prevent="handleSubmit">
<v-text-field
v-model="playlistName"
single-line
:label="$t('name')"
append-outer-icon="mdi-content-save"
hide-details="auto"
prepend-icon="mdi-cancel"
class="py-1"
@click:append-outer="saveQueueAsPlaylist"
@click:prepend="cancelSaveQueue"
/>
</v-form>
</v-card-text>
<v-card-actions
v-if="!creatingPlaylist"
class="d-flex justify-space-between"
>
<v-tooltip top>
<template #activator="{ on: tooltip }">
<v-btn icon v-on="tooltip" @click="playbackManager.stop">
Expand All @@ -86,7 +104,7 @@
</v-tooltip>
<v-tooltip top>
<template #activator="{ on: tooltip }">
<v-btn icon disabled v-on="tooltip">
<v-btn icon v-on="tooltip" @click="askForPlaylistName">
<v-icon>mdi-content-save</v-icon>
</v-btn>
</template>
Expand All @@ -102,7 +120,7 @@
import { BaseItemDto } from '@jellyfin/client-axios';
import Vue from 'vue';
import { mapStores } from 'pinia';
import { playbackManagerStore } from '~/store';
import { authStore, playbackManagerStore } from '~/store';
import { InitMode } from '~/store/playbackManager';
import { getTotalEndsAtTime } from '~/utils/time';
Expand All @@ -121,11 +139,13 @@ export default Vue.extend({
return {
menu: false,
destroy: false,
timeout: undefined as undefined | number
timeout: undefined as undefined | number,
playlistName: null as string | null,
creatingPlaylist: false
};
},
computed: {
...mapStores(playbackManagerStore),
...mapStores(playbackManagerStore, authStore),
sourceText: {
get(): string {
/**
Expand Down Expand Up @@ -204,7 +224,24 @@ export default Vue.extend({
}
},
methods: {
getTotalEndsAtTime
getTotalEndsAtTime,
askForPlaylistName() {
this.creatingPlaylist = true;
},
async saveQueueAsPlaylist() {
try {
await this.$nuxt.$api.playlists.createPlaylist({
name: this.playlistName as string,
ids: this.playbackManager.queue,
userId: this.auth.currentUserId
});
} finally {
this.creatingPlaylist = false;
}
},
cancelSaveQueue() {
this.creatingPlaylist = false;
}
}
});
</script>
Expand Down
7 changes: 5 additions & 2 deletions frontend/components/Item/CollectionTabs.vue
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,11 @@ export default Vue.extend({
computed: {
...mapStores(itemsStore),
children(): Record<string, BaseItemDto[]> | 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'
);
}
}
},
Expand Down
177 changes: 177 additions & 0 deletions frontend/components/Item/PlaylistItems.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
<template>
<div>
<h1 v-if="!children && !loading" class="text-h5 text-center">
{{ $t('collectionEmpty') }}
</h1>
<skeleton-item-grid v-if="loading" :view-type="''" />

<v-list v-if="!!children" color="transparent" two-line>
<v-list-item-group class="list-group">
<draggable
v-if="children.length > 0"
v-bind="dragOptions"
v-model="children"
:move="checkMove"
class="list-draggable"
>
<v-hover
v-for="(item, index) in children"
:key="`${item.Id}-${index}`"
v-slot="{ hover }"
>
<v-list-item ripple @click="playQueueFrom(index)">
<v-list-item-action
v-if="!hover"
class="list-group-item d-flex justify-center d-flex transition"
:class="{ 'primary--text font-weight-bold': isPlaying(item) }"
>
{{ index + 1 }}
</v-list-item-action>
<v-list-item-action v-else class="justify-center d-flex">
<v-icon>mdi-drag-horizontal</v-icon>
</v-list-item-action>
<v-list-item-avatar tile class="list-group-item">
<blurhash-image :item="item" />
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title
class="text-truncate ml-2 list-group-item transition"
:class="{
'primary--text font-weight-bold': isPlaying(item)
}"
>
{{ item.Name }}
</v-list-item-title>
<v-list-item-subtitle
v-if="getArtists(item)"
class="ml-2 list-group-item transition"
:class="{
'primary--text font-weight-bold': isPlaying(item)
}"
>
{{ getArtists(item) }}
</v-list-item-subtitle>
</v-list-item-content>

<v-list-item-action>
<like-button :item="item" />
</v-list-item-action>
<v-list-item-action class="mr-2">
<item-menu :item="item" queue />
</v-list-item-action>
</v-list-item>
</v-hover>
</draggable>
<div
v-for="index in skeletonLength"
v-else
:key="index"
class="d-flex align-center mt-5 mb-5"
>
<v-skeleton-loader type="avatar" class="ml-3 mr-3" />
<v-skeleton-loader type="sentences" width="10em" class="pr-5" />
</div>
</v-list-item-group>
</v-list>
</div>
</template>

<script lang="ts">
import { BaseItemDto } from '@jellyfin/client-axios';
import Vue from 'vue';
import { mapStores } from 'pinia';
import { MoveEvent } from 'vuedraggable';
import { itemsStore, playbackManagerStore } from '~/store';
export default Vue.extend({
props: {
playlist: {
type: Object as () => BaseItemDto,
required: true
}
},
data() {
return {
currentTab: 0,
loading: false,
newIndex: null as number | null,
oldIndex: null as number | null,
dragOptions: {
animation: 500,
delay: 0,
group: false,
dragoverBubble: true,
ghostClass: 'ghost'
}
};
},
computed: {
...mapStores(itemsStore, playbackManagerStore),
children: {
get(): BaseItemDto[] {
return this.items.getChildrenOfParentPlaylist(
this.playlist.Id
) as BaseItemDto[];
},
set(_: BaseItemDto[]): void {
if (this.oldIndex != null && this.newIndex != null) {
this.items.movePlaylistItem(
this.playlist,
this.children[this.oldIndex],
this.newIndex
);
}
}
}
},
watch: {
playlist: {
immediate: true,
async handler(playlist: BaseItemDto): Promise<void> {
if (!this.children) {
this.loading = true;
await this.items.fetchAndAddPlaylist(playlist.Id as string);
this.loading = false;
}
}
}
},
methods: {
checkMove(evt: MoveEvent<BaseItemDto>) {
this.newIndex = evt.draggedContext.futureIndex;
this.oldIndex = evt.draggedContext.index;
},
getArtists(item: BaseItemDto): string | null {
if (item.Artists) {
return item.Artists.join(', ');
} else {
return null;
}
},
isPlaying(item: BaseItemDto): boolean {
if (this.playbackManager.getCurrentItem === undefined) {
return false;
}
return (
item.Id === (this.playbackManager.getCurrentItem as BaseItemDto).Id
);
},
async playQueueFrom(playFromIndex: number): Promise<void> {
await this.playbackManager.play({
item: this.playlist,
startFromIndex: playFromIndex,
initiator: this.playlist
});
await this.items.fetchAndAddPlaylist(this.playlist.Id as string);
}
}
});
</script>

<style lang="scss" scoped>
.list-draggable {
user-select: none;
min-height: 20px;
}
</style>
3 changes: 3 additions & 0 deletions frontend/pages/item/_itemId/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,9 @@
<v-col v-if="item.Type === 'BoxSet'" cols="12">
<collection-tabs :item="item" />
</v-col>
<v-col v-if="item.Type === 'Playlist'" cols="12">
<playlist-items :playlist="item" />
</v-col>
<v-col cols="12">
<related-items :id="$route.params.itemId" :item="item" />
</v-col>
Expand Down
Loading

0 comments on commit 46ab70b

Please sign in to comment.