forked from platypush/platypush
[Youtube UI] Added playlist operations.
- `add_to_playlist` - `remove_from_playlist`
This commit is contained in:
parent
701623c99d
commit
d4354e81f8
12 changed files with 388 additions and 21 deletions
|
@ -43,13 +43,21 @@ export default {
|
|||
this.close()
|
||||
},
|
||||
|
||||
show() {
|
||||
open() {
|
||||
this.$refs.modal.show()
|
||||
},
|
||||
|
||||
close() {
|
||||
this.$refs.modal.hide()
|
||||
},
|
||||
|
||||
show() {
|
||||
this.open()
|
||||
},
|
||||
|
||||
hide() {
|
||||
this.close()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<form @submit.prevent="onConfirm">
|
||||
<div class="dialog-content">
|
||||
<slot />
|
||||
<input type="text" ref="input" />
|
||||
<input type="text" ref="input" v-model="value_" />
|
||||
</div>
|
||||
|
||||
<div class="buttons">
|
||||
|
@ -38,26 +38,79 @@ export default {
|
|||
type: String,
|
||||
default: "Cancel",
|
||||
},
|
||||
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
|
||||
value: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
value_: "",
|
||||
visible_: false,
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
onConfirm() {
|
||||
this.$emit('input', this.$refs.input.value)
|
||||
this.$emit('input', this.value_)
|
||||
this.close()
|
||||
},
|
||||
|
||||
show() {
|
||||
open() {
|
||||
if (this.visible_)
|
||||
return
|
||||
|
||||
this.value_ = this.value
|
||||
this.$refs.modal.show()
|
||||
this.visible_ = true
|
||||
this.focus()
|
||||
},
|
||||
|
||||
close() {
|
||||
if (!this.visible_)
|
||||
return
|
||||
|
||||
this.value_ = ""
|
||||
this.$refs.modal.hide()
|
||||
this.visible_ = false
|
||||
},
|
||||
|
||||
show() {
|
||||
this.open()
|
||||
},
|
||||
|
||||
hide() {
|
||||
this.close()
|
||||
},
|
||||
|
||||
focus() {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.input.focus()
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
visible(val) {
|
||||
if (val) {
|
||||
this.open()
|
||||
} else {
|
||||
this.close()
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.visible_ = this.visible
|
||||
this.value_ = this.value || ""
|
||||
this.$nextTick(() => {
|
||||
this.$refs.input.value = ""
|
||||
this.$refs.input.focus()
|
||||
})
|
||||
},
|
||||
|
|
|
@ -23,9 +23,11 @@
|
|||
<component
|
||||
:is="mediaProvider"
|
||||
:filter="filter"
|
||||
@add-to-playlist="$emit('add-to-playlist', $event)"
|
||||
@back="mediaProvider = null"
|
||||
@path-change="$emit('path-change', $event)"
|
||||
@play="$emit('play', $event)" />
|
||||
@play="$emit('play', $event)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</keep-alive>
|
||||
|
@ -39,8 +41,17 @@ import Utils from "@/Utils";
|
|||
import providersMetadata from "./Providers/meta.json";
|
||||
|
||||
export default {
|
||||
emits: ['path-change', 'play'],
|
||||
mixins: [Utils],
|
||||
emits: [
|
||||
'add-to-playlist',
|
||||
'create-playlist',
|
||||
'path-change',
|
||||
'play',
|
||||
'remove-from-playlist',
|
||||
'remove-playlist',
|
||||
'rename-playlist',
|
||||
],
|
||||
|
||||
components: {
|
||||
Browser,
|
||||
Loading,
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
:plugin-name="pluginName"
|
||||
:loading="loading"
|
||||
:filter="browserFilter"
|
||||
@add-to-playlist="addToPlaylistItem = $event"
|
||||
@select="onResultSelect($event)"
|
||||
@play="play"
|
||||
@view="view"
|
||||
|
@ -51,6 +52,7 @@
|
|||
v-else-if="selectedView === 'torrents'" />
|
||||
|
||||
<Browser :filter="browserFilter"
|
||||
@add-to-playlist="addToPlaylistItem = $event"
|
||||
@path-change="browserFilter = ''"
|
||||
@play="play($event)"
|
||||
v-else-if="selectedView === 'browser'" />
|
||||
|
@ -75,6 +77,16 @@
|
|||
<UrlPlayer :value="urlPlay" @input="urlPlay = $event.target.value" @play="playUrl($event)" />
|
||||
</Modal>
|
||||
</div>
|
||||
|
||||
<div class="add-to-playlist-container" v-if="addToPlaylistItem">
|
||||
<Modal title="Add to playlist" :visible="addToPlaylistItem != null" @close="addToPlaylistItem = null">
|
||||
<PlaylistAdder
|
||||
:item="addToPlaylistItem"
|
||||
@done="addToPlaylistItem = null"
|
||||
@close="addToPlaylistItem = null"
|
||||
/>
|
||||
</Modal>
|
||||
</div>
|
||||
</div>
|
||||
</keep-alive>
|
||||
</template>
|
||||
|
@ -88,6 +100,7 @@ import Header from "@/components/panels/Media/Header";
|
|||
import MediaUtils from "@/components/Media/Utils";
|
||||
import MediaView from "@/components/Media/View";
|
||||
import Nav from "@/components/panels/Media/Nav";
|
||||
import PlaylistAdder from "@/components/panels/Media/PlaylistAdder";
|
||||
import Results from "@/components/panels/Media/Results";
|
||||
import Subtitles from "@/components/panels/Media/Subtitles";
|
||||
import Transfers from "@/components/panels/Torrent/Transfers";
|
||||
|
@ -102,6 +115,7 @@ export default {
|
|||
MediaView,
|
||||
Modal,
|
||||
Nav,
|
||||
PlaylistAdder,
|
||||
Results,
|
||||
Subtitles,
|
||||
Transfers,
|
||||
|
@ -139,6 +153,7 @@ export default {
|
|||
awaitingPlayTorrent: null,
|
||||
urlPlay: null,
|
||||
browserFilter: null,
|
||||
addToPlaylistItem: null,
|
||||
torrentPlugin: null,
|
||||
torrentPlugins: [
|
||||
'torrent',
|
||||
|
@ -487,4 +502,10 @@ export default {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.add-to-playlist-container) {
|
||||
.body {
|
||||
padding: 0 !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
:class="{selected: selected}"
|
||||
@click.right.prevent="$refs.dropdown.toggle()"
|
||||
v-if="!hidden">
|
||||
|
||||
<div class="thumbnail">
|
||||
<MediaImage :item="item" @play="$emit('play')" />
|
||||
</div>
|
||||
|
@ -19,6 +20,10 @@
|
|||
v-if="item.type === 'torrent'" />
|
||||
<DropdownItem icon-class="fa fa-window-maximize" text="View in browser" @click="$emit('view')"
|
||||
v-if="item.type === 'file'" />
|
||||
<DropdownItem icon-class="fa fa-list" text="Add to playlist" @click="$emit('add-to-playlist')"
|
||||
v-if="item.type === 'youtube'" />
|
||||
<DropdownItem icon-class="fa fa-trash" text="Remove from playlist" @click="$refs.confirmPlaylistRemove.open()"
|
||||
v-if="playlist" />
|
||||
<DropdownItem icon-class="fa fa-info-circle" text="Info" @click="$emit('select')" />
|
||||
</Dropdown>
|
||||
</div>
|
||||
|
@ -35,10 +40,15 @@
|
|||
{{ formatDateTime(item.created_at, true) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ConfirmDialog ref="confirmPlaylistRemove" @input="$emit('remove-from-playlist')">
|
||||
Are you sure you want to remove this item from the playlist?
|
||||
</ConfirmDialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ConfirmDialog from "@/components/elements/ConfirmDialog";
|
||||
import Dropdown from "@/components/elements/Dropdown";
|
||||
import DropdownItem from "@/components/elements/DropdownItem";
|
||||
import Icons from "./icons.json";
|
||||
|
@ -46,9 +56,23 @@ import MediaImage from "./MediaImage";
|
|||
import Utils from "@/Utils";
|
||||
|
||||
export default {
|
||||
components: {Dropdown, DropdownItem, MediaImage},
|
||||
mixins: [Utils],
|
||||
emits: ['play', 'select', 'view', 'download'],
|
||||
components: {
|
||||
ConfirmDialog,
|
||||
Dropdown,
|
||||
DropdownItem,
|
||||
MediaImage,
|
||||
},
|
||||
|
||||
emits: [
|
||||
'add-to-playlist',
|
||||
'download',
|
||||
'play',
|
||||
'remove-from-playlist',
|
||||
'select',
|
||||
'view',
|
||||
],
|
||||
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
|
@ -64,6 +88,10 @@ export default {
|
|||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
|
||||
playlist: {
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
|
@ -87,6 +115,10 @@ export default {
|
|||
border: 1px solid transparent;
|
||||
border-bottom: 1px solid transparent !important;
|
||||
|
||||
@include from($tablet) {
|
||||
max-height: max(25em, 25%);
|
||||
}
|
||||
|
||||
&.selected {
|
||||
box-shadow: $border-shadow-bottom;
|
||||
background: $selected-bg;
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
<template>
|
||||
<div class="playlist-adder-container">
|
||||
<Loading v-if="loading" />
|
||||
<TextPrompt ref="newPlaylistName" :visible="showNewPlaylist" @input="createPlaylist($event)">
|
||||
Playlist name
|
||||
</TextPrompt>
|
||||
|
||||
<div class="playlists">
|
||||
<div class="playlist new-playlist">
|
||||
<button @click="showNewPlaylist = true">
|
||||
<i class="fa fa-plus" />
|
||||
Create new playlist
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="playlist" v-for="playlist in playlists" :key="playlist.id">
|
||||
<button @click="addToPlaylist(playlist.id)">
|
||||
<i class="fa fa-list" />
|
||||
{{ playlist.name }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Loading from "@/components/Loading";
|
||||
import Utils from "@/Utils";
|
||||
import TextPrompt from "@/components/elements/TextPrompt"
|
||||
|
||||
export default {
|
||||
emits: ['done'],
|
||||
mixins: [Utils],
|
||||
components: {Loading, TextPrompt},
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
playlists: [],
|
||||
showNewPlaylist: false,
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
async createPlaylist(name) {
|
||||
name = name?.trim()
|
||||
if (!name?.length)
|
||||
return
|
||||
|
||||
this.loading = true
|
||||
|
||||
try {
|
||||
const playlist = await this.request('youtube.create_playlist', {
|
||||
name: name,
|
||||
})
|
||||
|
||||
await this.request('youtube.add_to_playlist', {
|
||||
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 {
|
||||
this.loading = false
|
||||
this.showNewPlaylist = false
|
||||
}
|
||||
},
|
||||
|
||||
async refreshPlaylists() {
|
||||
this.loading = true
|
||||
|
||||
try {
|
||||
this.playlists = await this.request('youtube.get_playlists')
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
async addToPlaylist(playlistId) {
|
||||
this.loading = true
|
||||
|
||||
try {
|
||||
await this.request('youtube.add_to_playlist', {
|
||||
playlist_id: playlistId,
|
||||
video_id: this.item.id || this.item.url,
|
||||
})
|
||||
|
||||
this.notify({
|
||||
text: 'Video added to playlist',
|
||||
image: {
|
||||
icon: 'check',
|
||||
}
|
||||
})
|
||||
|
||||
this.$emit('done')
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.refreshPlaylists()
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.playlist-adder-container {
|
||||
min-width: 300px;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
.playlists {
|
||||
width: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.playlist {
|
||||
button {
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
padding: 0.5em 1em;
|
||||
border: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s, color 0.2s;
|
||||
|
||||
&:hover {
|
||||
background: $hover-bg;
|
||||
}
|
||||
|
||||
i {
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.new-playlist {
|
||||
button {
|
||||
font-weight: bold;
|
||||
border-bottom: $default-border;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -2,8 +2,18 @@
|
|||
import Utils from "@/Utils";
|
||||
|
||||
export default {
|
||||
emits: ['back', 'path-change', 'play'],
|
||||
mixins: [Utils],
|
||||
emits: [
|
||||
'add-to-playlist',
|
||||
'back',
|
||||
'create-playlist',
|
||||
'path-change',
|
||||
'play',
|
||||
'remove-from-playlist',
|
||||
'remove-playlist',
|
||||
'rename-playlist',
|
||||
],
|
||||
|
||||
props: {
|
||||
filter: {
|
||||
type: String,
|
||||
|
|
|
@ -8,17 +8,27 @@
|
|||
|
||||
<div class="body" v-else>
|
||||
<Feed :filter="filter"
|
||||
@play="$emit('play', $event)" v-if="selectedView === 'feed'" />
|
||||
@add-to-playlist="$emit('add-to-playlist', $event)"
|
||||
@play="$emit('play', $event)"
|
||||
v-if="selectedView === 'feed'"
|
||||
/>
|
||||
|
||||
<Playlists :filter="filter"
|
||||
:selected-playlist="selectedPlaylist"
|
||||
@add-to-playlist="$emit('add-to-playlist', $event)"
|
||||
@play="$emit('play', $event)"
|
||||
@remove-from-playlist="removeFromPlaylist"
|
||||
@select="onPlaylistSelected"
|
||||
v-else-if="selectedView === 'playlists'" />
|
||||
v-else-if="selectedView === 'playlists'"
|
||||
/>
|
||||
|
||||
<Subscriptions :filter="filter"
|
||||
:selected-channel="selectedChannel"
|
||||
@play="$emit('play', $event)"
|
||||
@select="onChannelSelected"
|
||||
v-else-if="selectedView === 'subscriptions'" />
|
||||
v-else-if="selectedView === 'subscriptions'"
|
||||
/>
|
||||
|
||||
<Index @select="selectView" v-else />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -87,6 +97,21 @@ export default {
|
|||
}
|
||||
},
|
||||
|
||||
async removeFromPlaylist(event) {
|
||||
const playlistId = event.playlist_id
|
||||
const videoId = event.item.url
|
||||
this.loading = true
|
||||
|
||||
try {
|
||||
await this.request('youtube.remove_from_playlist', {
|
||||
playlist_id: playlistId,
|
||||
video_id: videoId,
|
||||
})
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
selectView(view) {
|
||||
this.selectedView = view
|
||||
if (view === 'playlists')
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
:filter="filter"
|
||||
:sources="{'youtube': true}"
|
||||
:selected-result="selectedResult"
|
||||
@add-to-playlist="$emit('add-to-playlist', $event)"
|
||||
@select="selectedResult = $event"
|
||||
@play="$emit('play', $event)"
|
||||
v-else />
|
||||
|
@ -22,8 +23,12 @@ import Results from "@/components/panels/Media/Results";
|
|||
import Utils from "@/Utils";
|
||||
|
||||
export default {
|
||||
emits: ['play'],
|
||||
mixins: [Utils],
|
||||
emits: [
|
||||
'add-to-playlist',
|
||||
'play',
|
||||
],
|
||||
|
||||
components: {
|
||||
Loading,
|
||||
NoItems,
|
||||
|
|
|
@ -8,9 +8,12 @@
|
|||
<Results :results="items"
|
||||
:sources="{'youtube': true}"
|
||||
:filter="filter"
|
||||
:playlist="id"
|
||||
:selected-result="selectedResult"
|
||||
@select="selectedResult = $event"
|
||||
@add-to-playlist="$emit('add-to-playlist', $event)"
|
||||
@play="$emit('play', $event)"
|
||||
@remove-from-playlist="$emit('remove-from-playlist', $event)"
|
||||
@select="selectedResult = $event"
|
||||
v-else />
|
||||
</div>
|
||||
</template>
|
||||
|
@ -22,8 +25,13 @@ import Results from "@/components/panels/Media/Results";
|
|||
import Utils from "@/Utils";
|
||||
|
||||
export default {
|
||||
emits: ['play'],
|
||||
mixins: [Utils],
|
||||
emits: [
|
||||
'add-to-playlist',
|
||||
'play',
|
||||
'remove-from-playlist',
|
||||
],
|
||||
|
||||
components: {
|
||||
Loading,
|
||||
NoItems,
|
||||
|
|
|
@ -18,7 +18,14 @@
|
|||
</div>
|
||||
|
||||
<div class="playlist-body" v-else>
|
||||
<Playlist :id="selectedPlaylist" :filter="filter" @play="$emit('play', $event)" />
|
||||
<Playlist
|
||||
:id="selectedPlaylist"
|
||||
:filter="filter"
|
||||
:playlist="playlist"
|
||||
@add-to-playlist="$emit('add-to-playlist', $event)"
|
||||
@remove-from-playlist="$emit('remove-from-playlist', {item: $event, playlist_id: selectedPlaylist})"
|
||||
@play="$emit('play', $event)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -31,8 +38,17 @@ import Playlist from "./Playlist";
|
|||
import Utils from "@/Utils";
|
||||
|
||||
export default {
|
||||
emits: ['play', 'select'],
|
||||
mixins: [Utils],
|
||||
emits: [
|
||||
'add-to-playlist',
|
||||
'create-playlist',
|
||||
'play',
|
||||
'remove-from-playlist',
|
||||
'remove-playlist',
|
||||
'rename-playlist',
|
||||
'select',
|
||||
],
|
||||
|
||||
components: {
|
||||
Loading,
|
||||
MediaImage,
|
||||
|
|
|
@ -4,9 +4,12 @@
|
|||
<div class="grid" ref="grid" v-if="results?.length" @scroll="onScroll">
|
||||
<Item v-for="(item, i) in visibleResults"
|
||||
:key="i"
|
||||
:item="item"
|
||||
:selected="selectedResult === i"
|
||||
:hidden="!!Object.keys(sources || {}).length && !sources[item.type]"
|
||||
:item="item"
|
||||
:playlist="playlist"
|
||||
:selected="selectedResult === i"
|
||||
@add-to-playlist="$emit('add-to-playlist', item)"
|
||||
@remove-from-playlist="$emit('remove-from-playlist', item)"
|
||||
@select="$emit('select', i)"
|
||||
@play="$emit('play', item)"
|
||||
@view="$emit('view', item)"
|
||||
|
@ -30,7 +33,16 @@ import Modal from "@/components/Modal";
|
|||
|
||||
export default {
|
||||
components: {Info, Item, Loading, Modal},
|
||||
emits: ['select', 'play', 'view', 'download', 'scroll-end'],
|
||||
emits: [
|
||||
'add-to-playlist',
|
||||
'download',
|
||||
'play',
|
||||
'remove-from-playlist',
|
||||
'scroll-end',
|
||||
'select',
|
||||
'view',
|
||||
],
|
||||
|
||||
props: {
|
||||
loading: {
|
||||
type: Boolean,
|
||||
|
@ -64,6 +76,10 @@ export default {
|
|||
type: Number,
|
||||
default: 25,
|
||||
},
|
||||
|
||||
playlist: {
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
|
|
Loading…
Reference in a new issue