forked from platypush/platypush
parent
2df88c1911
commit
d799d50391
5 changed files with 153 additions and 45 deletions
|
@ -5,15 +5,25 @@
|
||||||
Playlist name
|
Playlist name
|
||||||
</TextPrompt>
|
</TextPrompt>
|
||||||
|
|
||||||
<div class="playlists">
|
<div class="playlists-container">
|
||||||
|
<div class="header">
|
||||||
|
<div class="filter">
|
||||||
|
<input type="text"
|
||||||
|
placeholder="Filter playlists"
|
||||||
|
ref="playlistFilter"
|
||||||
|
@input="filter = $event.target.value">
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="playlist new-playlist">
|
<div class="playlist new-playlist">
|
||||||
<button @click="showNewPlaylist = true">
|
<button @click="showNewPlaylist = true">
|
||||||
<i class="fa fa-plus" />
|
<i class="fa fa-plus" />
|
||||||
Create new playlist
|
Create new playlist
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="playlist" v-for="playlist in playlists" :key="playlist.id">
|
<div class="playlists">
|
||||||
|
<div class="playlist" v-for="playlist in sortedPlaylists" :key="playlist.id">
|
||||||
<button @click="addToPlaylist(playlist.id)">
|
<button @click="addToPlaylist(playlist.id)">
|
||||||
<i class="fa fa-list" />
|
<i class="fa fa-list" />
|
||||||
{{ playlist.name }}
|
{{ playlist.name }}
|
||||||
|
@ -21,6 +31,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -41,13 +52,53 @@ export default {
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
filter: '',
|
||||||
loading: false,
|
loading: false,
|
||||||
playlists: [],
|
playlists: [],
|
||||||
showNewPlaylist: false,
|
showNewPlaylist: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
pluginName() {
|
||||||
|
switch (this.item.type) {
|
||||||
|
case 'youtube':
|
||||||
|
return 'youtube'
|
||||||
|
|
||||||
|
case 'jellyfin':
|
||||||
|
return 'media.jellyfin'
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
sortedPlaylists() {
|
||||||
|
return this.playlists
|
||||||
|
.filter((playlist) => playlist.name.toLowerCase().includes(this.filter.toLowerCase()))
|
||||||
|
.sort((a, b) => a.name.localeCompare(b.name))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
checkPlugin() {
|
||||||
|
if (!this.pluginName) {
|
||||||
|
this.notify({
|
||||||
|
title: 'Unsupported item type',
|
||||||
|
text: `Item type ${this.item.type} does not support playlists`,
|
||||||
|
warning: true,
|
||||||
|
image: {
|
||||||
|
icon: 'exclamation-triangle',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
console.warn(`Unsupported item type: ${this.item.type}`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
|
||||||
async createPlaylist(name) {
|
async createPlaylist(name) {
|
||||||
name = name?.trim()
|
name = name?.trim()
|
||||||
if (!name?.length)
|
if (!name?.length)
|
||||||
|
@ -56,23 +107,11 @@ export default {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const playlist = await this.request('youtube.create_playlist', {
|
const playlist = await this.request(`${this.pluginName}.create_playlist`, {
|
||||||
name: name,
|
name: name,
|
||||||
})
|
})
|
||||||
|
|
||||||
await this.request('youtube.add_to_playlist', {
|
await this.addToPlaylist(playlist.id)
|
||||||
playlist_id: playlist.id,
|
|
||||||
video_id: this.item.id || this.item.url,
|
|
||||||
})
|
|
||||||
|
|
||||||
this.$emit('done')
|
|
||||||
this.notify({
|
|
||||||
text: 'Playlist created and video added',
|
|
||||||
image: {
|
|
||||||
icon: 'check',
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
this.loading = false
|
this.loading = false
|
||||||
this.showNewPlaylist = false
|
this.showNewPlaylist = false
|
||||||
|
@ -80,26 +119,30 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
async refreshPlaylists() {
|
async refreshPlaylists() {
|
||||||
this.loading = true
|
if (!this.checkPlugin())
|
||||||
|
return
|
||||||
|
|
||||||
|
this.loading = true
|
||||||
try {
|
try {
|
||||||
this.playlists = await this.request('youtube.get_playlists')
|
this.playlists = await this.request(`${this.pluginName}.get_playlists`)
|
||||||
} finally {
|
} finally {
|
||||||
this.loading = false
|
this.loading = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async addToPlaylist(playlistId) {
|
async addToPlaylist(playlistId) {
|
||||||
this.loading = true
|
if (!this.checkPlugin())
|
||||||
|
return
|
||||||
|
|
||||||
|
this.loading = true
|
||||||
try {
|
try {
|
||||||
await this.request('youtube.add_to_playlist', {
|
await this.request(`${this.pluginName}.add_to_playlist`, {
|
||||||
playlist_id: playlistId,
|
playlist_id: playlistId,
|
||||||
video_id: this.item.id || this.item.url,
|
item_ids: [this.item.id || this.item.url],
|
||||||
})
|
})
|
||||||
|
|
||||||
this.notify({
|
this.notify({
|
||||||
text: 'Video added to playlist',
|
text: 'Item added to playlist',
|
||||||
image: {
|
image: {
|
||||||
icon: 'check',
|
icon: 'check',
|
||||||
}
|
}
|
||||||
|
@ -112,6 +155,15 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
loading() {
|
||||||
|
if (this.loading)
|
||||||
|
return
|
||||||
|
|
||||||
|
this.$nextTick(() => this.$refs.playlistFilter.focus())
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.refreshPlaylists()
|
this.refreshPlaylists()
|
||||||
},
|
},
|
||||||
|
@ -119,19 +171,47 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
$header-height: 6.5em;
|
||||||
|
|
||||||
.playlist-adder-container {
|
.playlist-adder-container {
|
||||||
min-width: 300px;
|
width: 30em;
|
||||||
height: 100%;
|
height: fit-content;
|
||||||
|
max-width: 90vw;
|
||||||
|
max-height: 70vh;
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
|
.playlists-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.header {
|
||||||
|
width: 100%;
|
||||||
|
height: $header-height;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.playlists {
|
.playlists {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
height: calc(100% - #{$header-height});
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.filter {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
input[type="text"] {
|
||||||
|
width: 100%;
|
||||||
|
margin: 0.5em 0.5em 0.25em 0.5em;
|
||||||
|
padding: 0.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.playlist {
|
.playlist {
|
||||||
button {
|
button {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -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"
|
||||||
|
@add-to-playlist="$emit('add-to-playlist', $event)"
|
||||||
@delete="deleteItem"
|
@delete="deleteItem"
|
||||||
@select="select"
|
@select="select"
|
||||||
@select-collection="selectCollection"
|
@select-collection="selectCollection"
|
||||||
|
|
|
@ -131,8 +131,8 @@ export default {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.request('media.jellyfin.playlist_move', {
|
await this.request('media.jellyfin.playlist_move', {
|
||||||
playlist: this.collection.id,
|
playlist_id: this.collection.id,
|
||||||
playlist_item_id: item.playlist_item_id,
|
item_id: item.playlist_item_id,
|
||||||
to_pos: to,
|
to_pos: to,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -92,7 +92,7 @@
|
||||||
@move="$emit('playlist-move', $event)"
|
@move="$emit('playlist-move', $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)"
|
@remove-from-playlist="removeFromPlaylist"
|
||||||
@select="selectedResult = $event"
|
@select="selectedResult = $event"
|
||||||
@view="$emit('view', $event)"
|
@view="$emit('view', $event)"
|
||||||
v-if="mediaItems?.length > 0" />
|
v-if="mediaItems?.length > 0" />
|
||||||
|
@ -131,7 +131,6 @@ export default {
|
||||||
'play',
|
'play',
|
||||||
'play-with-opts',
|
'play-with-opts',
|
||||||
'playlist-move',
|
'playlist-move',
|
||||||
'remove-from-playlist',
|
|
||||||
'select',
|
'select',
|
||||||
'select-collection',
|
'select-collection',
|
||||||
'view',
|
'view',
|
||||||
|
@ -178,7 +177,11 @@ export default {
|
||||||
mediaItems() {
|
mediaItems() {
|
||||||
return (
|
return (
|
||||||
this.sortedItems?.filter((item) => !['collection', 'artist', 'album'].includes(item.item_type)) ?? []
|
this.sortedItems?.filter((item) => !['collection', 'artist', 'album'].includes(item.item_type)) ?? []
|
||||||
).sort((a, b) => {
|
).map((item) => {
|
||||||
|
item.media_type = item.type
|
||||||
|
item.type = 'jellyfin'
|
||||||
|
return item
|
||||||
|
}).sort((a, b) => {
|
||||||
if (this.view === 'playlist') {
|
if (this.view === 'playlist') {
|
||||||
// Skip sorting if this is a playlist
|
// Skip sorting if this is a playlist
|
||||||
return 0
|
return 0
|
||||||
|
@ -219,6 +222,20 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
async removeFromPlaylist(item) {
|
||||||
|
this.loading_ = true
|
||||||
|
try {
|
||||||
|
await this.request('media.jellyfin.remove_from_playlist', {
|
||||||
|
playlist_id: this.collection.id,
|
||||||
|
item_ids: [item.playlist_item_id],
|
||||||
|
})
|
||||||
|
|
||||||
|
await this.refresh()
|
||||||
|
} finally {
|
||||||
|
this.loading_ = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
async selectArtist() {
|
async selectArtist() {
|
||||||
const artistId = this.displayedArtist?.id || this.getUrlArgs().artist
|
const artistId = this.displayedArtist?.id || this.getUrlArgs().artist
|
||||||
if (!artistId?.length)
|
if (!artistId?.length)
|
||||||
|
@ -319,13 +336,23 @@ export default {
|
||||||
break
|
break
|
||||||
|
|
||||||
case 'playlist':
|
case 'playlist':
|
||||||
this.items = await this.request(
|
this.items = this.collection?.item_type === 'playlist' ? (
|
||||||
|
await this.request(
|
||||||
'media.jellyfin.get_playlist_items',
|
'media.jellyfin.get_playlist_items',
|
||||||
{
|
{
|
||||||
playlist: this.collection.id,
|
playlist_id: this.collection.id,
|
||||||
limit: 25000,
|
limit: 25000,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
) : (
|
||||||
|
await this.request(
|
||||||
|
'media.jellyfin.get_items',
|
||||||
|
{
|
||||||
|
parent_id: this.collection.id,
|
||||||
|
limit: 25000,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
break
|
break
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -133,7 +133,7 @@ export default {
|
||||||
try {
|
try {
|
||||||
await this.request('youtube.remove_from_playlist', {
|
await this.request('youtube.remove_from_playlist', {
|
||||||
playlist_id: playlistId,
|
playlist_id: playlistId,
|
||||||
video_id: videoId,
|
item_ids: [videoId],
|
||||||
})
|
})
|
||||||
} finally {
|
} finally {
|
||||||
this.loading_ = false
|
this.loading_ = false
|
||||||
|
|
Loading…
Reference in a new issue