forked from platypush/platypush
[media.jellyfin] Playlist implementation [UI].
This commit is contained in:
parent
bc42ba16d7
commit
b646b5f3d7
7 changed files with 326 additions and 14 deletions
|
@ -27,6 +27,16 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="row duration" v-if="computedItem?.duration">
|
||||||
|
<div class="left side">Duration</div>
|
||||||
|
<div class="right side" v-text="formatDuration(computedItem.duration, true)" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row duration" v-if="computedItem?.n_items != null">
|
||||||
|
<div class="left side">Items</div>
|
||||||
|
<div class="right side" v-text="computedItem.n_items" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row direct-url" v-if="computedItem?.imdb_url">
|
<div class="row direct-url" v-if="computedItem?.imdb_url">
|
||||||
<div class="left side">ImDB URL</div>
|
<div class="left side">ImDB URL</div>
|
||||||
<div class="right side">
|
<div class="right side">
|
||||||
|
|
|
@ -14,10 +14,18 @@
|
||||||
<div class="left side"
|
<div class="left side"
|
||||||
:class="{'col-11': !listView, 'col-10': listView }"
|
:class="{'col-11': !listView, 'col-10': listView }"
|
||||||
@click.stop="$emit('select')">
|
@click.stop="$emit('select')">
|
||||||
<span class="track-number" v-if="listView && item.track_number">
|
<span class="track-number" v-if="playlistView">
|
||||||
|
{{ index + 1 }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="track-number" v-else-if="listView && item.track_number">
|
||||||
{{ item.track_number }}
|
{{ item.track_number }}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
<span class="artist" v-if="playlistView && item.artist">
|
||||||
|
{{ item.artist.name ?? item.artist }} —
|
||||||
|
</span>
|
||||||
|
|
||||||
{{item.title || item.name}}
|
{{item.title || item.name}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -111,6 +119,10 @@ export default {
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
index: {
|
||||||
|
type: Number,
|
||||||
|
},
|
||||||
|
|
||||||
listView: {
|
listView: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
|
@ -187,7 +199,7 @@ export default {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.item.type === 'youtube') {
|
if (['jellyfin', 'youtube'].includes(this.item.type)) {
|
||||||
actions.push({
|
actions.push({
|
||||||
iconClass: 'fa fa-list',
|
iconClass: 'fa fa-list',
|
||||||
text: 'Add to Playlist',
|
text: 'Add to Playlist',
|
||||||
|
@ -195,7 +207,7 @@ export default {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.item.type === 'youtube' && this.playlist?.length) {
|
if (['jellyfin', 'youtube'].includes(this.item.type) && this.playlist) {
|
||||||
actions.push({
|
actions.push({
|
||||||
iconClass: 'fa fa-trash',
|
iconClass: 'fa fa-trash',
|
||||||
text: 'Remove from Playlist',
|
text: 'Remove from Playlist',
|
||||||
|
@ -211,6 +223,10 @@ export default {
|
||||||
|
|
||||||
return actions
|
return actions
|
||||||
},
|
},
|
||||||
|
|
||||||
|
playlistView() {
|
||||||
|
return this.playlist && this.listView
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -308,6 +324,12 @@ export default {
|
||||||
&:hover {
|
&:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.artist {
|
||||||
|
font-weight: 300;
|
||||||
|
opacity: .75;
|
||||||
|
letter-spacing: 0.065em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.side {
|
.side {
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
<Media v-bind="componentData.props"
|
<Media v-bind="componentData.props"
|
||||||
v-on="componentData.on"
|
v-on="componentData.on"
|
||||||
:collection="collection"
|
:collection="collection"
|
||||||
|
@delete="deleteItem"
|
||||||
@select="select"
|
@select="select"
|
||||||
@select-collection="selectCollection"
|
@select-collection="selectCollection"
|
||||||
@view="$emit('view', $event)"
|
@view="$emit('view', $event)"
|
||||||
|
@ -148,6 +149,27 @@ export default {
|
||||||
this.collection = collection
|
this.collection = collection
|
||||||
this.select(collection)
|
this.select(collection)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async deleteItem(item_id) {
|
||||||
|
this.loading_ = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.request('media.jellyfin.delete_item', {
|
||||||
|
item_id: item_id,
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
this.loading_ = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.path.length > 1) {
|
||||||
|
if (this.path[this.path.length - 1].id === item_id) {
|
||||||
|
this.selectCollection(this.path[this.path.length - 2])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.collection = null
|
||||||
|
this.select(this.rootItem)
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
|
|
|
@ -22,12 +22,61 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="add-playlist floating-btn-container" v-if="isPlaylistsCollection">
|
||||||
|
<FloatingButton icon-class="fa fa-plus"
|
||||||
|
title="Create Playlist"
|
||||||
|
:disabled="loading"
|
||||||
|
@click="showNewPlaylist = true" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="add-playlist-modal" v-if="showNewPlaylist">
|
||||||
|
<Modal title="Create Playlist"
|
||||||
|
:visible="true"
|
||||||
|
@close="showNewPlaylist = false">
|
||||||
|
<form class="modal-body" @submit.prevent="createPlaylist">
|
||||||
|
<div class="row">
|
||||||
|
<label for="newPlaylistName">Playlist Name</label>
|
||||||
|
<input name="name"
|
||||||
|
type="text"
|
||||||
|
id="newPlaylistName"
|
||||||
|
placeholder="Playlist Name"
|
||||||
|
required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<label for="newPlaylistPublic">Public</label>
|
||||||
|
<input name="public" type="checkbox" checked id="newPlaylistPublic">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row buttons">
|
||||||
|
<button type="button" @click="showNewPlaylist = false">Cancel</button>
|
||||||
|
<button type="submit">Create</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import FloatingButton from "@/components/elements/FloatingButton";
|
||||||
|
import Modal from "@/components/Modal";
|
||||||
|
import Utils from '@/Utils'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
mixins: [Utils],
|
||||||
|
emits: ['refresh', 'select'],
|
||||||
|
components: {
|
||||||
|
FloatingButton,
|
||||||
|
Modal,
|
||||||
|
},
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
|
collection: {
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
|
||||||
filter: {
|
filter: {
|
||||||
type: String,
|
type: String,
|
||||||
},
|
},
|
||||||
|
@ -50,7 +99,9 @@ export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
fallbackImageCollections: {},
|
fallbackImageCollections: {},
|
||||||
|
loading: false,
|
||||||
maxResultIndex: this.batchItems,
|
maxResultIndex: this.batchItems,
|
||||||
|
showNewPlaylist: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -82,9 +133,23 @@ export default {
|
||||||
return a.name.localeCompare(b.name)
|
return a.name.localeCompare(b.name)
|
||||||
}).slice(0, this.maxResultIndex)
|
}).slice(0, this.maxResultIndex)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
isPlaylistsCollection() {
|
||||||
|
return this.collection?.collection_type === 'playlists'
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
async createPlaylist(event) {
|
||||||
|
const form = new FormData(event.target)
|
||||||
|
await this.request('media.jellyfin.create_playlist', {
|
||||||
|
name: form.get('name'),
|
||||||
|
public: form.get('public') === 'on',
|
||||||
|
})
|
||||||
|
|
||||||
|
this.$emit('refresh')
|
||||||
|
},
|
||||||
|
|
||||||
onImageError(collection) {
|
onImageError(collection) {
|
||||||
this.fallbackImageCollections[collection.id] = true
|
this.fallbackImageCollections[collection.id] = true
|
||||||
},
|
},
|
||||||
|
@ -102,6 +167,13 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
showNewPlaylist(value) {
|
||||||
|
if (value)
|
||||||
|
this.$nextTick(() => this.$el.querySelector('input[name="name"]').focus())
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.$el.parentElement?.addEventListener('scroll', this.onScroll)
|
this.$el.parentElement?.addEventListener('scroll', this.onScroll)
|
||||||
},
|
},
|
||||||
|
@ -149,5 +221,45 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:deep(.add-playlist-modal) {
|
||||||
|
.modal-body {
|
||||||
|
min-width: 30em;
|
||||||
|
max-width: calc(100% - 2em);
|
||||||
|
}
|
||||||
|
|
||||||
|
form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.row {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
|
||||||
|
label {
|
||||||
|
@extend .col-m-4;
|
||||||
|
@extend .col-s-12;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
@extend .col-m-8;
|
||||||
|
@extend .col-s-12;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.buttons {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin-left: 1em;
|
||||||
|
|
||||||
|
&[type="submit"] {
|
||||||
|
width: 10em;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -7,8 +7,11 @@
|
||||||
:filter="filter"
|
:filter="filter"
|
||||||
:loading="isLoading"
|
:loading="isLoading"
|
||||||
:path="path"
|
:path="path"
|
||||||
|
@add-to-playlist="$emit('add-to-playlist', $event)"
|
||||||
|
@delete="$emit('delete', $event)"
|
||||||
@play="$emit('play', $event)"
|
@play="$emit('play', $event)"
|
||||||
@play-with-opts="$emit('play-with-opts', $event)"
|
@play-with-opts="$emit('play-with-opts', $event)"
|
||||||
|
@remove-from-playlist="$emit('remove-from-playlist', $event)"
|
||||||
@select="selectedResult = $event; $emit('select', $event)"
|
@select="selectedResult = $event; $emit('select', $event)"
|
||||||
@select-collection="selectCollection"
|
@select-collection="selectCollection"
|
||||||
@view="$emit('view', $event)" />
|
@view="$emit('view', $event)" />
|
||||||
|
@ -16,7 +19,7 @@
|
||||||
|
|
||||||
<NoItems :with-shadow="false"
|
<NoItems :with-shadow="false"
|
||||||
v-else-if="!items?.length">
|
v-else-if="!items?.length">
|
||||||
No videos found.
|
No media found.
|
||||||
</NoItems>
|
</NoItems>
|
||||||
|
|
||||||
<div class="wrapper items-wrapper" v-else>
|
<div class="wrapper items-wrapper" v-else>
|
||||||
|
@ -25,6 +28,7 @@
|
||||||
:items="collections"
|
:items="collections"
|
||||||
:loading="isLoading"
|
:loading="isLoading"
|
||||||
:parent-id="collection?.id"
|
:parent-id="collection?.id"
|
||||||
|
@refresh="refresh"
|
||||||
@select="selectCollection"
|
@select="selectCollection"
|
||||||
v-if="collections.length > 0" />
|
v-if="collections.length > 0" />
|
||||||
|
|
||||||
|
@ -54,7 +58,18 @@ import Results from "@/components/panels/Media/Results";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [Mixin],
|
mixins: [Mixin],
|
||||||
emits: ['select', 'select-collection'],
|
emits: [
|
||||||
|
'add-to-playlist',
|
||||||
|
'delete',
|
||||||
|
'download',
|
||||||
|
'play',
|
||||||
|
'play-with-opts',
|
||||||
|
'remove-from-playlist',
|
||||||
|
'select',
|
||||||
|
'select-collection',
|
||||||
|
'view',
|
||||||
|
],
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
Collections,
|
Collections,
|
||||||
Loading,
|
Loading,
|
||||||
|
@ -65,11 +80,11 @@ export default {
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
collections() {
|
collections() {
|
||||||
return this.sortedItems?.filter((item) => item.item_type === 'collection') ?? []
|
return this.sortedItems?.filter((item) => ['collection', 'playlist'].includes(item.item_type)) ?? []
|
||||||
},
|
},
|
||||||
|
|
||||||
mediaItems() {
|
mediaItems() {
|
||||||
const items = this.sortedItems?.filter((item) => item.item_type !== 'collection') ?? []
|
const items = this.sortedItems?.filter((item) => !['collection', 'playlist'].includes(item.item_type)) ?? []
|
||||||
|
|
||||||
if (this.collection && (!this.collection.collection_type || this.collection.collection_type === 'books')) {
|
if (this.collection && (!this.collection.collection_type || this.collection.collection_type === 'books')) {
|
||||||
return items.sort((a, b) => {
|
return items.sort((a, b) => {
|
||||||
|
@ -193,5 +208,18 @@ export default {
|
||||||
.music-wrapper {
|
.music-wrapper {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:deep(.floating-btn-container) {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 5.5em;
|
||||||
|
|
||||||
|
@include until($tablet) {
|
||||||
|
right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include from($tablet) {
|
||||||
|
right: 2.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -2,11 +2,7 @@
|
||||||
<div class="music index">
|
<div class="music index">
|
||||||
<Loading v-if="isLoading" />
|
<Loading v-if="isLoading" />
|
||||||
|
|
||||||
<NoItems :with-shadow="false" v-else-if="!items?.length">
|
<main :class="containerClass">
|
||||||
No music found.
|
|
||||||
</NoItems>
|
|
||||||
|
|
||||||
<main :class="{ album: view === 'album', artist: view === 'artist' }" v-else>
|
|
||||||
<div class="artist header" v-if="view === 'artist'">
|
<div class="artist header" v-if="view === 'artist'">
|
||||||
<div class="image" v-if="collection.image">
|
<div class="image" v-if="collection.image">
|
||||||
<img :src="collection.image" />
|
<img :src="collection.image" />
|
||||||
|
@ -17,7 +13,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="album header" v-if="view === 'album'">
|
<div class="album header" v-else-if="view === 'album'">
|
||||||
<div class="image" v-if="collection.image">
|
<div class="image" v-if="collection.image">
|
||||||
<img :src="collection.image" />
|
<img :src="collection.image" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -44,6 +40,38 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="playlist header" v-else-if="view === 'playlist'">
|
||||||
|
<div class="image" v-if="collection.image">
|
||||||
|
<img :src="collection.image" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info">
|
||||||
|
<h1 v-text="collection.name" />
|
||||||
|
<div class="details">
|
||||||
|
<div class="row" v-if="collection.duration">
|
||||||
|
<span class="label">Duration:</span>
|
||||||
|
<span class="value" v-text="formatDuration(collection.duration, true)" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="actions">
|
||||||
|
<Dropdown title="Playlist Actions">
|
||||||
|
<DropdownItem text="Remove Playlist"
|
||||||
|
icon-class="fas fa-trash"
|
||||||
|
@input="$refs.deleteConfirmDialog.show()" />
|
||||||
|
|
||||||
|
<DropdownItem text="Info"
|
||||||
|
icon-class="fas fa-info"
|
||||||
|
@input="showInfoModal = true" />
|
||||||
|
</Dropdown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<NoItems :with-shadow="false" v-if="!items?.length">
|
||||||
|
No media found.
|
||||||
|
</NoItems>
|
||||||
|
|
||||||
<Collections :collection="collection"
|
<Collections :collection="collection"
|
||||||
:filter="filter"
|
:filter="filter"
|
||||||
:items="collections"
|
:items="collections"
|
||||||
|
@ -56,6 +84,7 @@
|
||||||
:sources="{'jellyfin': true}"
|
:sources="{'jellyfin': true}"
|
||||||
:filter="filter"
|
:filter="filter"
|
||||||
:list-view="true"
|
:list-view="true"
|
||||||
|
:playlist="collection?.item_type === 'playlist' ? collection : null"
|
||||||
:selected-result="selectedResult"
|
:selected-result="selectedResult"
|
||||||
:show-date="false"
|
:show-date="false"
|
||||||
@add-to-playlist="$emit('add-to-playlist', $event)"
|
@add-to-playlist="$emit('add-to-playlist', $event)"
|
||||||
|
@ -66,23 +95,54 @@
|
||||||
@select="selectedResult = $event"
|
@select="selectedResult = $event"
|
||||||
@view="$emit('view', $event)"
|
@view="$emit('view', $event)"
|
||||||
v-if="mediaItems?.length > 0" />
|
v-if="mediaItems?.length > 0" />
|
||||||
|
|
||||||
|
<div class="collection-modal" v-if="showInfoModal">
|
||||||
|
<Modal title="Collection Info" visible @close="showInfoModal = false">
|
||||||
|
<Info :item="collection" :pluginName="pluginName" />
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ConfirmDialog ref="deleteConfirmDialog" @input="$emit('delete', collection.id)">
|
||||||
|
Are you sure you want to delete this playlist?
|
||||||
|
</ConfirmDialog>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Collections from "@/components/panels/Media/Providers/Jellyfin/Collections";
|
import Collections from "@/components/panels/Media/Providers/Jellyfin/Collections";
|
||||||
|
import ConfirmDialog from "@/components/elements/ConfirmDialog";
|
||||||
|
import Dropdown from "@/components/elements/Dropdown";
|
||||||
|
import DropdownItem from "@/components/elements/DropdownItem";
|
||||||
|
import Info from "@/components/panels/Media/Info";
|
||||||
import Loading from "@/components/Loading";
|
import Loading from "@/components/Loading";
|
||||||
import Mixin from "@/components/panels/Media/Providers/Jellyfin/Mixin";
|
import Mixin from "@/components/panels/Media/Providers/Jellyfin/Mixin";
|
||||||
|
import Modal from "@/components/Modal";
|
||||||
import NoItems from "@/components/elements/NoItems";
|
import NoItems from "@/components/elements/NoItems";
|
||||||
import Results from "@/components/panels/Media/Results";
|
import Results from "@/components/panels/Media/Results";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [Mixin],
|
mixins: [Mixin],
|
||||||
emits: ['select', 'select-collection'],
|
emits: [
|
||||||
|
'add-to-playlist',
|
||||||
|
'delete',
|
||||||
|
'download',
|
||||||
|
'play',
|
||||||
|
'play-with-opts',
|
||||||
|
'remove-from-playlist',
|
||||||
|
'select',
|
||||||
|
'select-collection',
|
||||||
|
'view',
|
||||||
|
],
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
Collections,
|
Collections,
|
||||||
|
ConfirmDialog,
|
||||||
|
Dropdown,
|
||||||
|
DropdownItem,
|
||||||
|
Info,
|
||||||
Loading,
|
Loading,
|
||||||
|
Modal,
|
||||||
NoItems,
|
NoItems,
|
||||||
Results,
|
Results,
|
||||||
},
|
},
|
||||||
|
@ -90,6 +150,7 @@ export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
artist: null,
|
artist: null,
|
||||||
|
showInfoModal: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -100,6 +161,14 @@ export default {
|
||||||
).sort((a, b) => a.name.localeCompare(b.name))
|
).sort((a, b) => a.name.localeCompare(b.name))
|
||||||
},
|
},
|
||||||
|
|
||||||
|
containerClass() {
|
||||||
|
return {
|
||||||
|
artist: this.view === 'artist',
|
||||||
|
album: this.view === 'album',
|
||||||
|
playlist: this.view === 'playlist',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
displayedArtist() {
|
displayedArtist() {
|
||||||
return this.artist || this.collection?.artist
|
return this.artist || this.collection?.artist
|
||||||
},
|
},
|
||||||
|
@ -134,6 +203,8 @@ export default {
|
||||||
return 'artist'
|
return 'artist'
|
||||||
case 'album':
|
case 'album':
|
||||||
return 'album'
|
return 'album'
|
||||||
|
case 'playlist':
|
||||||
|
return 'playlist'
|
||||||
default:
|
default:
|
||||||
return 'index'
|
return 'index'
|
||||||
}
|
}
|
||||||
|
@ -240,6 +311,16 @@ export default {
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
case 'playlist':
|
||||||
|
this.items = await this.request(
|
||||||
|
'media.jellyfin.get_items',
|
||||||
|
{
|
||||||
|
parent_id: this.collection.id,
|
||||||
|
limit: 25000,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
break
|
||||||
|
|
||||||
default:
|
default:
|
||||||
this.artist = null
|
this.artist = null
|
||||||
this.items = await this.request(
|
this.items = await this.request(
|
||||||
|
@ -274,6 +355,8 @@ export default {
|
||||||
|
|
||||||
$artist-header-height: 5em;
|
$artist-header-height: 5em;
|
||||||
$album-header-height: 10em;
|
$album-header-height: 10em;
|
||||||
|
$playlist-header-height: 5em;
|
||||||
|
$actions-dropdown-width: 5em;
|
||||||
|
|
||||||
.index {
|
.index {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -313,6 +396,12 @@ $album-header-height: 10em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.playlist {
|
||||||
|
.media-results {
|
||||||
|
height: calc(100% - #{$playlist-header-height} - 0.5em);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.index {
|
.index {
|
||||||
height: fit-content;
|
height: fit-content;
|
||||||
}
|
}
|
||||||
|
@ -363,6 +452,32 @@ $album-header-height: 10em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.playlist {
|
||||||
|
height: $playlist-header-height;
|
||||||
|
border-bottom: 1px solid $default-shadow-color;
|
||||||
|
|
||||||
|
.image {
|
||||||
|
img {
|
||||||
|
width: calc($playlist-header-height - 0.5em);
|
||||||
|
height: calc($playlist-header-height - 0.5em);
|
||||||
|
padding: 0.25em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
width: calc(100% - #{$actions-dropdown-width});
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
width: $actions-dropdown-width;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
margin-right: -0.75em;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.image {
|
.image {
|
||||||
margin-right: 1em;
|
margin-right: 1em;
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
<Item v-for="(item, i) in visibleResults"
|
<Item v-for="(item, i) in visibleResults"
|
||||||
:key="i"
|
:key="i"
|
||||||
:hidden="!!Object.keys(sources || {}).length && !sources[item.type]"
|
:hidden="!!Object.keys(sources || {}).length && !sources[item.type]"
|
||||||
|
:index="i"
|
||||||
:item="item"
|
:item="item"
|
||||||
:list-view="listView"
|
:list-view="listView"
|
||||||
:playlist="playlist"
|
:playlist="playlist"
|
||||||
|
@ -184,6 +185,8 @@ export default {
|
||||||
|
|
||||||
&.list {
|
&.list {
|
||||||
:deep(.grid) {
|
:deep(.grid) {
|
||||||
|
height: fit-content;
|
||||||
|
max-height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
Loading…
Reference in a new issue