forked from rc/aircox
work on hot reload; fix bugs in player
This commit is contained in:
parent
5fd72c33cc
commit
239580c04a
|
@ -7443,8 +7443,6 @@ a.navbar-item.is-active {
|
||||||
min-width: 2.5em;
|
min-width: 2.5em;
|
||||||
border-radius: 0px;
|
border-radius: 0px;
|
||||||
transition: background-color 1s; }
|
transition: background-color 1s; }
|
||||||
.player .player-bar .button:focus {
|
|
||||||
background-color: #209cee; }
|
|
||||||
.player .player-bar .title {
|
.player .player-bar .title {
|
||||||
margin: 0em; }
|
margin: 0em; }
|
||||||
|
|
||||||
|
@ -7469,9 +7467,6 @@ main .cover.is-small {
|
||||||
main .cover.is-tiny {
|
main .cover.is-tiny {
|
||||||
height: 2em; }
|
height: 2em; }
|
||||||
|
|
||||||
.sound-item .cover {
|
|
||||||
height: 5em; }
|
|
||||||
|
|
||||||
aside > section {
|
aside > section {
|
||||||
margin-bottom: 2em; }
|
margin-bottom: 2em; }
|
||||||
|
|
||||||
|
@ -7487,6 +7482,12 @@ aside .cover.is-tiny {
|
||||||
aside .media .subtitle {
|
aside .media .subtitle {
|
||||||
font-size: 1em; }
|
font-size: 1em; }
|
||||||
|
|
||||||
|
.sound-item .cover {
|
||||||
|
height: 5em; }
|
||||||
|
|
||||||
|
.sound-item .media-content a {
|
||||||
|
padding: 0em; }
|
||||||
|
|
||||||
.is-round, .sound-item .button {
|
.is-round, .sound-item .button {
|
||||||
border: 1px #7a7a7a solid;
|
border: 1px #7a7a7a solid;
|
||||||
border-radius: 1em; }
|
border-radius: 1em; }
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -7422,8 +7422,6 @@ a.navbar-item.is-active {
|
||||||
min-width: 2.5em;
|
min-width: 2.5em;
|
||||||
border-radius: 0px;
|
border-radius: 0px;
|
||||||
transition: background-color 1s; }
|
transition: background-color 1s; }
|
||||||
.player .player-bar .button:focus {
|
|
||||||
background-color: #209cee; }
|
|
||||||
.player .player-bar .title {
|
.player .player-bar .title {
|
||||||
margin: 0em; }
|
margin: 0em; }
|
||||||
|
|
||||||
|
@ -7448,9 +7446,6 @@ main .cover.is-small {
|
||||||
main .cover.is-tiny {
|
main .cover.is-tiny {
|
||||||
height: 2em; }
|
height: 2em; }
|
||||||
|
|
||||||
.sound-item .cover {
|
|
||||||
height: 5em; }
|
|
||||||
|
|
||||||
aside > section {
|
aside > section {
|
||||||
margin-bottom: 2em; }
|
margin-bottom: 2em; }
|
||||||
|
|
||||||
|
@ -7466,6 +7461,12 @@ aside .cover.is-tiny {
|
||||||
aside .media .subtitle {
|
aside .media .subtitle {
|
||||||
font-size: 1em; }
|
font-size: 1em; }
|
||||||
|
|
||||||
|
.sound-item .cover {
|
||||||
|
height: 5em; }
|
||||||
|
|
||||||
|
.sound-item .media-content a {
|
||||||
|
padding: 0em; }
|
||||||
|
|
||||||
.is-round, .sound-item .button {
|
.is-round, .sound-item .button {
|
||||||
border: 1px #7a7a7a solid;
|
border: 1px #7a7a7a solid;
|
||||||
border-radius: 1em; }
|
border-radius: 1em; }
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -159,11 +159,11 @@
|
||||||
/*!******************************!*\
|
/*!******************************!*\
|
||||||
!*** ./assets/public/app.js ***!
|
!*** ./assets/public/app.js ***!
|
||||||
\******************************/
|
\******************************/
|
||||||
/*! exports provided: defaultConfig, default, AppConfig */
|
/*! exports provided: defaultConfig, default */
|
||||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"defaultConfig\", function() { return defaultConfig; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return App; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"AppConfig\", function() { return AppConfig; });\n/* harmony import */ var vue__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! vue */ \"./node_modules/vue/dist/vue.esm.browser.js\");\n\n\nconst defaultConfig = {\n el: '#app',\n delimiters: ['[[', ']]'],\n\n data() {\n return {\n page: null,\n }\n },\n\n computed: {\n player() { return window.aircox.player; },\n },\n}\n\nfunction App(config, sync=false) {\n return (new AppConfig(config)).load(sync)\n}\n\n/**\n * Application config for an application instance\n */\nclass AppConfig {\n constructor(config) {\n this._config = config;\n }\n\n get config() {\n let config = this._config instanceof Function ? this._config() : this._config;\n for(var k of new Set([...Object.keys(config || {}), ...Object.keys(defaultConfig)])) {\n if(!config[k] && defaultConfig[k])\n config[k] = defaultConfig[k]\n else if(config[k] instanceof Object)\n config[k] = {...defaultConfig[k], ...config[k]}\n }\n return config;\n }\n\n set config(value) {\n this._config = value;\n }\n\n load(sync=false) {\n var self = this;\n return new Promise(function(resolve, reject) {\n let func = () => { try { resolve(self.build()) } catch(error) { reject(error) }};\n sync ? func() : window.addEventListener('load', func);\n });\n }\n\n build() {\n let config = this.config;\n const el = document.querySelector(config.el)\n if(!el) {\n reject(`Error: missing element ${config.el}`);\n return;\n }\n return new vue__WEBPACK_IMPORTED_MODULE_0__[\"default\"](config);\n }\n}\n\n\n\n\n//# sourceURL=webpack:///./assets/public/app.js?");
|
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"defaultConfig\", function() { return defaultConfig; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return AppBuilder; });\n/* harmony import */ var vue__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! vue */ \"./node_modules/vue/dist/vue.esm.browser.js\");\n\n\nconst defaultConfig = {\n el: '#app',\n delimiters: ['[[', ']]'],\n\n computed: {\n player() { return window.aircox.player; },\n },\n}\n\n\nclass AppBuilder {\n constructor(config={}) {\n this._config = config;\n this.app = null;\n }\n\n get config() {\n let config = this._config instanceof Function ? this._config() : this._config;\n for(var k of new Set([...Object.keys(config || {}), ...Object.keys(defaultConfig)])) {\n if(!config[k] && defaultConfig[k])\n config[k] = defaultConfig[k]\n else if(config[k] instanceof Object)\n config[k] = {...defaultConfig[k], ...config[k]}\n }\n return config;\n }\n\n set config(value) {\n this._config = value;\n }\n\n destroy() {\n self.app && self.app.$destroy();\n self.app = null;\n }\n\n fetch(url, options) {\n return fetch(url, options).then(response => response.text())\n .then(content => {\n let doc = new DOMParser().parseFromString(content, 'text/html');\n let app = doc.getElementById('app');\n content = app ? app.innerHTML : content;\n return this.load({sync: true, content, title: doc.title, url })\n })\n }\n\n load({async=false,content=null, title=null, url=null}={}) {\n var self = this;\n return new Promise((resolve, reject) => {\n let func = () => {\n try {\n let config = self.config;\n const el = document.querySelector(config.el);\n if(!el)\n return reject(`Error: can't get element ${config.el}`)\n\n if(content)\n el.innerHTML = content\n if(title)\n document.title = title;\n if(url && content)\n history.pushState({ content: content, title: title }, '', url)\n\n this.app = new vue__WEBPACK_IMPORTED_MODULE_0__[\"default\"](config);\n resolve(self.app)\n } catch(error) {\n self.destroy();\n reject(error)\n }};\n async ? window.addEventListener('load', func) : func();\n });\n }\n\n loadFromState(state) {\n return this.load({ content: state.content, title: state.title });\n }\n}\n\n\n\n\n//# sourceURL=webpack:///./assets/public/app.js?");
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ List item for a podcast.
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
<a-sound-item :data="{{ object|json }}" :player="player"
|
<a-sound-item :data="{{ object|json }}" :player="player"
|
||||||
:actions="['play']" @click="player.play(item)">
|
:actions="['play']">
|
||||||
</a-sound-item>
|
</a-sound-item>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -4,27 +4,16 @@ export const defaultConfig = {
|
||||||
el: '#app',
|
el: '#app',
|
||||||
delimiters: ['[[', ']]'],
|
delimiters: ['[[', ']]'],
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
page: null,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
player() { return window.aircox.player; },
|
player() { return window.aircox.player; },
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function App(config, sync=false) {
|
|
||||||
return (new AppConfig(config)).load(sync)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
export default class AppBuilder {
|
||||||
* Application config for an application instance
|
constructor(config={}) {
|
||||||
*/
|
|
||||||
export class AppConfig {
|
|
||||||
constructor(config) {
|
|
||||||
this._config = config;
|
this._config = config;
|
||||||
|
this.app = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
get config() {
|
get config() {
|
||||||
|
@ -42,22 +31,50 @@ export class AppConfig {
|
||||||
this._config = value;
|
this._config = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
load(sync=false) {
|
destroy() {
|
||||||
|
self.app && self.app.$destroy();
|
||||||
|
self.app = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch(url, options) {
|
||||||
|
return fetch(url, options).then(response => response.text())
|
||||||
|
.then(content => {
|
||||||
|
let doc = new DOMParser().parseFromString(content, 'text/html');
|
||||||
|
let app = doc.getElementById('app');
|
||||||
|
content = app ? app.innerHTML : content;
|
||||||
|
return this.load({sync: true, content, title: doc.title, url })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
load({async=false,content=null, title=null, url=null}={}) {
|
||||||
var self = this;
|
var self = this;
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise((resolve, reject) => {
|
||||||
let func = () => { try { resolve(self.build()) } catch(error) { reject(error) }};
|
let func = () => {
|
||||||
sync ? func() : window.addEventListener('load', func);
|
try {
|
||||||
|
let config = self.config;
|
||||||
|
const el = document.querySelector(config.el);
|
||||||
|
if(!el)
|
||||||
|
return reject(`Error: can't get element ${config.el}`)
|
||||||
|
|
||||||
|
if(content)
|
||||||
|
el.innerHTML = content
|
||||||
|
if(title)
|
||||||
|
document.title = title;
|
||||||
|
if(url && content)
|
||||||
|
history.pushState({ content: content, title: title }, '', url)
|
||||||
|
|
||||||
|
this.app = new Vue(config);
|
||||||
|
resolve(self.app)
|
||||||
|
} catch(error) {
|
||||||
|
self.destroy();
|
||||||
|
reject(error)
|
||||||
|
}};
|
||||||
|
async ? window.addEventListener('load', func) : func();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
build() {
|
loadFromState(state) {
|
||||||
let config = this.config;
|
return this.load({ content: state.content, title: state.title });
|
||||||
const el = document.querySelector(config.el)
|
|
||||||
if(!el) {
|
|
||||||
reject(`Error: missing element ${config.el}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return new Vue(config);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ import '@fortawesome/fontawesome-free/css/fontawesome.min.css';
|
||||||
|
|
||||||
|
|
||||||
//-- aircox
|
//-- aircox
|
||||||
import App from './app';
|
import AppBuilder from './app';
|
||||||
import Sound from './sound';
|
import Sound from './sound';
|
||||||
import {Set} from './model';
|
import {Set} from './model';
|
||||||
|
|
||||||
|
@ -31,31 +31,40 @@ Vue.component('a-sound-item', SoundItem)
|
||||||
|
|
||||||
window.aircox = {
|
window.aircox = {
|
||||||
// main application
|
// main application
|
||||||
app: null,
|
appBuilder: null,
|
||||||
|
|
||||||
// main application config
|
|
||||||
appConfig: {},
|
appConfig: {},
|
||||||
|
get app() { return this.appBuilder.app },
|
||||||
|
|
||||||
// player application
|
// player application
|
||||||
playerApp: null,
|
playerBuilder: null,
|
||||||
|
get playerApp() { return this.playerBuilder && this.playerBuilder.app },
|
||||||
|
get player() { return this.playerApp && this.playerApp.$refs.player },
|
||||||
|
|
||||||
// player component
|
onPageFetch(event) {
|
||||||
get player() {
|
let submit = event.type == 'submit';
|
||||||
return this.playerApp && this.playerApp.$refs.player
|
let target = submit || event.target.tagName == 'A'
|
||||||
},
|
? event.target : event.target.closest('a');
|
||||||
|
if(!target || target.hasAttribute('target'))
|
||||||
|
return;
|
||||||
|
|
||||||
loadPage(url) {
|
let url = submit ? target.getAttribute('action') || ''
|
||||||
fetch(url).then(response => response.text())
|
: target.getAttribute('href');
|
||||||
.then(response => {
|
if(url===null || !(url === '' || url.startsWith('/') || url.startsWith('?')))
|
||||||
let doc = new DOMParser().parseFromString(response, 'text/html');
|
return;
|
||||||
|
|
||||||
aircox.app && aircox.app.$destroy();
|
let options = {};
|
||||||
document.getElementById('app').innerHTML = doc.getElementById('app').innerHTML;
|
if(submit) {
|
||||||
App(() => window.aircox.appConfig, true).then(app => {
|
let formData = new FormData(event.target);
|
||||||
aircox.app = app;
|
if(target.method == 'get')
|
||||||
document.title = doc.title;
|
url += '?' + (new URLSearchParams(formData)).toString();
|
||||||
})
|
else {
|
||||||
});
|
options['method'] = target.method;
|
||||||
|
options['body'] = formData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.appBuilder.fetch(url, options);
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
},
|
},
|
||||||
|
|
||||||
Set: Set, Sound: Sound,
|
Set: Set, Sound: Sound,
|
||||||
|
@ -63,21 +72,17 @@ window.aircox = {
|
||||||
window.Vue = Vue;
|
window.Vue = Vue;
|
||||||
|
|
||||||
|
|
||||||
App({el: '#player'}).then(app => window.aircox.playerApp = app);
|
aircox.playerBuilder = new AppBuilder({el: '#player'});
|
||||||
App(() => window.aircox.appConfig).then(app => {
|
aircox.playerBuilder.load({async:true});
|
||||||
window.aircox.app = app;
|
aircox.appBuilder = new AppBuilder(x => window.aircox.appConfig);
|
||||||
window.addEventListener('click', event => {
|
aircox.appBuilder.load({async:true}).then(app => {
|
||||||
let target = event.target.tagName == 'A' ? event.target : event.target.closest('a');
|
//-- load page hooks
|
||||||
if(!target || !target.hasAttribute('href'))
|
window.addEventListener('click', event => aircox.onPageFetch(event), true);
|
||||||
return;
|
window.addEventListener('submit', event => aircox.onPageFetch(event), true);
|
||||||
|
window.addEventListener('popstate', event => {
|
||||||
let href = target.getAttribute('href');
|
if(event.state && event.state.content)
|
||||||
if(href && href !='#') {
|
aircox.appBuilder.loadFromState(event.state);
|
||||||
window.aircox.loadPage(href);
|
});
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
}
|
|
||||||
}, true);
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<Playlist ref="pin" class="player-panel menu" v-show="panel == 'pin'"
|
<Playlist ref="pin" class="player-panel menu" v-show="panel == 'pin'"
|
||||||
name="Pinned"
|
name="Pinned"
|
||||||
:actions="['page']"
|
:actions="['page']"
|
||||||
:editable="true" :player="self" :set="sets.pin" @select="play('pin', $event.index)"
|
:editable="true" :player="self" :set="sets.pin" @select="togglePlay('pin', $event.index)"
|
||||||
listClass="menu-list" itemClass="menu-item">
|
listClass="menu-list" itemClass="menu-item">
|
||||||
<template v-slot:header="">
|
<template v-slot:header="">
|
||||||
<p class="menu-label">
|
<p class="menu-label">
|
||||||
|
@ -15,7 +15,7 @@
|
||||||
</Playlist>
|
</Playlist>
|
||||||
<Playlist ref="queue" class="player-panel menu" v-show="panel == 'queue'"
|
<Playlist ref="queue" class="player-panel menu" v-show="panel == 'queue'"
|
||||||
:actions="['page']"
|
:actions="['page']"
|
||||||
:editable="true" :player="self" :set="sets.queue" @select="play('queue', $event.index)"
|
:editable="true" :player="self" :set="sets.queue" @select="togglePlay('queue', $event.index)"
|
||||||
listClass="menu-list" itemClass="menu-item">
|
listClass="menu-list" itemClass="menu-item">
|
||||||
<template v-slot:header="">
|
<template v-slot:header="">
|
||||||
<p class="menu-label">
|
<p class="menu-label">
|
||||||
|
@ -44,7 +44,7 @@
|
||||||
@select="audio.currentTime = $event"></Progress>
|
@select="audio.currentTime = $event"></Progress>
|
||||||
</div>
|
</div>
|
||||||
<div class="media-right">
|
<div class="media-right">
|
||||||
<button class="button has-text-weight-bold" v-if="loaded" @click="playLive()">
|
<button class="button has-text-weight-bold" v-if="loaded" @click="play()">
|
||||||
<span class="icon is-size-6 has-text-danger">
|
<span class="icon is-size-6 has-text-danger">
|
||||||
<span class="fa fa-circle"></span>
|
<span class="fa fa-circle"></span>
|
||||||
</span>
|
</span>
|
||||||
|
@ -105,8 +105,8 @@ export default {
|
||||||
loaded: null,
|
loaded: null,
|
||||||
//! Active panel name
|
//! Active panel name
|
||||||
panel: null,
|
panel: null,
|
||||||
//! current playing playlist component
|
//! current playing playlist name
|
||||||
playlist: null,
|
playlistName: null,
|
||||||
//! players' playlists' sets
|
//! players' playlists' sets
|
||||||
sets: {
|
sets: {
|
||||||
queue: Set.storeLoad(Sound, "playlist.queue", { max: 30, unique: true }),
|
queue: Set.storeLoad(Sound, "playlist.queue", { max: 30, unique: true }),
|
||||||
|
@ -126,6 +126,10 @@ export default {
|
||||||
playing() { return this.state == State.playing; },
|
playing() { return this.state == State.playing; },
|
||||||
loading() { return this.state == State.loading; },
|
loading() { return this.state == State.loading; },
|
||||||
|
|
||||||
|
playlist() {
|
||||||
|
return this.playlistName ? this.$refs[this.playlistName] : null;
|
||||||
|
},
|
||||||
|
|
||||||
current() {
|
current() {
|
||||||
return this.loaded || this.live && this.live.current;
|
return this.loaded || this.live && this.live.current;
|
||||||
},
|
},
|
||||||
|
@ -155,7 +159,7 @@ export default {
|
||||||
let set = this.sets[name];
|
let set = this.sets[name];
|
||||||
return (set ? (set.length ? "" : "has-text-grey-light ")
|
return (set ? (set.length ? "" : "has-text-grey-light ")
|
||||||
+ (this.panel == name ? "is-info "
|
+ (this.panel == name ? "is-info "
|
||||||
: this.playlist && this.playlist == this.$refs[name] ? 'is-primary '
|
: this.playlistName == name ? 'is-primary '
|
||||||
: '') : '')
|
: '') : '')
|
||||||
+ "button has-text-weight-bold";
|
+ "button has-text-weight-bold";
|
||||||
},
|
},
|
||||||
|
@ -168,17 +172,33 @@ export default {
|
||||||
isPlaying(item) { return this.isLoaded(item) && !this.paused },
|
isPlaying(item) { return this.isLoaded(item) && !this.paused },
|
||||||
|
|
||||||
_setPlaylist(playlist) {
|
_setPlaylist(playlist) {
|
||||||
this.playlist = playlist ? this.$refs[playlist] : null;
|
this.playlistName = playlist;
|
||||||
for(var p in this.sets)
|
for(var p in this.sets)
|
||||||
if(p != playlist)
|
if(p != playlist)
|
||||||
this.$refs[p].unselect();
|
this.$refs[p].unselect();
|
||||||
},
|
},
|
||||||
|
|
||||||
load(playlist, {src=null, item=null}={}) {
|
/// Play a playlist's sound (by playlist name, and sound index)
|
||||||
src = src || item.src;
|
play(playlist=null, index=0) {
|
||||||
this.loaded = item;
|
let src = null;
|
||||||
|
|
||||||
|
// from playlist
|
||||||
|
if(playlist !== null) {
|
||||||
|
let item = this.$refs[playlist].get(index);
|
||||||
|
if(!item)
|
||||||
|
throw `No sound at index ${index} for playlist ${playlist}`;
|
||||||
|
this.loaded = item;
|
||||||
|
src = item.src;
|
||||||
|
}
|
||||||
|
// from live
|
||||||
|
else {
|
||||||
|
this.loaded = null;
|
||||||
|
src = this.live.src;
|
||||||
|
}
|
||||||
|
|
||||||
this._setPlaylist(playlist);
|
this._setPlaylist(playlist);
|
||||||
|
|
||||||
|
// load sources
|
||||||
const audio = this.audio;
|
const audio = this.audio;
|
||||||
if(src instanceof Array) {
|
if(src instanceof Array) {
|
||||||
audio.innerHTML = '';
|
audio.innerHTML = '';
|
||||||
|
@ -192,29 +212,13 @@ export default {
|
||||||
audio.src = src;
|
audio.src = src;
|
||||||
}
|
}
|
||||||
audio.load();
|
audio.load();
|
||||||
},
|
audio.play();
|
||||||
|
audio.play().catch(e => console.error(e))
|
||||||
/// Play a playlist's sound (by playlist name, and sound index)
|
|
||||||
play(playlist=null, index=0) {
|
|
||||||
if(index < 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if(!playlist)
|
|
||||||
playlist = 'queue';
|
|
||||||
|
|
||||||
console.log('play', playlist, index, this.audio);
|
|
||||||
let item = this.$refs[playlist].get(index);
|
|
||||||
if(item) {
|
|
||||||
this.load(playlist, {item});
|
|
||||||
this.audio.play().catch(e => console.error(e))
|
|
||||||
}
|
|
||||||
else
|
|
||||||
throw `No sound at index ${index} for playlist ${playlist}`;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Push items to playlist (by name)
|
/// Push items to playlist (by name)
|
||||||
push(playlist, ...items) {
|
push(playlist, ...items) {
|
||||||
this.$refs[playlist].push(...items);
|
return this.$refs[playlist].push(...items);
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Push and play items
|
/// Push and play items
|
||||||
|
@ -224,29 +228,30 @@ export default {
|
||||||
this.play(playlist, index);
|
this.play(playlist, index);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Handle click event that plays multiple items (from `data-sounds` attribute)
|
||||||
playButtonClick(event) {
|
playButtonClick(event) {
|
||||||
var items = JSON.parse(event.currentTarget.dataset.sounds);
|
var items = JSON.parse(event.currentTarget.dataset.sounds);
|
||||||
this.playItems('queue', ...items);
|
this.playItems('queue', ...items);
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Play live stream
|
|
||||||
playLive() {
|
|
||||||
this.load(null, {src: this.live.src});
|
|
||||||
this.audio.play().catch(e => console.error(e))
|
|
||||||
this.panel = '';
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Pause
|
/// Pause
|
||||||
pause() {
|
pause() {
|
||||||
this.audio.pause()
|
this.audio.pause()
|
||||||
},
|
},
|
||||||
|
|
||||||
//! Play/pause
|
//! Play/pause
|
||||||
togglePlay() {
|
togglePlay(playlist=null, index=0) {
|
||||||
|
if(playlist !== null) {
|
||||||
|
let item = this.sets[playlist].get(index);
|
||||||
|
if(!this.playlist || this.playlistName !== playlist || this.loaded != item) {
|
||||||
|
this.play(playlist, index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
if(this.paused)
|
if(this.paused)
|
||||||
this.audio.play().catch(e => console.error(e))
|
this.audio.play().catch(e => console.error(e))
|
||||||
else
|
else
|
||||||
this.pause()
|
this.audio.pause();
|
||||||
},
|
},
|
||||||
|
|
||||||
//! Pin/Unpin an item
|
//! Pin/Unpin an item
|
||||||
|
@ -266,7 +271,7 @@ export default {
|
||||||
this.state = audio.paused ? State.paused : State.playing;
|
this.state = audio.paused ? State.paused : State.playing;
|
||||||
|
|
||||||
if(event.type == 'ended' && (!this.playlist || this.playlist.selectNext() == -1))
|
if(event.type == 'ended' && (!this.playlist || this.playlist.selectNext() == -1))
|
||||||
this.playLive();
|
this.play();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -14,11 +14,8 @@
|
||||||
<div class="media-content">
|
<div class="media-content">
|
||||||
<slot name="content" :player="player" :item="item" :loaded="loaded">
|
<slot name="content" :player="player" :item="item" :loaded="loaded">
|
||||||
<h4 class="title is-4">{{ name || item.name }}</h4>
|
<h4 class="title is-4">{{ name || item.name }}</h4>
|
||||||
<a class="subtitle is-6" v-if="hasAction('page') && item.data.page_url"
|
<a class="subtitle is-6 is-inline-block" v-if="hasAction('page') && item.data.page_url"
|
||||||
:href="item.data.page_url">
|
:href="item.data.page_url">
|
||||||
<i class="icon">
|
|
||||||
<i class="fas fa-link"></i>
|
|
||||||
</i>
|
|
||||||
{{ item.data.page_title }}
|
{{ item.data.page_title }}
|
||||||
</a>
|
</a>
|
||||||
</slot>
|
</slot>
|
||||||
|
|
|
@ -221,10 +221,6 @@ a.navbar-item.is-active {
|
||||||
transition: background-color 1s;
|
transition: background-color 1s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button:focus {
|
|
||||||
background-color: $info;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
margin: 0em;
|
margin: 0em;
|
||||||
}
|
}
|
||||||
|
@ -260,7 +256,6 @@ main {
|
||||||
.cover.is-tiny { height: 2em; }
|
.cover.is-tiny { height: 2em; }
|
||||||
}
|
}
|
||||||
|
|
||||||
.sound-item .cover { height: 5em; }
|
|
||||||
|
|
||||||
aside {
|
aside {
|
||||||
& > section {
|
& > section {
|
||||||
|
@ -280,6 +275,11 @@ aside {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.sound-item {
|
||||||
|
.cover { height: 5em; }
|
||||||
|
.media-content a { padding: 0em; }
|
||||||
|
}
|
||||||
|
|
||||||
.is-round, .sound-item .button {
|
.is-round, .sound-item .button {
|
||||||
border: 1px $grey solid;
|
border: 1px $grey solid;
|
||||||
border-radius: 1em;
|
border-radius: 1em;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user