Finalized new music.mpd web interface
This commit is contained in:
parent
d2887b7454
commit
67d3b40772
17 changed files with 292 additions and 34 deletions
2
platypush/backend/http/dist/index.html
vendored
2
platypush/backend/http/dist/index.html
vendored
|
@ -1 +1 @@
|
|||
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/favicon.ico"><title>platypush</title><link href="/static/css/chunk-24ff873d.64d9bc0b.css" rel="prefetch"><link href="/static/css/chunk-3b44ec4e.0c4a18da.css" rel="prefetch"><link href="/static/css/chunk-45939517.e4a1ddf3.css" rel="prefetch"><link href="/static/css/chunk-4bbbb9a3.3108d379.css" rel="prefetch"><link href="/static/css/chunk-4c0b0f48.009b6a70.css" rel="prefetch"><link href="/static/css/chunk-4eeb8349.2026dd4f.css" rel="prefetch"><link href="/static/css/chunk-53360c78.c486a396.css" rel="prefetch"><link href="/static/css/chunk-62a3d08e.6cb54f10.css" rel="prefetch"><link href="/static/css/chunk-d8561e02.b52f89a0.css" rel="prefetch"><link href="/static/css/chunk-e8078048.c6785c78.css" rel="prefetch"><link href="/static/css/chunk-e9fa4af6.f205d678.css" rel="prefetch"><link href="/static/js/chunk-24ff873d.691c883d.js" rel="prefetch"><link href="/static/js/chunk-2d2091df.1e51ae4c.js" rel="prefetch"><link href="/static/js/chunk-2d21da1a.911f2770.js" rel="prefetch"><link href="/static/js/chunk-3b44ec4e.904c7e10.js" rel="prefetch"><link href="/static/js/chunk-45939517.c0034c6b.js" rel="prefetch"><link href="/static/js/chunk-4bbbb9a3.251fff37.js" rel="prefetch"><link href="/static/js/chunk-4c0b0f48.366980a2.js" rel="prefetch"><link href="/static/js/chunk-4eeb8349.5c94d58c.js" rel="prefetch"><link href="/static/js/chunk-53360c78.51ee7c96.js" rel="prefetch"><link href="/static/js/chunk-62a3d08e.17d3c86d.js" rel="prefetch"><link href="/static/js/chunk-d8561e02.1e366cb3.js" rel="prefetch"><link href="/static/js/chunk-e8078048.ce29b8d4.js" rel="prefetch"><link href="/static/js/chunk-e9fa4af6.567a1ae1.js" rel="prefetch"><link href="/static/css/app.4868c461.css" rel="preload" as="style"><link href="/static/css/chunk-vendors.5dad8b00.css" rel="preload" as="style"><link href="/static/js/app.6e791066.js" rel="preload" as="script"><link href="/static/js/chunk-vendors.30e3a6cb.js" rel="preload" as="script"><link href="/static/css/chunk-vendors.5dad8b00.css" rel="stylesheet"><link href="/static/css/app.4868c461.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but platypush doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="/static/js/chunk-vendors.30e3a6cb.js"></script><script src="/static/js/app.6e791066.js"></script></body></html>
|
||||
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/favicon.ico"><title>platypush</title><link href="/static/css/chunk-24ff873d.64d9bc0b.css" rel="prefetch"><link href="/static/css/chunk-3b44ec4e.0c4a18da.css" rel="prefetch"><link href="/static/css/chunk-45939517.e4a1ddf3.css" rel="prefetch"><link href="/static/css/chunk-4bbbb9a3.3108d379.css" rel="prefetch"><link href="/static/css/chunk-4c0b0f48.009b6a70.css" rel="prefetch"><link href="/static/css/chunk-4eeb8349.2026dd4f.css" rel="prefetch"><link href="/static/css/chunk-53360c78.c486a396.css" rel="prefetch"><link href="/static/css/chunk-5fea187e.d6e3f8eb.css" rel="prefetch"><link href="/static/css/chunk-62a3d08e.6cb54f10.css" rel="prefetch"><link href="/static/css/chunk-d8561e02.b52f89a0.css" rel="prefetch"><link href="/static/css/chunk-e8078048.c6785c78.css" rel="prefetch"><link href="/static/js/chunk-24ff873d.691c883d.js" rel="prefetch"><link href="/static/js/chunk-2d2091df.1e51ae4c.js" rel="prefetch"><link href="/static/js/chunk-2d21da1a.adf909a2.js" rel="prefetch"><link href="/static/js/chunk-3b44ec4e.904c7e10.js" rel="prefetch"><link href="/static/js/chunk-45939517.c0034c6b.js" rel="prefetch"><link href="/static/js/chunk-4bbbb9a3.251fff37.js" rel="prefetch"><link href="/static/js/chunk-4c0b0f48.366980a2.js" rel="prefetch"><link href="/static/js/chunk-4eeb8349.5c94d58c.js" rel="prefetch"><link href="/static/js/chunk-53360c78.51ee7c96.js" rel="prefetch"><link href="/static/js/chunk-5fea187e.4466d92f.js" rel="prefetch"><link href="/static/js/chunk-62a3d08e.17d3c86d.js" rel="prefetch"><link href="/static/js/chunk-d8561e02.1e366cb3.js" rel="prefetch"><link href="/static/js/chunk-e8078048.ce29b8d4.js" rel="prefetch"><link href="/static/css/app.4868c461.css" rel="preload" as="style"><link href="/static/css/chunk-vendors.5dad8b00.css" rel="preload" as="style"><link href="/static/js/app.3770dd06.js" rel="preload" as="script"><link href="/static/js/chunk-vendors.30e3a6cb.js" rel="preload" as="script"><link href="/static/css/chunk-vendors.5dad8b00.css" rel="stylesheet"><link href="/static/css/app.4868c461.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but platypush doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="/static/js/chunk-vendors.30e3a6cb.js"></script><script src="/static/js/app.3770dd06.js"></script></body></html>
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
platypush/backend/http/dist/static/js/chunk-2d21da1a.adf909a2.js
vendored
Normal file
2
platypush/backend/http/dist/static/js/chunk-2d21da1a.adf909a2.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
platypush/backend/http/dist/static/js/chunk-2d21da1a.adf909a2.js.map
vendored
Normal file
1
platypush/backend/http/dist/static/js/chunk-2d21da1a.adf909a2.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
2
platypush/backend/http/dist/static/js/chunk-5fea187e.4466d92f.js
vendored
Normal file
2
platypush/backend/http/dist/static/js/chunk-5fea187e.4466d92f.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
platypush/backend/http/dist/static/js/chunk-5fea187e.4466d92f.js.map
vendored
Normal file
1
platypush/backend/http/dist/static/js/chunk-5fea187e.4466d92f.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -223,13 +223,14 @@ export default {
|
|||
|
||||
mounted() {
|
||||
const self = this
|
||||
this.lastSync = this.getTime()
|
||||
|
||||
this.$watch(() => self.track, (track) => {
|
||||
this.$watch(() => this.track, (track) => {
|
||||
if (!track || self.status?.state !== 'play')
|
||||
self.lastSync = this.getTime()
|
||||
})
|
||||
|
||||
this.$watch(() => self.status, () => {
|
||||
this.$watch(() => this.status, () => {
|
||||
self.lastSync = this.getTime()
|
||||
})
|
||||
|
||||
|
|
|
@ -29,7 +29,12 @@
|
|||
<Search :loading="loading" v-else-if="selectedView === 'search'" @search="search"
|
||||
:results="searchResults" @clear="$emit('search-clear')" @info="$emit('info', $event)"
|
||||
@play="$emit('play', $event)" @load="$emit('add-to-tracklist', $event)"
|
||||
@add-to-playlist="openAddToPlaylist"/>
|
||||
@add-to-playlist="openAddToPlaylist" />
|
||||
|
||||
<Library :loading="loading" v-else-if="selectedView === 'library'" @search="search"
|
||||
:results="libraryResults" :path="path" @clear="$emit('search-clear')" @info="$emit('info', $event)"
|
||||
@play="$emit('play', $event)" @load="$emit('add-to-tracklist', $event)"
|
||||
@add-to-playlist="openAddToPlaylist" @cd="$emit('cd', $event)" />
|
||||
</div>
|
||||
</main>
|
||||
</MediaView>
|
||||
|
@ -71,6 +76,16 @@
|
|||
<div class="col-3 attr">Duration</div>
|
||||
<div class="col-9 value" v-text="convertTime(trackInfo.time)" />
|
||||
</div>
|
||||
|
||||
<div class="row track" v-if="trackInfo.track">
|
||||
<div class="col-3 attr">Track</div>
|
||||
<div class="col-9 value" v-text="trackInfo.track" />
|
||||
</div>
|
||||
|
||||
<div class="row disc" v-if="trackInfo.disc">
|
||||
<div class="col-3 attr">Disc</div>
|
||||
<div class="col-9 value" v-text="trackInfo.disc" />
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</div>
|
||||
|
@ -112,6 +127,7 @@ import Nav from "@/components/panels/Music/Nav";
|
|||
import Playlist from "@/components/panels/Music/Playlist";
|
||||
import Playlists from "@/components/panels/Music/Playlists";
|
||||
import Search from "@/components/panels/Music/Search";
|
||||
import Library from "@/components/panels/Music/Library";
|
||||
import Utils from "@/Utils";
|
||||
|
||||
export default {
|
||||
|
@ -120,10 +136,10 @@ export default {
|
|||
'status-update', 'playlist-update', 'new-playing-track', 'add-to-tracklist', 'remove-from-tracklist',
|
||||
'swap-tracks', 'play-playlist', 'load-playlist', 'remove-playlist', 'tracklist-move', 'tracklist-save',
|
||||
'add-to-tracklist-from-edited-playlist', 'remove-from-playlist', 'info', 'playlist-add', 'add-to-playlist',
|
||||
'playlist-track-move', 'search', 'search-clear'],
|
||||
'playlist-track-move', 'search', 'search-clear', 'cd'],
|
||||
|
||||
mixins: [Utils, MediaUtils],
|
||||
components: {Loading, Modal, Nav, MediaView, Playlist, Playlists, FormFooter, Search},
|
||||
components: {Loading, Modal, Nav, MediaView, Playlist, Playlists, FormFooter, Search, Library},
|
||||
props: {
|
||||
pluginName: {
|
||||
type: String,
|
||||
|
@ -171,6 +187,14 @@ export default {
|
|||
searchResults: {
|
||||
type: Array,
|
||||
},
|
||||
|
||||
libraryResults: {
|
||||
type: Array,
|
||||
},
|
||||
|
||||
path: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
|
|
|
@ -0,0 +1,217 @@
|
|||
<template>
|
||||
<div class="library fade-in">
|
||||
<Loading v-if="loading" />
|
||||
|
||||
<MusicHeader>
|
||||
<label class="search-box">
|
||||
<input type="search" placeholder="Filter" v-model="filter">
|
||||
</label>
|
||||
</MusicHeader>
|
||||
|
||||
<div class="results">
|
||||
<div class="row track back-track" @click="back" v-if="path !== '/'">
|
||||
<div class="icon-container">
|
||||
<i class="icon fa fa-folder" />
|
||||
</div>
|
||||
<div class="result-container">
|
||||
<div class="title">..</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row track" :class="{selected: selectedResults.has(i), hidden: !displayedResults.has(i)}"
|
||||
v-for="(result, i) in results" :key="i" @click="resultClick(i, $event)">
|
||||
<div class="col-10 left-side">
|
||||
<div class="icon-container">
|
||||
<i class="icon fa fa-folder" v-if="result.directory" />
|
||||
<i class="icon fa fa-music" v-else-if="result.file" />
|
||||
</div>
|
||||
|
||||
<div class="info">
|
||||
<div class="title">
|
||||
<span v-if="result.directory" v-text="result.directory.split('/').pop()" />
|
||||
<span v-else-if="result.title" v-text="result.title" />
|
||||
</div>
|
||||
|
||||
<div class="artist-album">
|
||||
<div class="artist" v-text="result.artist" v-if="result.artist?.length" />
|
||||
<div class="album" v-text="result.album" v-if="result.album?.length" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-2 right-side">
|
||||
<span class="duration" v-text="result.time && parseInt(result.time) ? convertTime(result.time) : '-:--'" />
|
||||
|
||||
<span class="actions">
|
||||
<Dropdown title="Actions" icon-class="fa fa-ellipsis-h">
|
||||
<DropdownItem text="Play" icon-class="fa fa-play" @click="play(i)" />
|
||||
<DropdownItem text="Add to queue" icon-class="fa fa-plus" @click="load(i)" />
|
||||
<DropdownItem text="Add to playlist" icon-class="fa fa-list-ul" @click="$emit('add-to-playlist', result)" />
|
||||
<DropdownItem text="Info" icon-class="fa fa-info" @click="$emit('info', result)" />
|
||||
</Dropdown>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Loading from "@/components/Loading";
|
||||
import Dropdown from "@/components/elements/Dropdown";
|
||||
import DropdownItem from "@/components/elements/DropdownItem";
|
||||
import MediaUtils from "@/components/Media/Utils";
|
||||
import MusicHeader from "@/components/panels/Music/Header";
|
||||
|
||||
export default {
|
||||
name: "Library",
|
||||
components: {Dropdown, DropdownItem, MusicHeader, Loading},
|
||||
mixins: [MediaUtils],
|
||||
emits: ['search', 'play', 'load', 'add-to-playlist', 'info', 'cd'],
|
||||
props: {
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
|
||||
results: {
|
||||
type: Array,
|
||||
},
|
||||
|
||||
path: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
selectedResults: new Set(),
|
||||
filter: '',
|
||||
query: {
|
||||
any: '',
|
||||
artist: '',
|
||||
title: '',
|
||||
album: '',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
displayedResults() {
|
||||
return new Set([...Array(this.results?.length || 0).keys()].filter((i) => {
|
||||
const result = this.results[i]
|
||||
if (!this.filter?.length)
|
||||
return result
|
||||
|
||||
const filter = this.filter.toLowerCase()
|
||||
return (result?.artist || '').toLowerCase().indexOf(filter) >= 0 ||
|
||||
(result?.title || '').toLowerCase().indexOf(filter) >= 0 ||
|
||||
(result?.album || '').toLowerCase().indexOf(filter) >= 0 ||
|
||||
(result?.directory || '').toLowerCase().indexOf(filter) >= 0
|
||||
}))
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
resultClick(pos, event) {
|
||||
if (event.shiftKey) {
|
||||
if (this.selectedResults.size > 0 && !this.selectedResults.has(pos)) {
|
||||
const results = [...this.selectedResults]
|
||||
const min = Math.min(Math.min(results), pos)
|
||||
const max = Math.max(Math.max(results), pos)
|
||||
this.selectedResults = new Set([...Array(max-min+1).keys()].map((i) => i+min))
|
||||
}
|
||||
} else if (event.ctrlKey) {
|
||||
if (this.selectedResults.has(pos))
|
||||
this.selectedResults.delete(pos)
|
||||
else
|
||||
this.selectedResults.add(pos)
|
||||
} else {
|
||||
if (this.results[pos].directory) {
|
||||
this.$emit('cd', this.results[pos].directory)
|
||||
} else {
|
||||
this.selectedResults = new Set()
|
||||
if (this.selectedResults.has(pos))
|
||||
this.selectedResults.delete(pos)
|
||||
else
|
||||
this.selectedResults.add(pos)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
play(pos) {
|
||||
this.$emit('play', this.results[pos])
|
||||
if (this.selectedResults.size)
|
||||
this.selectedResults.forEach((result) => {
|
||||
this.$emit('load', result)
|
||||
})
|
||||
},
|
||||
|
||||
load(pos) {
|
||||
if (!this.selectedResults.has(pos))
|
||||
this.selectedResults.add(pos)
|
||||
|
||||
this.selectedResults.forEach((i) => {
|
||||
this.$emit('load', this.results[i])
|
||||
})
|
||||
},
|
||||
|
||||
back() {
|
||||
const path = this.path.split('/')
|
||||
this.$emit('cd', path.slice(0, path.length-1).join('/'))
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import 'track.scss';
|
||||
|
||||
.library {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.results {
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
|
||||
.track {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: left;
|
||||
|
||||
.left-side {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
opacity: .5;
|
||||
margin-right: .75em;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep(.header) {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
|
||||
.search-box {
|
||||
width: 70%;
|
||||
|
||||
input[type=search] {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.buttons {
|
||||
width: 30%;
|
||||
display: inline-flex;
|
||||
justify-content: right;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -21,11 +21,9 @@
|
|||
border-top: 2px solid $default-hover-fg;
|
||||
}
|
||||
|
||||
.title, .artist, .album, .duration {
|
||||
&::selection {
|
||||
background: rgba(0, 0, 0, 0) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 1em;
|
||||
|
|
|
@ -2,18 +2,18 @@
|
|||
<Loading v-if="loading" />
|
||||
<MusicPlugin plugin-name="music.mpd" :loading="loading" :config="config" :tracks="tracks" :status="status"
|
||||
:playlists="playlists" :edited-playlist="editedPlaylist" :edited-playlist-tracks="editedPlaylistTracks"
|
||||
:track-info="trackInfo" :search-results="searchResults" @play="play" @pause="pause" @stop="stop"
|
||||
@previous="previous" @next="next" @clear="clear" @set-volume="setVolume" @seek="seek" @consume="consume"
|
||||
@random="random" @repeat="repeat" @status-update="refreshStatus(true)"
|
||||
@playlist-update="refresh(true)" @new-playing-track="refreshStatus(true)"
|
||||
@remove-from-tracklist="removeFromTracklist" @add-to-tracklist="addToTracklist" @swap-tracks="swapTracks"
|
||||
@load-playlist="loadPlaylist" @play-playlist="playPlaylist" @remove-playlist="removePlaylist"
|
||||
@tracklist-move="moveTracklistTracks" @tracklist-save="saveToPlaylist"
|
||||
@playlist-edit="playlistEditChanged"
|
||||
:track-info="trackInfo" :search-results="searchResults" :library-results="libraryResults" :path="path"
|
||||
@play="play" @pause="pause" @stop="stop" @previous="previous" @next="next" @clear="clear"
|
||||
@set-volume="setVolume" @seek="seek" @consume="consume" @random="random" @repeat="repeat"
|
||||
@status-update="refreshStatus(true)" @playlist-update="refresh(true)"
|
||||
@new-playing-track="refreshStatus(true)" @remove-from-tracklist="removeFromTracklist"
|
||||
@add-to-tracklist="addToTracklist" @swap-tracks="swapTracks" @load-playlist="loadPlaylist"
|
||||
@play-playlist="playPlaylist" @remove-playlist="removePlaylist" @tracklist-move="moveTracklistTracks"
|
||||
@tracklist-save="saveToPlaylist" @playlist-edit="playlistEditChanged"
|
||||
@add-to-tracklist-from-edited-playlist="addToTracklistFromEditedPlaylist"
|
||||
@remove-from-playlist="removeFromPlaylist" @info="trackInfo = $event" @playlist-add="playlistAdd"
|
||||
@add-to-playlist="addToPlaylist" @playlist-track-move="playlistTrackMove" @search="search"
|
||||
@search-clear="searchResults = []" />
|
||||
@search-clear="searchResults = []" @cd="cd"/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -42,6 +42,8 @@ export default {
|
|||
editedPlaylistTracks: [],
|
||||
trackInfo: null,
|
||||
searchResults: [],
|
||||
libraryResults: [],
|
||||
path: '/',
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -133,7 +135,7 @@ export default {
|
|||
async play(event) {
|
||||
if (event?.pos != null) {
|
||||
await this.request('music.mpd.play_pos', {pos: event.pos})
|
||||
} else if (event.file) {
|
||||
} else if (event?.file) {
|
||||
await this.request('music.mpd.play', {resource: event.file})
|
||||
} else {
|
||||
await this.request('music.mpd.play')
|
||||
|
@ -313,10 +315,24 @@ export default {
|
|||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
async cd(path) {
|
||||
this.loading = true
|
||||
|
||||
try {
|
||||
this.libraryResults = (await this.request('music.mpd.lsinfo', {uri: path})).
|
||||
filter((result) => !result.playlist)
|
||||
|
||||
this.path = path
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.refresh()
|
||||
this.cd(this.path)
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
Loading…
Reference in a new issue