mirror of
https://github.com/owntone/owntone-server.git
synced 2024-12-27 23:55:57 -05:00
commit
b3a661cae8
@ -25,7 +25,6 @@ htdocsassetsdir = $(datadir)/owntone/htdocs/assets
|
|||||||
dist_htdocsassets_DATA = \
|
dist_htdocsassets_DATA = \
|
||||||
assets/index.css \
|
assets/index.css \
|
||||||
assets/index.js \
|
assets/index.js \
|
||||||
assets/vendor.js \
|
|
||||||
assets/materialdesignicons-webfont.svg \
|
assets/materialdesignicons-webfont.svg \
|
||||||
assets/materialdesignicons-webfont.ttf \
|
assets/materialdesignicons-webfont.ttf \
|
||||||
assets/materialdesignicons-webfont.woff2 \
|
assets/materialdesignicons-webfont.woff2 \
|
||||||
|
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
@ -17,7 +17,6 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>OwnTone</title>
|
<title>OwnTone</title>
|
||||||
<script type="module" crossorigin src="/assets/index.js"></script>
|
<script type="module" crossorigin src="/assets/index.js"></script>
|
||||||
<link rel="modulepreload" href="/assets/vendor.js">
|
|
||||||
<link rel="stylesheet" href="/assets/index.css">
|
<link rel="stylesheet" href="/assets/index.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
963
web-src/package-lock.json
generated
963
web-src/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -12,17 +12,16 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aacassandra/vue3-progressbar": "^1.0.3",
|
"@aacassandra/vue3-progressbar": "^1.0.3",
|
||||||
"@ts-pro/vue-eternal-loading": "^1.2.0",
|
"@ts-pro/vue-eternal-loading": "^1.2.0",
|
||||||
"@vueform/slider": "^2.0.9",
|
"@vueform/slider": "github:chme/slider#faff83ed8a77f2cdbcb7252505ef734301efd139",
|
||||||
"axios": "^0.26.1",
|
"axios": "^0.26.1",
|
||||||
"bulma": "^0.9.3",
|
"bulma": "^0.9.3",
|
||||||
"bulma-switch": "^2.0.4",
|
"bulma-switch": "^2.0.4",
|
||||||
|
"luxon": "^2.3.1",
|
||||||
"mdi": "^2.2.43",
|
"mdi": "^2.2.43",
|
||||||
"moment": "^2.29.1",
|
|
||||||
"moment-duration-format": "^2.3.2",
|
|
||||||
"reconnectingwebsocket": "^1.0.0",
|
"reconnectingwebsocket": "^1.0.0",
|
||||||
"spotify-web-api-js": "^1.5.2",
|
"spotify-web-api-js": "^1.5.2",
|
||||||
"string-to-color": "^2.2.2",
|
"string-to-color": "^2.2.2",
|
||||||
"vue": "^3.2.31",
|
"vue": "^3.2.33",
|
||||||
"vue-router": "^4.0.14",
|
"vue-router": "^4.0.14",
|
||||||
"vue-scrollto": "^2.20.0",
|
"vue-scrollto": "^2.20.0",
|
||||||
"vue3-click-away": "^1.2.4",
|
"vue3-click-away": "^1.2.4",
|
||||||
@ -31,12 +30,12 @@
|
|||||||
"vuex": "^4.0.2"
|
"vuex": "^4.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^2.2.4",
|
"@vitejs/plugin-vue": "^2.3.1",
|
||||||
"eslint": "^8.11.0",
|
"eslint": "^8.13.0",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "^8.5.0",
|
||||||
"eslint-plugin-vue": "^8.5.0",
|
"eslint-plugin-vue": "^8.6.0",
|
||||||
"prettier": "2.6.0",
|
"prettier": "2.6.2",
|
||||||
"sass": "^1.49.9",
|
"sass": "^1.50.0",
|
||||||
"vite": "^2.8.6"
|
"vite": "^2.9.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,6 @@ import ModalDialogUpdate from '@/components/ModalDialogUpdate.vue'
|
|||||||
import webapi from '@/webapi'
|
import webapi from '@/webapi'
|
||||||
import * as types from '@/store/mutation_types'
|
import * as types from '@/store/mutation_types'
|
||||||
import ReconnectingWebSocket from 'reconnectingwebsocket'
|
import ReconnectingWebSocket from 'reconnectingwebsocket'
|
||||||
import moment from 'moment'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'App',
|
name: 'App',
|
||||||
@ -90,7 +89,6 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
created: function () {
|
created: function () {
|
||||||
moment.locale(navigator.language)
|
|
||||||
this.connect()
|
this.connect()
|
||||||
|
|
||||||
// Start the progress bar on app start
|
// Start the progress bar on app start
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-else-if="album.isItem" class="media" @click="open_album(album.item)">
|
<div v-else-if="album.isItem" class="media" @click="open_album(album.item)">
|
||||||
<div v-if="is_visible_artwork" class="media-left fd-has-action">
|
<div v-if="is_visible_artwork" class="media-left fd-has-action">
|
||||||
<p class="image is-64x64 fd-has-shadow fd-has-action">
|
<div class="image is-64x64 fd-has-shadow fd-has-action">
|
||||||
<figure>
|
<figure>
|
||||||
<img
|
<img
|
||||||
v-lazy="{
|
v-lazy="{
|
||||||
@ -20,7 +20,7 @@
|
|||||||
:artist="album.item.artist"
|
:artist="album.item.artist"
|
||||||
/>
|
/>
|
||||||
</figure>
|
</figure>
|
||||||
</p>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="media-content fd-has-action is-clipped">
|
<div class="media-content fd-has-action is-clipped">
|
||||||
<div style="margin-top: 0.7rem">
|
<div style="margin-top: 0.7rem">
|
||||||
@ -34,7 +34,7 @@
|
|||||||
v-if="album.item.date_released && album.item.media_kind === 'music'"
|
v-if="album.item.date_released && album.item.media_kind === 'music'"
|
||||||
class="subtitle is-7 has-text-grey has-text-weight-normal"
|
class="subtitle is-7 has-text-grey has-text-weight-normal"
|
||||||
>
|
>
|
||||||
{{ $filters.time(album.item.date_released, 'L') }}
|
{{ $filters.date(album.item.date_released) }}
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
<p v-if="album.date_released">
|
<p v-if="album.date_released">
|
||||||
<span class="heading">Release date</span>
|
<span class="heading">Release date</span>
|
||||||
<span class="title is-6">{{
|
<span class="title is-6">{{
|
||||||
$filters.time(album.date_released, 'L')
|
$filters.date(album.date_released)
|
||||||
}}</span>
|
}}</span>
|
||||||
</p>
|
</p>
|
||||||
<p v-else-if="album.year > 0">
|
<p v-else-if="album.year > 0">
|
||||||
@ -49,7 +49,7 @@
|
|||||||
<p>
|
<p>
|
||||||
<span class="heading">Length</span>
|
<span class="heading">Length</span>
|
||||||
<span class="title is-6">{{
|
<span class="title is-6">{{
|
||||||
$filters.duration(album.length_ms)
|
$filters.durationInHours(album.length_ms)
|
||||||
}}</span>
|
}}</span>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
@ -61,7 +61,7 @@
|
|||||||
<p>
|
<p>
|
||||||
<span class="heading">Added at</span>
|
<span class="heading">Added at</span>
|
||||||
<span class="title is-6">{{
|
<span class="title is-6">{{
|
||||||
$filters.time(album.time_added, 'L LT')
|
$filters.datetime(album.time_added)
|
||||||
}}</span>
|
}}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
<p>
|
<p>
|
||||||
<span class="heading">Added at</span>
|
<span class="heading">Added at</span>
|
||||||
<span class="title is-6">{{
|
<span class="title is-6">{{
|
||||||
$filters.time(artist.time_added, 'L LT')
|
$filters.datetime(artist.time_added)
|
||||||
}}</span>
|
}}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -23,6 +23,12 @@
|
|||||||
composer.track_count
|
composer.track_count
|
||||||
}}</a>
|
}}</a>
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
<span class="heading">Length</span>
|
||||||
|
<span class="title is-6">{{
|
||||||
|
$filters.durationInHours(composer.length_ms)
|
||||||
|
}}</span>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<footer class="card-footer">
|
<footer class="card-footer">
|
||||||
<a class="card-footer-item has-text-dark" @click="queue_add">
|
<a class="card-footer-item has-text-dark" @click="queue_add">
|
||||||
|
@ -11,6 +11,22 @@
|
|||||||
genre.name
|
genre.name
|
||||||
}}</a>
|
}}</a>
|
||||||
</p>
|
</p>
|
||||||
|
<div class="content is-small">
|
||||||
|
<p>
|
||||||
|
<span class="heading">Albums</span>
|
||||||
|
<span class="title is-6">{{ genre.album_count }}</span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span class="heading">Tracks</span>
|
||||||
|
<span class="title is-6">{{ genre.track_count }}</span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span class="heading">Length</span>
|
||||||
|
<span class="title is-6">{{
|
||||||
|
$filters.durationInHours(genre.length_ms)
|
||||||
|
}}</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<footer class="card-footer">
|
<footer class="card-footer">
|
||||||
<a class="card-footer-item has-text-dark" @click="queue_add">
|
<a class="card-footer-item has-text-dark" @click="queue_add">
|
||||||
|
@ -56,7 +56,7 @@
|
|||||||
<p>
|
<p>
|
||||||
<span class="heading">Length</span>
|
<span class="heading">Length</span>
|
||||||
<span class="title is-6">{{
|
<span class="title is-6">{{
|
||||||
$filters.duration(item.length_ms)
|
$filters.durationInHours(item.length_ms)
|
||||||
}}</span>
|
}}</span>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
@ -48,7 +48,7 @@
|
|||||||
<p v-if="track.date_released">
|
<p v-if="track.date_released">
|
||||||
<span class="heading">Release date</span>
|
<span class="heading">Release date</span>
|
||||||
<span class="title is-6">{{
|
<span class="title is-6">{{
|
||||||
$filters.time(track.date_released, 'L')
|
$filters.date(track.date_released)
|
||||||
}}</span>
|
}}</span>
|
||||||
</p>
|
</p>
|
||||||
<p v-else-if="track.year > 0">
|
<p v-else-if="track.year > 0">
|
||||||
@ -70,7 +70,7 @@
|
|||||||
<p>
|
<p>
|
||||||
<span class="heading">Length</span>
|
<span class="heading">Length</span>
|
||||||
<span class="title is-6">{{
|
<span class="title is-6">{{
|
||||||
$filters.duration(track.length_ms)
|
$filters.durationInHours(track.length_ms)
|
||||||
}}</span>
|
}}</span>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
@ -107,7 +107,7 @@
|
|||||||
<p>
|
<p>
|
||||||
<span class="heading">Added at</span>
|
<span class="heading">Added at</span>
|
||||||
<span class="title is-6">{{
|
<span class="title is-6">{{
|
||||||
$filters.time(track.time_added, 'L LT')
|
$filters.datetime(track.time_added)
|
||||||
}}</span>
|
}}</span>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
<b>{{ album.artists[0].name }}</b>
|
<b>{{ album.artists[0].name }}</b>
|
||||||
</h2>
|
</h2>
|
||||||
<h2 class="subtitle is-7 has-text-grey has-text-weight-normal">
|
<h2 class="subtitle is-7 has-text-grey has-text-weight-normal">
|
||||||
({{ album.album_type }}, {{ $filters.time(album.release_date, 'L') }})
|
({{ album.album_type }}, {{ $filters.date(album.release_date) }})
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="media-right">
|
<div class="media-right">
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
<p>
|
<p>
|
||||||
<span class="heading">Release date</span>
|
<span class="heading">Release date</span>
|
||||||
<span class="title is-6">{{
|
<span class="title is-6">{{
|
||||||
$filters.time(album.release_date, 'L')
|
$filters.date(album.release_date)
|
||||||
}}</span>
|
}}</span>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
<p>
|
<p>
|
||||||
<span class="heading">Release date</span>
|
<span class="heading">Release date</span>
|
||||||
<span class="title is-6">{{
|
<span class="title is-6">{{
|
||||||
$filters.time(album.release_date, 'L')
|
$filters.date(album.release_date)
|
||||||
}}</span>
|
}}</span>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
@ -40,7 +40,7 @@
|
|||||||
<p>
|
<p>
|
||||||
<span class="heading">Length</span>
|
<span class="heading">Length</span>
|
||||||
<span class="title is-6">{{
|
<span class="title is-6">{{
|
||||||
$filters.duration(track.duration_ms)
|
$filters.durationInHours(track.duration_ms)
|
||||||
}}</span>
|
}}</span>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
@ -1,25 +1,47 @@
|
|||||||
import moment from 'moment'
|
import { DateTime, Duration } from 'luxon'
|
||||||
import momentDurationFormatSetup from 'moment-duration-format'
|
|
||||||
|
|
||||||
momentDurationFormatSetup(moment)
|
|
||||||
|
|
||||||
export const filters = {
|
export const filters = {
|
||||||
duration: function (value, format) {
|
durationInHours: function (value_ms) {
|
||||||
if (format) {
|
const seconds = Math.floor(value_ms / 1000)
|
||||||
return moment.duration(value).format(format)
|
if (seconds > 3600) {
|
||||||
|
return Duration.fromObject({ seconds: seconds })
|
||||||
|
.shiftTo('hours', 'minutes', 'seconds')
|
||||||
|
.toFormat('hh:mm:ss')
|
||||||
}
|
}
|
||||||
return moment.duration(value).format('hh:*mm:ss')
|
return Duration.fromObject({ seconds: seconds })
|
||||||
|
.shiftTo('minutes', 'seconds')
|
||||||
|
.toFormat('mm:ss')
|
||||||
},
|
},
|
||||||
|
|
||||||
time: function (value, format) {
|
durationInDays: function (value_ms) {
|
||||||
if (format) {
|
const minutes = Math.floor(value_ms / 60000)
|
||||||
return moment(value).format(format)
|
if (minutes > 1440) {
|
||||||
|
// 60 * 24
|
||||||
|
return Duration.fromObject({ minutes: minutes })
|
||||||
|
.shiftTo('days', 'hours', 'minutes')
|
||||||
|
.toHuman()
|
||||||
|
} else if (minutes > 60) {
|
||||||
|
return Duration.fromObject({ minutes: minutes })
|
||||||
|
.shiftTo('hours', 'minutes')
|
||||||
|
.toHuman()
|
||||||
}
|
}
|
||||||
return moment(value).format()
|
return Duration.fromObject({ minutes: minutes })
|
||||||
|
.shiftTo('minutes')
|
||||||
|
.toHuman()
|
||||||
},
|
},
|
||||||
|
|
||||||
timeFromNow: function (value, withoutSuffix) {
|
date: function (value) {
|
||||||
return moment(value).fromNow(withoutSuffix)
|
return DateTime.fromISO(value).toLocaleString(DateTime.DATE_FULL)
|
||||||
|
},
|
||||||
|
|
||||||
|
datetime: function (value) {
|
||||||
|
return DateTime.fromISO(value).toLocaleString(DateTime.DATETIME_MED)
|
||||||
|
},
|
||||||
|
|
||||||
|
timeFromNow: function (value) {
|
||||||
|
var diff = DateTime.now().diff(DateTime.fromISO(value))
|
||||||
|
|
||||||
|
return this.durationInDays(diff.as('milliseconds'))
|
||||||
},
|
},
|
||||||
|
|
||||||
number: function (value) {
|
number: function (value) {
|
||||||
|
@ -132,10 +132,12 @@ section.hero + section.fd-content {
|
|||||||
|
|
||||||
.fd-page {
|
.fd-page {
|
||||||
margin-top: 3.25rem;
|
margin-top: 3.25rem;
|
||||||
|
margin-bottom: 3.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fd-page-with-tabs {
|
.fd-page-with-tabs {
|
||||||
margin-top: 6.25rem !important;
|
margin-top: 6.25rem !important;
|
||||||
|
margin-bottom: 3.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set minimum height to hide "option" section */
|
/* Set minimum height to hide "option" section */
|
||||||
|
@ -61,20 +61,15 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>Total playtime</th>
|
<th>Total playtime</th>
|
||||||
<td class="has-text-right">
|
<td class="has-text-right">
|
||||||
{{
|
{{ $filters.durationInDays(library.db_playtime * 1000) }}
|
||||||
$filters.duration(
|
|
||||||
library.db_playtime * 1000,
|
|
||||||
'y [years], d [days], h [hours], m [minutes]'
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Library updated</th>
|
<th>Library updated</th>
|
||||||
<td class="has-text-right">
|
<td class="has-text-right">
|
||||||
{{ $filters.timeFromNow(library.updated_at) }}
|
{{ $filters.timeFromNow(library.updated_at) }} ago
|
||||||
<span class="has-text-grey"
|
<span class="has-text-grey"
|
||||||
>({{ $filters.time(library.updated_at, 'lll') }})</span
|
>({{ $filters.datetime(library.updated_at) }})</span
|
||||||
>
|
>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -83,7 +78,7 @@
|
|||||||
<td class="has-text-right">
|
<td class="has-text-right">
|
||||||
{{ $filters.timeFromNow(library.started_at, true) }}
|
{{ $filters.timeFromNow(library.started_at, true) }}
|
||||||
<span class="has-text-grey"
|
<span class="has-text-grey"
|
||||||
>({{ $filters.time(library.started_at, 'll') }})</span
|
>({{ $filters.datetime(library.started_at) }})</span
|
||||||
>
|
>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<content-with-heading>
|
<content-with-heading>
|
||||||
<template #heading-left>
|
<template #heading-left>
|
||||||
<p class="title is-4">
|
<p class="title is-4">
|
||||||
{{ name }}
|
{{ composer.name }}
|
||||||
</p>
|
</p>
|
||||||
</template>
|
</template>
|
||||||
<template #heading-right>
|
<template #heading-right>
|
||||||
@ -24,14 +24,16 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<p class="heading has-text-centered-mobile">
|
<p class="heading has-text-centered-mobile">
|
||||||
{{ albums_list.total }} albums |
|
{{ composer.album_count }} albums |
|
||||||
<a class="has-text-link" @click="open_tracks">tracks</a>
|
<a class="has-text-link" @click="open_tracks"
|
||||||
|
>{{ composer.track_count }} tracks</a
|
||||||
|
>
|
||||||
</p>
|
</p>
|
||||||
<list-albums :albums="albums_list" :hide_group_title="true" />
|
<list-albums :albums="albums_list" :hide_group_title="true" />
|
||||||
|
|
||||||
<modal-dialog-composer
|
<modal-dialog-composer
|
||||||
:show="show_composer_details_modal"
|
:show="show_composer_details_modal"
|
||||||
:composer="{ name: name }"
|
:composer="composer"
|
||||||
@close="show_composer_details_modal = false"
|
@close="show_composer_details_modal = false"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
@ -48,12 +50,15 @@ import { GroupByList } from '@/lib/GroupByList'
|
|||||||
|
|
||||||
const dataObject = {
|
const dataObject = {
|
||||||
load: function (to) {
|
load: function (to) {
|
||||||
return webapi.library_composer(to.params.composer)
|
return Promise.all([
|
||||||
|
webapi.library_composer(to.params.composer),
|
||||||
|
webapi.library_composer_albums(to.params.composer)
|
||||||
|
])
|
||||||
},
|
},
|
||||||
|
|
||||||
set: function (vm, response) {
|
set: function (vm, response) {
|
||||||
vm.name = vm.$route.params.composer
|
vm.composer = response[0].data
|
||||||
vm.albums_list = new GroupByList(response.data.albums)
|
vm.albums_list = new GroupByList(response[1].data.albums)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,7 +85,7 @@ export default {
|
|||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
name: '',
|
composer: {},
|
||||||
albums_list: new GroupByList(),
|
albums_list: new GroupByList(),
|
||||||
show_composer_details_modal: false
|
show_composer_details_modal: false
|
||||||
}
|
}
|
||||||
@ -90,13 +95,13 @@ export default {
|
|||||||
open_tracks: function () {
|
open_tracks: function () {
|
||||||
this.$router.push({
|
this.$router.push({
|
||||||
name: 'ComposerTracks',
|
name: 'ComposerTracks',
|
||||||
params: { composer: this.name }
|
params: { composer: this.composer.name }
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
play: function () {
|
play: function () {
|
||||||
webapi.player_play_expression(
|
webapi.player_play_expression(
|
||||||
'composer is "' + this.name + '" and media_kind is music',
|
'composer is "' + this.composer.name + '" and media_kind is music',
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<content-with-heading>
|
<content-with-heading>
|
||||||
<template #heading-left>
|
<template #heading-left>
|
||||||
<p class="title is-4">
|
<p class="title is-4">
|
||||||
{{ composer }}
|
{{ composer.name }}
|
||||||
</p>
|
</p>
|
||||||
</template>
|
</template>
|
||||||
<template #heading-right>
|
<template #heading-right>
|
||||||
@ -24,13 +24,15 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<p class="heading has-text-centered-mobile">
|
<p class="heading has-text-centered-mobile">
|
||||||
<a class="has-text-link" @click="open_albums">albums</a> |
|
<a class="has-text-link" @click="open_albums"
|
||||||
{{ tracks.total }} tracks
|
>{{ composer.album_count }} albums</a
|
||||||
|
>
|
||||||
|
| {{ composer.track_count }} tracks
|
||||||
</p>
|
</p>
|
||||||
<list-tracks :tracks="tracks.items" :expression="play_expression" />
|
<list-tracks :tracks="tracks.items" :expression="play_expression" />
|
||||||
<modal-dialog-composer
|
<modal-dialog-composer
|
||||||
:show="show_composer_details_modal"
|
:show="show_composer_details_modal"
|
||||||
:composer="{ name: composer }"
|
:composer="composer"
|
||||||
@close="show_composer_details_modal = false"
|
@close="show_composer_details_modal = false"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
@ -46,12 +48,15 @@ import webapi from '@/webapi'
|
|||||||
|
|
||||||
const dataObject = {
|
const dataObject = {
|
||||||
load: function (to) {
|
load: function (to) {
|
||||||
return webapi.library_composer_tracks(to.params.composer)
|
return Promise.all([
|
||||||
|
webapi.library_composer(to.params.composer),
|
||||||
|
webapi.library_composer_tracks(to.params.composer)
|
||||||
|
])
|
||||||
},
|
},
|
||||||
|
|
||||||
set: function (vm, response) {
|
set: function (vm, response) {
|
||||||
vm.composer = vm.$route.params.composer
|
vm.composer = response[0].data
|
||||||
vm.tracks = response.data.tracks
|
vm.tracks = response[1].data.tracks
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,7 +84,7 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
tracks: { items: [] },
|
tracks: { items: [] },
|
||||||
composer: '',
|
composer: {},
|
||||||
|
|
||||||
show_composer_details_modal: false
|
show_composer_details_modal: false
|
||||||
}
|
}
|
||||||
@ -87,7 +92,7 @@ export default {
|
|||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
play_expression() {
|
play_expression() {
|
||||||
return 'composer is "' + this.composer + '" and media_kind is music'
|
return 'composer is "' + this.composer.name + '" and media_kind is music'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -96,7 +101,7 @@ export default {
|
|||||||
this.show_details_modal = false
|
this.show_details_modal = false
|
||||||
this.$router.push({
|
this.$router.push({
|
||||||
name: 'ComposerAlbums',
|
name: 'ComposerAlbums',
|
||||||
params: { composer: this.composer }
|
params: { composer: this.composer.name }
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ import { byName, GroupByList } from '@/lib/GroupByList'
|
|||||||
|
|
||||||
const dataObject = {
|
const dataObject = {
|
||||||
load: function (to) {
|
load: function (to) {
|
||||||
return webapi.library_composers()
|
return webapi.library_composers('music')
|
||||||
},
|
},
|
||||||
|
|
||||||
set: function (vm, response) {
|
set: function (vm, response) {
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #heading-left>
|
<template #heading-left>
|
||||||
<p class="title is-4">
|
<p class="title is-4">
|
||||||
{{ name }}
|
{{ genre.name }}
|
||||||
</p>
|
</p>
|
||||||
</template>
|
</template>
|
||||||
<template #heading-right>
|
<template #heading-right>
|
||||||
@ -27,13 +27,15 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<p class="heading has-text-centered-mobile">
|
<p class="heading has-text-centered-mobile">
|
||||||
{{ albums_list.total }} albums |
|
{{ genre.album_count }} albums |
|
||||||
<a class="has-text-link" @click="open_tracks">tracks</a>
|
<a class="has-text-link" @click="open_tracks"
|
||||||
|
>{{ genre.track_count }} tracks</a
|
||||||
|
>
|
||||||
</p>
|
</p>
|
||||||
<list-albums :albums="albums_list" />
|
<list-albums :albums="albums_list" />
|
||||||
<modal-dialog-genre
|
<modal-dialog-genre
|
||||||
:show="show_genre_details_modal"
|
:show="show_genre_details_modal"
|
||||||
:genre="{ name: name }"
|
:genre="genre"
|
||||||
@close="show_genre_details_modal = false"
|
@close="show_genre_details_modal = false"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
@ -51,12 +53,15 @@ import { bySortName, GroupByList } from '@/lib/GroupByList'
|
|||||||
|
|
||||||
const dataObject = {
|
const dataObject = {
|
||||||
load: function (to) {
|
load: function (to) {
|
||||||
return webapi.library_genre(to.params.genre)
|
return Promise.all([
|
||||||
|
webapi.library_genre(to.params.genre),
|
||||||
|
webapi.library_genre_albums(to.params.genre)
|
||||||
|
])
|
||||||
},
|
},
|
||||||
|
|
||||||
set: function (vm, response) {
|
set: function (vm, response) {
|
||||||
vm.name = vm.$route.params.genre
|
vm.genre = response[0].data
|
||||||
vm.albums_list = new GroupByList(response.data.albums)
|
vm.albums_list = new GroupByList(response[1].data.albums)
|
||||||
vm.albums_list.group(bySortName('name_sort'))
|
vm.albums_list.group(bySortName('name_sort'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -89,7 +94,7 @@ export default {
|
|||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
name: '',
|
genre: {},
|
||||||
albums_list: new GroupByList(),
|
albums_list: new GroupByList(),
|
||||||
|
|
||||||
show_genre_details_modal: false
|
show_genre_details_modal: false
|
||||||
@ -99,12 +104,15 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
open_tracks: function () {
|
open_tracks: function () {
|
||||||
this.show_details_modal = false
|
this.show_details_modal = false
|
||||||
this.$router.push({ name: 'GenreTracks', params: { genre: this.name } })
|
this.$router.push({
|
||||||
|
name: 'GenreTracks',
|
||||||
|
params: { genre: this.genre.name }
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
play: function () {
|
play: function () {
|
||||||
webapi.player_play_expression(
|
webapi.player_play_expression(
|
||||||
'genre is "' + this.name + '" and media_kind is music',
|
'genre is "' + this.genre.name + '" and media_kind is music',
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #heading-left>
|
<template #heading-left>
|
||||||
<p class="title is-4">
|
<p class="title is-4">
|
||||||
{{ genre }}
|
{{ genre.name }}
|
||||||
</p>
|
</p>
|
||||||
</template>
|
</template>
|
||||||
<template #heading-right>
|
<template #heading-right>
|
||||||
@ -27,13 +27,15 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<p class="heading has-text-centered-mobile">
|
<p class="heading has-text-centered-mobile">
|
||||||
<a class="has-text-link" @click="open_genre">albums</a> |
|
<a class="has-text-link" @click="open_genre"
|
||||||
{{ tracks.total }} tracks
|
>{{ genre.album_count }} albums</a
|
||||||
|
>
|
||||||
|
| {{ genre.track_count }} tracks
|
||||||
</p>
|
</p>
|
||||||
<list-tracks :tracks="tracks.items" :expression="expression" />
|
<list-tracks :tracks="tracks.items" :expression="expression" />
|
||||||
<modal-dialog-genre
|
<modal-dialog-genre
|
||||||
:show="show_genre_details_modal"
|
:show="show_genre_details_modal"
|
||||||
:genre="{ name: genre }"
|
:genre="genre"
|
||||||
@close="show_genre_details_modal = false"
|
@close="show_genre_details_modal = false"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
@ -50,12 +52,15 @@ import webapi from '@/webapi'
|
|||||||
|
|
||||||
const dataObject = {
|
const dataObject = {
|
||||||
load: function (to) {
|
load: function (to) {
|
||||||
return webapi.library_genre_tracks(to.params.genre)
|
return Promise.all([
|
||||||
|
webapi.library_genre(to.params.genre),
|
||||||
|
webapi.library_genre_tracks(to.params.genre)
|
||||||
|
])
|
||||||
},
|
},
|
||||||
|
|
||||||
set: function (vm, response) {
|
set: function (vm, response) {
|
||||||
vm.genre = vm.$route.params.genre
|
vm.genre = response[0].data
|
||||||
vm.tracks = response.data.tracks
|
vm.tracks = response[1].data.tracks
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,14 +107,14 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
expression() {
|
expression() {
|
||||||
return 'genre is "' + this.genre + '" and media_kind is music'
|
return 'genre is "' + this.genre.name + '" and media_kind is music'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
open_genre: function () {
|
open_genre: function () {
|
||||||
this.show_details_modal = false
|
this.show_details_modal = false
|
||||||
this.$router.push({ name: 'Genre', params: { genre: this.genre } })
|
this.$router.push({ name: 'Genre', params: { genre: this.genre.name } })
|
||||||
},
|
},
|
||||||
|
|
||||||
play: function () {
|
play: function () {
|
||||||
|
@ -27,7 +27,7 @@ import { byName, GroupByList } from '@/lib/GroupByList'
|
|||||||
|
|
||||||
const dataObject = {
|
const dataObject = {
|
||||||
load: function (to) {
|
load: function (to) {
|
||||||
return webapi.library_genres()
|
return webapi.library_genres('music')
|
||||||
},
|
},
|
||||||
|
|
||||||
set: function (vm, response) {
|
set: function (vm, response) {
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
<div class="container has-text-centered">
|
<div class="container has-text-centered">
|
||||||
<p class="control has-text-centered fd-progress-now-playing">
|
<p class="control has-text-centered fd-progress-now-playing">
|
||||||
<Slider
|
<Slider
|
||||||
|
ref="slider"
|
||||||
v-model="item_progress_ms"
|
v-model="item_progress_ms"
|
||||||
:min="0"
|
:min="0"
|
||||||
:max="state.item_length_ms"
|
:max="state.item_length_ms"
|
||||||
@ -22,6 +23,8 @@
|
|||||||
:disabled="state.state === 'stop'"
|
:disabled="state.state === 'stop'"
|
||||||
:classes="{ target: 'seek-slider' }"
|
:classes="{ target: 'seek-slider' }"
|
||||||
@change="seek"
|
@change="seek"
|
||||||
|
@start="start_dragging"
|
||||||
|
@end="end_dragging"
|
||||||
/>
|
/>
|
||||||
<!--range-slider
|
<!--range-slider
|
||||||
class="seek-slider fd-has-action"
|
class="seek-slider fd-has-action"
|
||||||
@ -35,8 +38,8 @@
|
|||||||
</p>
|
</p>
|
||||||
<p class="content">
|
<p class="content">
|
||||||
<span
|
<span
|
||||||
>{{ $filters.duration(item_progress_ms) }} /
|
>{{ $filters.durationInHours(item_progress_ms) }} /
|
||||||
{{ $filters.duration(now_playing.length_ms) }}</span
|
{{ $filters.durationInHours(now_playing.length_ms) }}</span
|
||||||
>
|
>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@ -101,6 +104,7 @@ export default {
|
|||||||
return {
|
return {
|
||||||
item_progress_ms: 0,
|
item_progress_ms: 0,
|
||||||
interval_id: 0,
|
interval_id: 0,
|
||||||
|
is_dragged: false,
|
||||||
|
|
||||||
show_details_modal: false,
|
show_details_modal: false,
|
||||||
selected_item: {}
|
selected_item: {}
|
||||||
@ -157,6 +161,10 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
mounted: function () {
|
||||||
|
console.log(this.$refs.slider)
|
||||||
|
},
|
||||||
|
|
||||||
created() {
|
created() {
|
||||||
this.item_progress_ms = this.state.item_progress_ms
|
this.item_progress_ms = this.state.item_progress_ms
|
||||||
webapi.player_status().then(({ data }) => {
|
webapi.player_status().then(({ data }) => {
|
||||||
@ -176,7 +184,19 @@ export default {
|
|||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
tick: function () {
|
tick: function () {
|
||||||
this.item_progress_ms += 1000
|
if (!this.is_dragged) {
|
||||||
|
this.item_progress_ms += 1000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
start_dragging: function () {
|
||||||
|
console.log('@start')
|
||||||
|
this.is_dragged = true
|
||||||
|
},
|
||||||
|
|
||||||
|
end_dragging: function () {
|
||||||
|
console.log('@end')
|
||||||
|
this.is_dragged = false
|
||||||
},
|
},
|
||||||
|
|
||||||
seek: function (newPosition) {
|
seek: function (newPosition) {
|
||||||
|
@ -50,7 +50,7 @@
|
|||||||
:context_uri="album.uri"
|
:context_uri="album.uri"
|
||||||
>
|
>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<a @click="open_track_dialog(track)">
|
<a @click.prevent.stop="open_track_dialog(track)">
|
||||||
<span class="icon has-text-dark"
|
<span class="icon has-text-dark"
|
||||||
><i class="mdi mdi-dots-vertical mdi-18px"
|
><i class="mdi mdi-dots-vertical mdi-18px"
|
||||||
/></span>
|
/></span>
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
</p>
|
</p>
|
||||||
</template>
|
</template>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<a @click="open_album_dialog(album)">
|
<a @click.prevent.stop="open_album_dialog(album)">
|
||||||
<span class="icon has-text-dark"
|
<span class="icon has-text-dark"
|
||||||
><i class="mdi mdi-dots-vertical mdi-18px"
|
><i class="mdi mdi-dots-vertical mdi-18px"
|
||||||
/></span>
|
/></span>
|
||||||
@ -65,7 +65,7 @@
|
|||||||
:playlist="playlist"
|
:playlist="playlist"
|
||||||
>
|
>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<a @click="open_playlist_dialog(playlist)">
|
<a @click.prevent.stop="open_playlist_dialog(playlist)">
|
||||||
<span class="icon has-text-dark"
|
<span class="icon has-text-dark"
|
||||||
><i class="mdi mdi-dots-vertical mdi-18px"
|
><i class="mdi mdi-dots-vertical mdi-18px"
|
||||||
/></span>
|
/></span>
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
:playlist="playlist"
|
:playlist="playlist"
|
||||||
>
|
>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<a @click="open_playlist_dialog(playlist)">
|
<a @click.prevent.stop="open_playlist_dialog(playlist)">
|
||||||
<span class="icon has-text-dark"
|
<span class="icon has-text-dark"
|
||||||
><i class="mdi mdi-dots-vertical mdi-18px"
|
><i class="mdi mdi-dots-vertical mdi-18px"
|
||||||
/></span>
|
/></span>
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
</p>
|
</p>
|
||||||
</template>
|
</template>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<a @click="open_album_dialog(album)">
|
<a @click.prevent.stop="open_album_dialog(album)">
|
||||||
<span class="icon has-text-dark"
|
<span class="icon has-text-dark"
|
||||||
><i class="mdi mdi-dots-vertical mdi-18px"
|
><i class="mdi mdi-dots-vertical mdi-18px"
|
||||||
/></span>
|
/></span>
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
:context_uri="playlist.uri"
|
:context_uri="playlist.uri"
|
||||||
>
|
>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<a @click="open_track_dialog(item.track)">
|
<a @click.prevent.stop="open_track_dialog(item.track)">
|
||||||
<span class="icon has-text-dark"
|
<span class="icon has-text-dark"
|
||||||
><i class="mdi mdi-dots-vertical mdi-18px"
|
><i class="mdi mdi-dots-vertical mdi-18px"
|
||||||
/></span>
|
/></span>
|
||||||
|
@ -53,7 +53,7 @@
|
|||||||
:context_uri="track.uri"
|
:context_uri="track.uri"
|
||||||
>
|
>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<a @click="open_track_dialog(track)">
|
<a @click.prevent.stop="open_track_dialog(track)">
|
||||||
<span class="icon has-text-dark"
|
<span class="icon has-text-dark"
|
||||||
><i class="mdi mdi-dots-vertical mdi-18px"
|
><i class="mdi mdi-dots-vertical mdi-18px"
|
||||||
/></span>
|
/></span>
|
||||||
@ -103,7 +103,7 @@
|
|||||||
:artist="artist"
|
:artist="artist"
|
||||||
>
|
>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<a @click="open_artist_dialog(artist)">
|
<a @click.prevent.stop="open_artist_dialog(artist)">
|
||||||
<span class="icon has-text-dark"
|
<span class="icon has-text-dark"
|
||||||
><i class="mdi mdi-dots-vertical mdi-18px"
|
><i class="mdi mdi-dots-vertical mdi-18px"
|
||||||
/></span>
|
/></span>
|
||||||
@ -164,7 +164,7 @@
|
|||||||
</p>
|
</p>
|
||||||
</template>
|
</template>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<a @click="open_album_dialog(album)">
|
<a @click.prevent.stop="open_album_dialog(album)">
|
||||||
<span class="icon has-text-dark"
|
<span class="icon has-text-dark"
|
||||||
><i class="mdi mdi-dots-vertical mdi-18px"
|
><i class="mdi mdi-dots-vertical mdi-18px"
|
||||||
/></span>
|
/></span>
|
||||||
@ -213,7 +213,7 @@
|
|||||||
:playlist="playlist"
|
:playlist="playlist"
|
||||||
>
|
>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<a @click="open_playlist_dialog(playlist)">
|
<a @click.prevent.stop="open_playlist_dialog(playlist)">
|
||||||
<span class="icon has-text-dark"
|
<span class="icon has-text-dark"
|
||||||
><i class="mdi mdi-dots-vertical mdi-18px"
|
><i class="mdi mdi-dots-vertical mdi-18px"
|
||||||
/></span>
|
/></span>
|
||||||
|
@ -292,15 +292,20 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
library_genres() {
|
library_genres(media_kind = undefined) {
|
||||||
return axios.get('./api/library/genres')
|
return axios.get('./api/library/genres', {
|
||||||
|
params: { media_kind: media_kind }
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
library_genre(genre) {
|
library_genre(genre) {
|
||||||
|
return axios.get(`./api/library/genres/${encodeURIComponent(genre)}`)
|
||||||
|
},
|
||||||
|
|
||||||
|
library_genre_albums(genre) {
|
||||||
const genreParams = {
|
const genreParams = {
|
||||||
type: 'albums',
|
type: 'albums',
|
||||||
media_kind: 'music',
|
expression: `genre is "${genre}" and media_kind is music`
|
||||||
expression: 'genre is "' + genre + '"'
|
|
||||||
}
|
}
|
||||||
return axios.get('./api/search', {
|
return axios.get('./api/search', {
|
||||||
params: genreParams
|
params: genreParams
|
||||||
@ -310,8 +315,7 @@ export default {
|
|||||||
library_genre_tracks(genre) {
|
library_genre_tracks(genre) {
|
||||||
const genreParams = {
|
const genreParams = {
|
||||||
type: 'tracks',
|
type: 'tracks',
|
||||||
media_kind: 'music',
|
expression: `genre is "${genre}" and media_kind is music`
|
||||||
expression: 'genre is "' + genre + '"'
|
|
||||||
}
|
}
|
||||||
return axios.get('./api/search', {
|
return axios.get('./api/search', {
|
||||||
params: genreParams
|
params: genreParams
|
||||||
@ -329,15 +333,20 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
library_composers() {
|
library_composers(media_kind = undefined) {
|
||||||
return axios.get('./api/library/composers')
|
return axios.get('./api/library/composers', {
|
||||||
|
params: { media_kind: media_kind }
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
library_composer(composer) {
|
library_composer(composer) {
|
||||||
|
return axios.get(`./api/library/composers/${encodeURIComponent(composer)}`)
|
||||||
|
},
|
||||||
|
|
||||||
|
library_composer_albums(composer) {
|
||||||
const params = {
|
const params = {
|
||||||
type: 'albums',
|
type: 'albums',
|
||||||
media_kind: 'music',
|
expression: `composer is "${composer}" and media_kind is music`
|
||||||
expression: 'composer is "' + composer + '"'
|
|
||||||
}
|
}
|
||||||
return axios.get('./api/search', {
|
return axios.get('./api/search', {
|
||||||
params: params
|
params: params
|
||||||
@ -347,8 +356,7 @@ export default {
|
|||||||
library_composer_tracks(composer) {
|
library_composer_tracks(composer) {
|
||||||
const params = {
|
const params = {
|
||||||
type: 'tracks',
|
type: 'tracks',
|
||||||
media_kind: 'music',
|
expression: `composer is "${composer}" and media_kind is music`
|
||||||
expression: 'composer is "' + composer + '"'
|
|
||||||
}
|
}
|
||||||
return axios.get('./api/search', {
|
return axios.get('./api/search', {
|
||||||
params: params
|
params: params
|
||||||
|
Loading…
Reference in New Issue
Block a user