diff --git a/aircox/static/aircox/admin.css b/aircox/static/aircox/admin.css
index fe1a5fe..31f2b87 100644
--- a/aircox/static/aircox/admin.css
+++ b/aircox/static/aircox/admin.css
@@ -7417,14 +7417,14 @@ a.navbar-item.is-active {
overflow-y: auto; }
.player .player-bar {
border-top: 1px #b5b5b5 solid; }
- .player .player-bar .media-left:not(:last-child) {
+ .player .player-bar > .media-left:not(:last-child) {
margin-right: 0em; }
- .player .player-bar .media-cover {
+ .player .player-bar > .media-cover {
border-left: 1px black solid; }
.player .player-bar .cover {
font-size: 1.5rem !important;
height: 2.5em !important; }
- .player .player-bar .media-content {
+ .player .player-bar > .media-content {
padding-top: 0.4em;
padding-left: 0.4em; }
.player .player-bar .button {
diff --git a/aircox/static/aircox/admin.js b/aircox/static/aircox/admin.js
index f7614f7..f8aca18 100644
--- a/aircox/static/aircox/admin.js
+++ b/aircox/static/aircox/admin.js
@@ -370,6 +370,42 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _nod
/***/ }),
+/***/ "./assets/public/playerProgress.vue":
+/*!******************************************!*\
+ !*** ./assets/public/playerProgress.vue ***!
+ \******************************************/
+/*! no static exports found */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _playerProgress_vue_vue_type_template_id_a8a834d4___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./playerProgress.vue?vue&type=template&id=a8a834d4& */ \"./assets/public/playerProgress.vue?vue&type=template&id=a8a834d4&\");\n/* harmony import */ var _playerProgress_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./playerProgress.vue?vue&type=script&lang=js& */ \"./assets/public/playerProgress.vue?vue&type=script&lang=js&\");\n/* harmony reexport (unknown) */ for(var __WEBPACK_IMPORT_KEY__ in _playerProgress_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__) if([\"default\"].indexOf(__WEBPACK_IMPORT_KEY__) < 0) (function(key) { __webpack_require__.d(__webpack_exports__, key, function() { return _playerProgress_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__[key]; }) }(__WEBPACK_IMPORT_KEY__));\n/* harmony import */ var _node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../node_modules/vue-loader/lib/runtime/componentNormalizer.js */ \"./node_modules/vue-loader/lib/runtime/componentNormalizer.js\");\n\n\n\n\n\n/* normalize component */\n\nvar component = Object(_node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(\n _playerProgress_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n _playerProgress_vue_vue_type_template_id_a8a834d4___WEBPACK_IMPORTED_MODULE_0__[\"render\"],\n _playerProgress_vue_vue_type_template_id_a8a834d4___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"],\n false,\n null,\n null,\n null\n \n)\n\n/* hot reload */\nif (false) { var api; }\ncomponent.options.__file = \"assets/public/playerProgress.vue\"\n/* harmony default export */ __webpack_exports__[\"default\"] = (component.exports);\n\n//# sourceURL=webpack:///./assets/public/playerProgress.vue?");
+
+/***/ }),
+
+/***/ "./assets/public/playerProgress.vue?vue&type=script&lang=js&":
+/*!*******************************************************************!*\
+ !*** ./assets/public/playerProgress.vue?vue&type=script&lang=js& ***!
+ \*******************************************************************/
+/*! no static exports found */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_vue_loader_lib_index_js_vue_loader_options_playerProgress_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../node_modules/vue-loader/lib??vue-loader-options!./playerProgress.vue?vue&type=script&lang=js& */ \"./node_modules/vue-loader/lib/index.js?!./assets/public/playerProgress.vue?vue&type=script&lang=js&\");\n/* empty/unused harmony star reexport */ /* harmony default export */ __webpack_exports__[\"default\"] = (_node_modules_vue_loader_lib_index_js_vue_loader_options_playerProgress_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__[\"default\"]); \n\n//# sourceURL=webpack:///./assets/public/playerProgress.vue?");
+
+/***/ }),
+
+/***/ "./assets/public/playerProgress.vue?vue&type=template&id=a8a834d4&":
+/*!*************************************************************************!*\
+ !*** ./assets/public/playerProgress.vue?vue&type=template&id=a8a834d4& ***!
+ \*************************************************************************/
+/*! exports provided: render, staticRenderFns */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_playerProgress_vue_vue_type_template_id_a8a834d4___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!../../node_modules/vue-loader/lib??vue-loader-options!./playerProgress.vue?vue&type=template&id=a8a834d4& */ \"./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/vue-loader/lib/index.js?!./assets/public/playerProgress.vue?vue&type=template&id=a8a834d4&\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_playerProgress_vue_vue_type_template_id_a8a834d4___WEBPACK_IMPORTED_MODULE_0__[\"render\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_playerProgress_vue_vue_type_template_id_a8a834d4___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"]; });\n\n\n\n//# sourceURL=webpack:///./assets/public/playerProgress.vue?");
+
+/***/ }),
+
/***/ "./assets/public/playlist.vue":
/*!************************************!*\
!*** ./assets/public/playlist.vue ***!
@@ -509,7 +545,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var loda
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
-eval("__webpack_require__.r(__webpack_exports__);\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n data() {\n return {\n selectedIndex: this.defaultIndex,\n }\n },\n\n props: {\n listClass: String,\n itemClass: String,\n defaultIndex: { type: Number, default: -1},\n set: Object,\n },\n\n computed: {\n model() { return this.set.model },\n items() { return this.set.items },\n length() { return this.set.length },\n\n selected() {\n return this.selectedIndex > -1 && this.items.length > this.selectedIndex > -1\n ? this.items[this.selectedIndex] : null;\n },\n },\n\n methods: {\n get(index) { return this.set.get(index) },\n find(item) { return this.set.find(item) },\n findIndex(item) { return this.set.findIndex(item) },\n\n push(...items) {\n let index = this.set.length;\n for(var item of items)\n this.set.push(item);\n },\n\n remove(index, select=False) {\n this.set.remove(index);\n if(index < this.selectedIndex)\n this.selectedIndex--;\n if(select && this.selectedIndex == index)\n this.select(index)\n },\n\n select(index) {\n this.selectedIndex = index > -1 && this.items.length ? index % this.items.length : -1;\n this.$emit('select', { target: this, item: this.selected, index: this.selectedIndex });\n return this.selectedIndex;\n },\n\n\n },\n});\n\n\n//# sourceURL=webpack:///./assets/public/list.vue?./node_modules/vue-loader/lib??vue-loader-options");
+eval("__webpack_require__.r(__webpack_exports__);\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n data() {\n return {\n selectedIndex: this.defaultIndex,\n }\n },\n\n props: {\n listClass: String,\n itemClass: String,\n defaultIndex: { type: Number, default: -1},\n set: Object,\n },\n\n computed: {\n model() { return this.set.model },\n items() { return this.set.items },\n length() { return this.set.length },\n\n selected() {\n return this.selectedIndex > -1 && this.items.length > this.selectedIndex > -1\n ? this.items[this.selectedIndex] : null;\n },\n },\n\n methods: {\n get(index) { return this.set.get(index) },\n find(item) { return this.set.find(item) },\n findIndex(item) { return this.set.findIndex(item) },\n\n push(...items) {\n let index = this.set.length;\n for(var item of items)\n this.set.push(item);\n },\n\n remove(index, select=False) {\n this.set.remove(index);\n if(index < this.selectedIndex)\n this.selectedIndex--;\n if(select && this.selectedIndex == index)\n this.select(index)\n },\n\n select(index) {\n this.selectedIndex = index > -1 && this.items.length ? index % this.items.length : -1;\n this.$emit('select', { item: this.selected, index: this.selectedIndex });\n return this.selectedIndex;\n },\n\n\n },\n});\n\n\n//# sourceURL=webpack:///./assets/public/list.vue?./node_modules/vue-loader/lib??vue-loader-options");
/***/ }),
@@ -521,7 +557,19 @@ eval("__webpack_require__.r(__webpack_exports__);\n//\n//\n//\n//\n//\n//\n//\n/
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
-eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"State\", function() { return State; });\n/* harmony import */ var vue__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! vue */ \"./node_modules/vue/dist/vue.esm.browser.js\");\n/* harmony import */ var _live__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./live */ \"./assets/public/live.js\");\n/* harmony import */ var _playlist__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./playlist */ \"./assets/public/playlist.vue\");\n/* harmony import */ var _sound__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./sound */ \"./assets/public/sound.js\");\n/* harmony import */ var _model__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./model */ \"./assets/public/model.js\");\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n\n\n\n\nconst State = {\n paused: 0,\n playing: 1,\n loading: 2,\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n data() {\n return {\n state: State.paused,\n /// Loaded item\n loaded: null,\n /// Live instance\n live: this.liveArgs ? new _live__WEBPACK_IMPORTED_MODULE_1__[\"default\"](this.liveArgs) : null,\n //! Active panel name\n panel: null,\n //! current playing playlist component\n playlist: null,\n //! players' playlists' sets\n sets: {\n queue: _model__WEBPACK_IMPORTED_MODULE_4__[\"Set\"].storeLoad(_sound__WEBPACK_IMPORTED_MODULE_3__[\"default\"], \"playlist.queue\", { max: 30, unique: true }),\n pin: _model__WEBPACK_IMPORTED_MODULE_4__[\"Set\"].storeLoad(_sound__WEBPACK_IMPORTED_MODULE_3__[\"default\"], \"player.pin\", { max: 30, unique: true }),\n history: _model__WEBPACK_IMPORTED_MODULE_4__[\"Set\"].storeLoad(_sound__WEBPACK_IMPORTED_MODULE_3__[\"default\"], \"player.history\", { max: 30, unique: true }),\n }\n }\n },\n\n props: {\n buttonTitle: String,\n liveArgs: Object,\n },\n\n computed: {\n paused() { return this.state == State.paused; },\n playing() { return this.state == State.playing; },\n loading() { return this.state == State.loading; },\n self() { return this; },\n\n current() {\n return this.loaded || this.live && this.live.current;\n },\n\n progress() {\n let audio = this.$refs.audio;\n return audio && Number.isFinite(audio.duration) && audio.duration ?\n audio.pos / audio.duration * 100 : null;\n },\n\n buttonStyle() {\n if(!this.current)\n return;\n return { backgroundImage: `url(${this.current.cover})` }\n },\n },\n\n methods: {\n playlistButtonClass(name) {\n let set = this.sets[name];\n return (set ? (set.length ? \"\" : \"has-text-grey-light \")\n + (this.panel == name ? \"is-info \"\n : this.playlist && this.playlist == this.$refs[name] ? 'is-primary '\n : '') : '')\n + \"button has-text-weight-bold\";\n },\n\n /// Show/hide panel\n togglePanel(panel) {\n this.panel = this.panel == panel ? null : panel;\n },\n\n /// Return True if item is loaded\n isLoaded(item) {\n return this.loaded && this.loaded.src == item.src;\n },\n\n /// Return True if item is loaded\n isPlaying(item) {\n return this.isLoaded(item) && !this.player.paused;\n },\n\n load(playlist, {src=null, item=null}={}) {\n src = src || item.src;\n this.loaded = item;\n this.playlist = playlist ? this.$refs[playlist] : null;\n\n const audio = this.$refs.audio;\n if(src instanceof Array) {\n audio.innerHTML = '';\n for(var s of src) {\n let source = document.createElement(source);\n source.setAttribute('src', s);\n audio.appendChild(source)\n }\n }\n else {\n audio.src = src;\n }\n audio.load();\n },\n\n /// Play a playlist's sound (by playlist name, and sound index)\n play(playlist=null, index=0) {\n if(!playlist)\n playlist = 'queue';\n\n let item = this.$refs[playlist].get(index);\n if(item) {\n this.load(playlist, {item: item});\n this.sets.history.push(item);\n this.$refs.audio.play().catch(e => console.error(e))\n }\n else\n throw `No sound at index ${index} for playlist ${playlist}`;\n },\n\n /// Push items to playlist (by name)\n push(playlist, ...items) {\n this.$refs[playlist].push(...items);\n },\n\n /// Push and play items\n playItems(playlist, ...items) {\n this.push(playlist, ...items);\n\n let index = this.$refs[playlist].findIndex(items[0]);\n this.$refs[playlist].selectedIndex = index;\n this.play(playlist, index);\n },\n\n /// Play live stream\n playLive() {\n this.load(null, {src: this.live.src});\n this.$refs.audio.play().catch(e => console.error(e))\n this.panel = '';\n },\n\n /// Pause\n pause() {\n this.$refs.audio.pause()\n },\n\n //! Play/pause\n togglePlay() {\n if(this.paused)\n this.$refs.audio.play().catch(e => console.error(e))\n else\n this.pause()\n },\n\n //! Pin/Unpin an item\n togglePin(item) {\n let index = this.sets.pin.findIndex(item);\n if(index > -1)\n this.sets.pin.remove(index);\n else {\n this.sets.pin.push(item);\n if(!this.panel)\n this.panel = 'pin';\n }\n },\n\n /// Audio player state change event\n onState(event) {\n const audio = this.$refs.audio;\n this.state = audio.paused ? State.paused : State.playing;\n\n if(event.type == 'ended' && (!this.playlist || this.playlist.selectNext() == -1))\n this.playLive();\n },\n },\n\n mounted() {\n this.sources = this.$slots.sources;\n },\n\n components: { Playlist: _playlist__WEBPACK_IMPORTED_MODULE_2__[\"default\"] },\n});\n\n\n//# sourceURL=webpack:///./assets/public/player.vue?./node_modules/vue-loader/lib??vue-loader-options");
+eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"State\", function() { return State; });\n/* harmony import */ var vue__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! vue */ \"./node_modules/vue/dist/vue.esm.browser.js\");\n/* harmony import */ var _live__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./live */ \"./assets/public/live.js\");\n/* harmony import */ var _playlist__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./playlist */ \"./assets/public/playlist.vue\");\n/* harmony import */ var _playerProgress__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./playerProgress */ \"./assets/public/playerProgress.vue\");\n/* harmony import */ var _sound__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./sound */ \"./assets/public/sound.js\");\n/* harmony import */ var _model__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./model */ \"./assets/public/model.js\");\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n\n\n\n\n\nconst State = {\n paused: 0,\n playing: 1,\n loading: 2,\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n data() {\n let audio = new Audio();\n audio.addEventListener('ended', e => this.onState(e));\n audio.addEventListener('pause', e => this.onState(e));\n audio.addEventListener('playing', e => this.onState(e));\n audio.addEventListener('timeupdate', e => {\n this.currentTime = this.audio.currentTime;\n });\n audio.addEventListener('durationchange', e => {\n this.duration = Number.isFinite(this.audio.duration) ? this.audio.duration : null;\n });\n\n return {\n audio, duration: 0, currentTime: 0,\n\n /// Live instance\n live: this.liveArgs ? new _live__WEBPACK_IMPORTED_MODULE_1__[\"default\"](this.liveArgs) : null,\n /// Loaded item\n loaded: null,\n //! Active panel name\n panel: null,\n //! current playing playlist component\n playlist: null,\n //! players' playlists' sets\n sets: {\n queue: _model__WEBPACK_IMPORTED_MODULE_5__[\"Set\"].storeLoad(_sound__WEBPACK_IMPORTED_MODULE_4__[\"default\"], \"playlist.queue\", { max: 30, unique: true }),\n pin: _model__WEBPACK_IMPORTED_MODULE_5__[\"Set\"].storeLoad(_sound__WEBPACK_IMPORTED_MODULE_4__[\"default\"], \"player.pin\", { max: 30, unique: true }),\n history: _model__WEBPACK_IMPORTED_MODULE_5__[\"Set\"].storeLoad(_sound__WEBPACK_IMPORTED_MODULE_4__[\"default\"], \"player.history\", { max: 30, unique: true }),\n }\n }\n },\n\n props: {\n buttonTitle: String,\n liveArgs: Object,\n },\n\n computed: {\n self() { return this; },\n paused() { return this.state == State.paused; },\n playing() { return this.state == State.playing; },\n loading() { return this.state == State.loading; },\n\n current() {\n return this.loaded || this.live && this.live.current;\n },\n\n buttonStyle() {\n if(!this.current)\n return;\n return { backgroundImage: `url(${this.current.cover})` }\n },\n },\n\n methods: {\n displayTime(seconds) {\n seconds = parseInt(seconds);\n let s = seconds % 60;\n seconds = (seconds - s) / 60;\n let m = seconds % 60;\n let h = (seconds - m) / 60;\n return h ? `${h}:${m}:${s}` : `${m}:${s}`;\n },\n\n playlistButtonClass(name) {\n let set = this.sets[name];\n return (set ? (set.length ? \"\" : \"has-text-grey-light \")\n + (this.panel == name ? \"is-info \"\n : this.playlist && this.playlist == this.$refs[name] ? 'is-primary '\n : '') : '')\n + \"button has-text-weight-bold\";\n },\n\n /// Show/hide panel\n togglePanel(panel) {\n this.panel = this.panel == panel ? null : panel;\n },\n\n /// Return True if item is loaded\n isLoaded(item) {\n return this.loaded && this.loaded.src == item.src;\n },\n\n /// Return True if item is loaded\n isPlaying(item) {\n return this.isLoaded(item) && !this.player.paused;\n },\n\n load(playlist, {src=null, item=null}={}) {\n src = src || item.src;\n this.loaded = item;\n this.playlist = playlist ? this.$refs[playlist] : null;\n\n const audio = this.audio;\n if(src instanceof Array) {\n audio.innerHTML = '';\n for(var s of src) {\n let source = document.createElement(source);\n source.setAttribute('src', s);\n audio.appendChild(source)\n }\n }\n else {\n audio.src = src;\n }\n audio.load();\n },\n\n /// Play a playlist's sound (by playlist name, and sound index)\n play(playlist=null, index=0) {\n if(index < 0)\n return;\n\n if(!playlist)\n playlist = 'queue';\n\n console.log('play', playlist, index, this.audio);\n let item = this.$refs[playlist].get(index);\n if(item) {\n this.load(playlist, {item: item});\n this.sets.history.push(item);\n this.audio.play().catch(e => console.error(e))\n }\n else\n throw `No sound at index ${index} for playlist ${playlist}`;\n },\n\n /// Push items to playlist (by name)\n push(playlist, ...items) {\n this.$refs[playlist].push(...items);\n },\n\n /// Push and play items\n playItems(playlist, ...items) {\n this.push(playlist, ...items);\n\n let index = this.$refs[playlist].findIndex(items[0]);\n this.$refs[playlist].selectedIndex = index;\n this.play(playlist, index);\n },\n\n /// Play live stream\n playLive() {\n this.load(null, {src: this.live.src});\n this.audio.play().catch(e => console.error(e))\n this.panel = '';\n },\n\n /// Pause\n pause() {\n this.audio.pause()\n },\n\n //! Play/pause\n togglePlay() {\n if(this.paused)\n this.audio.play().catch(e => console.error(e))\n else\n this.pause()\n },\n\n //! Pin/Unpin an item\n togglePin(item) {\n let index = this.sets.pin.findIndex(item);\n if(index > -1)\n this.sets.pin.remove(index);\n else {\n this.sets.pin.push(item);\n if(!this.panel)\n this.panel = 'pin';\n }\n },\n\n /// Audio player state change event\n onState(event) {\n const audio = this.audio;\n this.state = audio.paused ? State.paused : State.playing;\n\n if(event.type == 'ended' && (!this.playlist || this.playlist.selectNext() == -1))\n this.playLive();\n },\n },\n\n mounted() {\n this.sources = this.$slots.sources;\n },\n\n components: { Playlist: _playlist__WEBPACK_IMPORTED_MODULE_2__[\"default\"], Progress: _playerProgress__WEBPACK_IMPORTED_MODULE_3__[\"default\"] },\n});\n\n\n//# sourceURL=webpack:///./assets/public/player.vue?./node_modules/vue-loader/lib??vue-loader-options");
+
+/***/ }),
+
+/***/ "./node_modules/vue-loader/lib/index.js?!./assets/public/playerProgress.vue?vue&type=script&lang=js&":
+/*!*********************************************************************************************************************!*\
+ !*** ./node_modules/vue-loader/lib??vue-loader-options!./assets/public/playerProgress.vue?vue&type=script&lang=js& ***!
+ \*********************************************************************************************************************/
+/*! exports provided: default */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+eval("__webpack_require__.r(__webpack_exports__);\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n props: {\n value: Number,\n max: Number,\n format: { type: Function, default: x => x },\n progressClass: { default: 'has-background-primary' },\n vertical: { type: Boolean, default: false },\n },\n\n computed: {\n progressStyle() {\n if(!this.max)\n return null;\n return this.vertical ? { height: (this.max ? this.value * 100 / this.max : 0) + '%' }\n : { width: (this.max ? this.value * 100 / this.max : 0) + '%' };\n },\n },\n\n methods: {\n xToValue(x) { return x * this.max / this.$refs.bar.getBoundingClientRect().width },\n yToValue(y) { return y * this.max / this.$refs.bar.getBoundingClientRect().height },\n\n onClick(event) {\n let rect = event.currentTarget.getBoundingClientRect()\n this.$emit('select', this.vertical ? this.yToValue(event.clientY - rect.y)\n : this.xToValue(event.clientX - rect.x));\n },\n },\n});\n\n\n//# sourceURL=webpack:///./assets/public/playerProgress.vue?./node_modules/vue-loader/lib??vue-loader-options");
/***/ }),
@@ -593,7 +641,19 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) *
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
-eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\n \"div\",\n { staticClass: \"player\" },\n [\n _c(\"Playlist\", {\n directives: [\n {\n name: \"show\",\n rawName: \"v-show\",\n value: _vm.panel == \"history\",\n expression: \"panel == 'history'\"\n }\n ],\n ref: \"history\",\n staticClass: \"panel-menu menu\",\n attrs: {\n name: \"History\",\n editable: true,\n player: _vm.self,\n set: _vm.sets.history,\n listClass: \"menu-list\",\n itemClass: \"menu-item\"\n },\n on: {\n select: function($event) {\n return _vm.play(\"pin\", $event.index)\n }\n },\n scopedSlots: _vm._u([\n {\n key: \"header\",\n fn: function() {\n return [\n _c(\"p\", { staticClass: \"menu-label\" }, [\n _c(\"span\", { staticClass: \"icon\" }, [\n _c(\"span\", { staticClass: \"fa fa-clock\" })\n ]),\n _vm._v(\"\\n History\\n \")\n ])\n ]\n },\n proxy: true\n }\n ])\n }),\n _vm._v(\" \"),\n _c(\"Playlist\", {\n directives: [\n {\n name: \"show\",\n rawName: \"v-show\",\n value: _vm.panel == \"pin\",\n expression: \"panel == 'pin'\"\n }\n ],\n ref: \"pin\",\n staticClass: \"player-panel menu\",\n attrs: {\n name: \"Pinned\",\n editable: true,\n player: _vm.self,\n set: _vm.sets.pin,\n listClass: \"menu-list\",\n itemClass: \"menu-item\"\n },\n on: {\n select: function($event) {\n return _vm.play(\"pin\", $event.index)\n }\n },\n scopedSlots: _vm._u([\n {\n key: \"header\",\n fn: function() {\n return [\n _c(\"p\", { staticClass: \"menu-label\" }, [\n _c(\"span\", { staticClass: \"icon\" }, [\n _c(\"span\", { staticClass: \"fa fa-thumbtack\" })\n ]),\n _vm._v(\"\\n Pinned\\n \")\n ])\n ]\n },\n proxy: true\n }\n ])\n }),\n _vm._v(\" \"),\n _c(\"Playlist\", {\n directives: [\n {\n name: \"show\",\n rawName: \"v-show\",\n value: _vm.panel == \"queue\",\n expression: \"panel == 'queue'\"\n }\n ],\n ref: \"queue\",\n staticClass: \"player-panel menu\",\n attrs: {\n editable: true,\n player: _vm.self,\n set: _vm.sets.queue,\n listClass: \"menu-list\",\n itemClass: \"menu-item\"\n },\n on: {\n select: function($event) {\n return _vm.play(\"queue\", $event.index)\n }\n },\n scopedSlots: _vm._u([\n {\n key: \"header\",\n fn: function() {\n return [\n _c(\"p\", { staticClass: \"menu-label\" }, [\n _c(\"span\", { staticClass: \"icon\" }, [\n _c(\"span\", { staticClass: \"fa fa-list\" })\n ]),\n _vm._v(\"\\n Playlist\\n \")\n ])\n ]\n },\n proxy: true\n }\n ])\n }),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"player-bar media\" }, [\n _c(\n \"div\",\n { staticClass: \"media-left\" },\n [\n _c(\n \"div\",\n {\n staticClass: \"button\",\n attrs: {\n title: _vm.buttonTitle,\n \"aria-label\": _vm.buttonTitle\n },\n on: {\n click: function($event) {\n return _vm.togglePlay()\n }\n }\n },\n [\n _vm.playing\n ? _c(\"span\", { staticClass: \"fas fa-pause\" })\n : _c(\"span\", { staticClass: \"fas fa-play\" })\n ]\n ),\n _vm._v(\" \"),\n _c(\"audio\", {\n ref: \"audio\",\n attrs: { preload: \"metadata\" },\n on: {\n playing: _vm.onState,\n ended: _vm.onState,\n pause: _vm.onState\n }\n }),\n _vm._v(\" \"),\n _vm._t(\"sources\")\n ],\n 2\n ),\n _vm._v(\" \"),\n _vm.current && _vm.current.cover\n ? _c(\"div\", { staticClass: \"media-left media-cover\" }, [\n _c(\"img\", {\n staticClass: \"cover\",\n attrs: { src: _vm.current.cover }\n })\n ])\n : _vm._e(),\n _vm._v(\" \"),\n _c(\n \"div\",\n { staticClass: \"media-content\" },\n [_vm._t(\"content\", null, { current: _vm.current })],\n 2\n ),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"media-right\" }, [\n _vm.loaded\n ? _c(\n \"button\",\n {\n staticClass: \"button has-text-weight-bold\",\n on: {\n click: function($event) {\n return _vm.playLive()\n }\n }\n },\n [_vm._m(0), _vm._v(\" \"), _c(\"span\", [_vm._v(\"Live\")])]\n )\n : _vm._e(),\n _vm._v(\" \"),\n _c(\n \"button\",\n {\n class: _vm.playlistButtonClass(\"history\"),\n on: {\n click: function($event) {\n return _vm.togglePanel(\"history\")\n }\n }\n },\n [\n _vm.sets.history.length\n ? _c(\"span\", { staticClass: \"mr-2 is-size-6\" }, [\n _vm._v(\n \"\\n \" + _vm._s(_vm.sets.history.length)\n )\n ])\n : _vm._e(),\n _vm._v(\" \"),\n _vm._m(1)\n ]\n ),\n _vm._v(\" \"),\n _c(\n \"button\",\n {\n class: _vm.playlistButtonClass(\"pin\"),\n on: {\n click: function($event) {\n return _vm.togglePanel(\"pin\")\n }\n }\n },\n [\n _vm.sets.pin.length\n ? _c(\"span\", { staticClass: \"mr-2 is-size-6\" }, [\n _vm._v(\n \"\\n \" + _vm._s(_vm.sets.pin.length)\n )\n ])\n : _vm._e(),\n _vm._v(\" \"),\n _vm._m(2)\n ]\n ),\n _vm._v(\" \"),\n _c(\n \"button\",\n {\n class: _vm.playlistButtonClass(\"queue\"),\n on: {\n click: function($event) {\n return _vm.togglePanel(\"queue\")\n }\n }\n },\n [\n _vm.sets.queue.length\n ? _c(\"span\", { staticClass: \"mr-2 is-size-6\" }, [\n _vm._v(\n \"\\n \" + _vm._s(_vm.sets.queue.length)\n )\n ])\n : _vm._e(),\n _vm._v(\" \"),\n _vm._m(3)\n ]\n )\n ])\n ]),\n _vm._v(\" \"),\n _vm.progress\n ? _c(\"div\", [_c(\"span\", { style: { width: _vm.progress } })])\n : _vm._e()\n ],\n 1\n )\n}\nvar staticRenderFns = [\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"span\", { staticClass: \"icon has-text-danger\" }, [\n _c(\"span\", { staticClass: \"fa fa-broadcast-tower\" })\n ])\n },\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"span\", { staticClass: \"icon\" }, [\n _c(\"span\", { staticClass: \"fa fa-clock\" })\n ])\n },\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"span\", { staticClass: \"icon\" }, [\n _c(\"span\", { staticClass: \"fa fa-thumbtack\" })\n ])\n },\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"span\", { staticClass: \"icon\" }, [\n _c(\"span\", { staticClass: \"fa fa-list\" })\n ])\n }\n]\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack:///./assets/public/player.vue?./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options");
+eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\n \"div\",\n { staticClass: \"player\" },\n [\n _c(\"Playlist\", {\n directives: [\n {\n name: \"show\",\n rawName: \"v-show\",\n value: _vm.panel == \"history\",\n expression: \"panel == 'history'\"\n }\n ],\n ref: \"history\",\n staticClass: \"player-panel menu\",\n attrs: {\n name: \"History\",\n editable: true,\n player: _vm.self,\n set: _vm.sets.history,\n listClass: \"menu-list\",\n itemClass: \"menu-item\"\n },\n on: {\n select: function($event) {\n return _vm.play(\"pin\", $event.index)\n }\n },\n scopedSlots: _vm._u([\n {\n key: \"header\",\n fn: function() {\n return [\n _c(\"p\", { staticClass: \"menu-label\" }, [\n _c(\"span\", { staticClass: \"icon\" }, [\n _c(\"span\", { staticClass: \"fa fa-clock\" })\n ]),\n _vm._v(\"\\n History\\n \")\n ])\n ]\n },\n proxy: true\n }\n ])\n }),\n _vm._v(\" \"),\n _c(\"Playlist\", {\n directives: [\n {\n name: \"show\",\n rawName: \"v-show\",\n value: _vm.panel == \"pin\",\n expression: \"panel == 'pin'\"\n }\n ],\n ref: \"pin\",\n staticClass: \"player-panel menu\",\n attrs: {\n name: \"Pinned\",\n editable: true,\n player: _vm.self,\n set: _vm.sets.pin,\n listClass: \"menu-list\",\n itemClass: \"menu-item\"\n },\n on: {\n select: function($event) {\n return _vm.play(\"pin\", $event.index)\n }\n },\n scopedSlots: _vm._u([\n {\n key: \"header\",\n fn: function() {\n return [\n _c(\"p\", { staticClass: \"menu-label\" }, [\n _c(\"span\", { staticClass: \"icon\" }, [\n _c(\"span\", { staticClass: \"fa fa-thumbtack\" })\n ]),\n _vm._v(\"\\n Pinned\\n \")\n ])\n ]\n },\n proxy: true\n }\n ])\n }),\n _vm._v(\" \"),\n _c(\"Playlist\", {\n directives: [\n {\n name: \"show\",\n rawName: \"v-show\",\n value: _vm.panel == \"queue\",\n expression: \"panel == 'queue'\"\n }\n ],\n ref: \"queue\",\n staticClass: \"player-panel menu\",\n attrs: {\n editable: true,\n player: _vm.self,\n set: _vm.sets.queue,\n listClass: \"menu-list\",\n itemClass: \"menu-item\"\n },\n on: {\n select: function($event) {\n return _vm.play(\"queue\", $event.index)\n }\n },\n scopedSlots: _vm._u([\n {\n key: \"header\",\n fn: function() {\n return [\n _c(\"p\", { staticClass: \"menu-label\" }, [\n _c(\"span\", { staticClass: \"icon\" }, [\n _c(\"span\", { staticClass: \"fa fa-list\" })\n ]),\n _vm._v(\"\\n Playlist\\n \")\n ])\n ]\n },\n proxy: true\n }\n ])\n }),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"player-bar media\" }, [\n _c(\n \"div\",\n { staticClass: \"media-left\" },\n [\n _c(\n \"div\",\n {\n staticClass: \"button\",\n attrs: {\n title: _vm.buttonTitle,\n \"aria-label\": _vm.buttonTitle\n },\n on: {\n click: function($event) {\n return _vm.togglePlay()\n }\n }\n },\n [\n _vm.playing\n ? _c(\"span\", { staticClass: \"fas fa-pause\" })\n : _c(\"span\", { staticClass: \"fas fa-play\" })\n ]\n ),\n _vm._v(\" \"),\n _vm._t(\"sources\")\n ],\n 2\n ),\n _vm._v(\" \"),\n _vm.current && _vm.current.cover\n ? _c(\"div\", { staticClass: \"media-left media-cover\" }, [\n _c(\"img\", {\n staticClass: \"cover\",\n attrs: { src: _vm.current.cover }\n })\n ])\n : _vm._e(),\n _vm._v(\" \"),\n _c(\n \"div\",\n { staticClass: \"media-content\" },\n [\n _vm._t(\"content\", null, { current: _vm.current }),\n _vm._v(\" \"),\n this.duration\n ? _c(\"Progress\", {\n attrs: {\n value: this.currentTime,\n max: this.duration,\n format: _vm.displayTime\n },\n on: {\n select: function($event) {\n _vm.audio.currentTime = $event\n }\n }\n })\n : _vm._e()\n ],\n 2\n ),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"media-right\" }, [\n _vm.loaded\n ? _c(\n \"button\",\n {\n staticClass: \"button has-text-weight-bold\",\n on: {\n click: function($event) {\n return _vm.playLive()\n }\n }\n },\n [_vm._m(0), _vm._v(\" \"), _c(\"span\", [_vm._v(\"Live\")])]\n )\n : _vm._e(),\n _vm._v(\" \"),\n _c(\n \"button\",\n {\n class: _vm.playlistButtonClass(\"history\"),\n on: {\n click: function($event) {\n return _vm.togglePanel(\"history\")\n }\n }\n },\n [\n _vm.sets.history.length\n ? _c(\"span\", { staticClass: \"mr-2 is-size-6\" }, [\n _vm._v(\n \"\\n \" + _vm._s(_vm.sets.history.length)\n )\n ])\n : _vm._e(),\n _vm._v(\" \"),\n _vm._m(1)\n ]\n ),\n _vm._v(\" \"),\n _c(\n \"button\",\n {\n class: _vm.playlistButtonClass(\"pin\"),\n on: {\n click: function($event) {\n return _vm.togglePanel(\"pin\")\n }\n }\n },\n [\n _vm.sets.pin.length\n ? _c(\"span\", { staticClass: \"mr-2 is-size-6\" }, [\n _vm._v(\n \"\\n \" + _vm._s(_vm.sets.pin.length)\n )\n ])\n : _vm._e(),\n _vm._v(\" \"),\n _vm._m(2)\n ]\n ),\n _vm._v(\" \"),\n _c(\n \"button\",\n {\n class: _vm.playlistButtonClass(\"queue\"),\n on: {\n click: function($event) {\n return _vm.togglePanel(\"queue\")\n }\n }\n },\n [\n _vm.sets.queue.length\n ? _c(\"span\", { staticClass: \"mr-2 is-size-6\" }, [\n _vm._v(\n \"\\n \" + _vm._s(_vm.sets.queue.length)\n )\n ])\n : _vm._e(),\n _vm._v(\" \"),\n _vm._m(3)\n ]\n )\n ])\n ])\n ],\n 1\n )\n}\nvar staticRenderFns = [\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"span\", { staticClass: \"icon has-text-danger\" }, [\n _c(\"span\", { staticClass: \"fa fa-broadcast-tower\" })\n ])\n },\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"span\", { staticClass: \"icon\" }, [\n _c(\"span\", { staticClass: \"fa fa-clock\" })\n ])\n },\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"span\", { staticClass: \"icon\" }, [\n _c(\"span\", { staticClass: \"fa fa-thumbtack\" })\n ])\n },\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"span\", { staticClass: \"icon\" }, [\n _c(\"span\", { staticClass: \"fa fa-list\" })\n ])\n }\n]\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack:///./assets/public/player.vue?./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options");
+
+/***/ }),
+
+/***/ "./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/vue-loader/lib/index.js?!./assets/public/playerProgress.vue?vue&type=template&id=a8a834d4&":
+/*!*******************************************************************************************************************************************************************************************************!*\
+ !*** ./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options!./assets/public/playerProgress.vue?vue&type=template&id=a8a834d4& ***!
+ \*******************************************************************************************************************************************************************************************************/
+/*! exports provided: render, staticRenderFns */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"div\", { staticClass: \"media\" }, [\n _c(\n \"div\",\n { staticClass: \"media-left\" },\n [\n _vm._t(\"value\", [_vm._v(_vm._s(_vm.format(_vm.value)))], {\n value: _vm.value,\n max: _vm.max\n })\n ],\n 2\n ),\n _vm._v(\" \"),\n _c(\n \"div\",\n {\n ref: \"bar\",\n staticClass: \"media-content\",\n on: {\n click: function($event) {\n $event.stopPropagation()\n return _vm.onClick($event)\n }\n }\n },\n [\n _c(\"div\", { class: _vm.progressClass, style: _vm.progressStyle }, [\n _vm._v(\" \")\n ])\n ]\n ),\n _vm._v(\" \"),\n _c(\n \"div\",\n { staticClass: \"media-right\" },\n [\n _vm._t(\"value\", [_vm._v(_vm._s(_vm.format(_vm.max)))], {\n value: _vm.value,\n max: _vm.max\n })\n ],\n 2\n )\n ])\n}\nvar staticRenderFns = []\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack:///./assets/public/playerProgress.vue?./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options");
/***/ }),
diff --git a/aircox/static/aircox/main.css b/aircox/static/aircox/main.css
index 33887a9..b71f99e 100644
--- a/aircox/static/aircox/main.css
+++ b/aircox/static/aircox/main.css
@@ -7396,14 +7396,14 @@ a.navbar-item.is-active {
overflow-y: auto; }
.player .player-bar {
border-top: 1px #b5b5b5 solid; }
- .player .player-bar .media-left:not(:last-child) {
+ .player .player-bar > .media-left:not(:last-child) {
margin-right: 0em; }
- .player .player-bar .media-cover {
+ .player .player-bar > .media-cover {
border-left: 1px black solid; }
.player .player-bar .cover {
font-size: 1.5rem !important;
height: 2.5em !important; }
- .player .player-bar .media-content {
+ .player .player-bar > .media-content {
padding-top: 0.4em;
padding-left: 0.4em; }
.player .player-bar .button {
diff --git a/aircox/static/aircox/main.js b/aircox/static/aircox/main.js
index b963a20..4b455f5 100644
--- a/aircox/static/aircox/main.js
+++ b/aircox/static/aircox/main.js
@@ -311,6 +311,42 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _nod
/***/ }),
+/***/ "./assets/public/playerProgress.vue":
+/*!******************************************!*\
+ !*** ./assets/public/playerProgress.vue ***!
+ \******************************************/
+/*! no static exports found */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _playerProgress_vue_vue_type_template_id_a8a834d4___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./playerProgress.vue?vue&type=template&id=a8a834d4& */ \"./assets/public/playerProgress.vue?vue&type=template&id=a8a834d4&\");\n/* harmony import */ var _playerProgress_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./playerProgress.vue?vue&type=script&lang=js& */ \"./assets/public/playerProgress.vue?vue&type=script&lang=js&\");\n/* harmony reexport (unknown) */ for(var __WEBPACK_IMPORT_KEY__ in _playerProgress_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__) if([\"default\"].indexOf(__WEBPACK_IMPORT_KEY__) < 0) (function(key) { __webpack_require__.d(__webpack_exports__, key, function() { return _playerProgress_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__[key]; }) }(__WEBPACK_IMPORT_KEY__));\n/* harmony import */ var _node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../node_modules/vue-loader/lib/runtime/componentNormalizer.js */ \"./node_modules/vue-loader/lib/runtime/componentNormalizer.js\");\n\n\n\n\n\n/* normalize component */\n\nvar component = Object(_node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(\n _playerProgress_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n _playerProgress_vue_vue_type_template_id_a8a834d4___WEBPACK_IMPORTED_MODULE_0__[\"render\"],\n _playerProgress_vue_vue_type_template_id_a8a834d4___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"],\n false,\n null,\n null,\n null\n \n)\n\n/* hot reload */\nif (false) { var api; }\ncomponent.options.__file = \"assets/public/playerProgress.vue\"\n/* harmony default export */ __webpack_exports__[\"default\"] = (component.exports);\n\n//# sourceURL=webpack:///./assets/public/playerProgress.vue?");
+
+/***/ }),
+
+/***/ "./assets/public/playerProgress.vue?vue&type=script&lang=js&":
+/*!*******************************************************************!*\
+ !*** ./assets/public/playerProgress.vue?vue&type=script&lang=js& ***!
+ \*******************************************************************/
+/*! no static exports found */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_vue_loader_lib_index_js_vue_loader_options_playerProgress_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../node_modules/vue-loader/lib??vue-loader-options!./playerProgress.vue?vue&type=script&lang=js& */ \"./node_modules/vue-loader/lib/index.js?!./assets/public/playerProgress.vue?vue&type=script&lang=js&\");\n/* empty/unused harmony star reexport */ /* harmony default export */ __webpack_exports__[\"default\"] = (_node_modules_vue_loader_lib_index_js_vue_loader_options_playerProgress_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__[\"default\"]); \n\n//# sourceURL=webpack:///./assets/public/playerProgress.vue?");
+
+/***/ }),
+
+/***/ "./assets/public/playerProgress.vue?vue&type=template&id=a8a834d4&":
+/*!*************************************************************************!*\
+ !*** ./assets/public/playerProgress.vue?vue&type=template&id=a8a834d4& ***!
+ \*************************************************************************/
+/*! exports provided: render, staticRenderFns */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_playerProgress_vue_vue_type_template_id_a8a834d4___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!../../node_modules/vue-loader/lib??vue-loader-options!./playerProgress.vue?vue&type=template&id=a8a834d4& */ \"./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/vue-loader/lib/index.js?!./assets/public/playerProgress.vue?vue&type=template&id=a8a834d4&\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_playerProgress_vue_vue_type_template_id_a8a834d4___WEBPACK_IMPORTED_MODULE_0__[\"render\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_playerProgress_vue_vue_type_template_id_a8a834d4___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"]; });\n\n\n\n//# sourceURL=webpack:///./assets/public/playerProgress.vue?");
+
+/***/ }),
+
/***/ "./assets/public/playlist.vue":
/*!************************************!*\
!*** ./assets/public/playlist.vue ***!
@@ -438,7 +474,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var loda
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
-eval("__webpack_require__.r(__webpack_exports__);\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n data() {\n return {\n selectedIndex: this.defaultIndex,\n }\n },\n\n props: {\n listClass: String,\n itemClass: String,\n defaultIndex: { type: Number, default: -1},\n set: Object,\n },\n\n computed: {\n model() { return this.set.model },\n items() { return this.set.items },\n length() { return this.set.length },\n\n selected() {\n return this.selectedIndex > -1 && this.items.length > this.selectedIndex > -1\n ? this.items[this.selectedIndex] : null;\n },\n },\n\n methods: {\n get(index) { return this.set.get(index) },\n find(item) { return this.set.find(item) },\n findIndex(item) { return this.set.findIndex(item) },\n\n push(...items) {\n let index = this.set.length;\n for(var item of items)\n this.set.push(item);\n },\n\n remove(index, select=False) {\n this.set.remove(index);\n if(index < this.selectedIndex)\n this.selectedIndex--;\n if(select && this.selectedIndex == index)\n this.select(index)\n },\n\n select(index) {\n this.selectedIndex = index > -1 && this.items.length ? index % this.items.length : -1;\n this.$emit('select', { target: this, item: this.selected, index: this.selectedIndex });\n return this.selectedIndex;\n },\n\n\n },\n});\n\n\n//# sourceURL=webpack:///./assets/public/list.vue?./node_modules/vue-loader/lib??vue-loader-options");
+eval("__webpack_require__.r(__webpack_exports__);\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n data() {\n return {\n selectedIndex: this.defaultIndex,\n }\n },\n\n props: {\n listClass: String,\n itemClass: String,\n defaultIndex: { type: Number, default: -1},\n set: Object,\n },\n\n computed: {\n model() { return this.set.model },\n items() { return this.set.items },\n length() { return this.set.length },\n\n selected() {\n return this.selectedIndex > -1 && this.items.length > this.selectedIndex > -1\n ? this.items[this.selectedIndex] : null;\n },\n },\n\n methods: {\n get(index) { return this.set.get(index) },\n find(item) { return this.set.find(item) },\n findIndex(item) { return this.set.findIndex(item) },\n\n push(...items) {\n let index = this.set.length;\n for(var item of items)\n this.set.push(item);\n },\n\n remove(index, select=False) {\n this.set.remove(index);\n if(index < this.selectedIndex)\n this.selectedIndex--;\n if(select && this.selectedIndex == index)\n this.select(index)\n },\n\n select(index) {\n this.selectedIndex = index > -1 && this.items.length ? index % this.items.length : -1;\n this.$emit('select', { item: this.selected, index: this.selectedIndex });\n return this.selectedIndex;\n },\n\n\n },\n});\n\n\n//# sourceURL=webpack:///./assets/public/list.vue?./node_modules/vue-loader/lib??vue-loader-options");
/***/ }),
@@ -450,7 +486,19 @@ eval("__webpack_require__.r(__webpack_exports__);\n//\n//\n//\n//\n//\n//\n//\n/
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
-eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"State\", function() { return State; });\n/* harmony import */ var vue__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! vue */ \"./node_modules/vue/dist/vue.esm.browser.js\");\n/* harmony import */ var _live__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./live */ \"./assets/public/live.js\");\n/* harmony import */ var _playlist__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./playlist */ \"./assets/public/playlist.vue\");\n/* harmony import */ var _sound__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./sound */ \"./assets/public/sound.js\");\n/* harmony import */ var _model__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./model */ \"./assets/public/model.js\");\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n\n\n\n\nconst State = {\n paused: 0,\n playing: 1,\n loading: 2,\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n data() {\n return {\n state: State.paused,\n /// Loaded item\n loaded: null,\n /// Live instance\n live: this.liveArgs ? new _live__WEBPACK_IMPORTED_MODULE_1__[\"default\"](this.liveArgs) : null,\n //! Active panel name\n panel: null,\n //! current playing playlist component\n playlist: null,\n //! players' playlists' sets\n sets: {\n queue: _model__WEBPACK_IMPORTED_MODULE_4__[\"Set\"].storeLoad(_sound__WEBPACK_IMPORTED_MODULE_3__[\"default\"], \"playlist.queue\", { max: 30, unique: true }),\n pin: _model__WEBPACK_IMPORTED_MODULE_4__[\"Set\"].storeLoad(_sound__WEBPACK_IMPORTED_MODULE_3__[\"default\"], \"player.pin\", { max: 30, unique: true }),\n history: _model__WEBPACK_IMPORTED_MODULE_4__[\"Set\"].storeLoad(_sound__WEBPACK_IMPORTED_MODULE_3__[\"default\"], \"player.history\", { max: 30, unique: true }),\n }\n }\n },\n\n props: {\n buttonTitle: String,\n liveArgs: Object,\n },\n\n computed: {\n paused() { return this.state == State.paused; },\n playing() { return this.state == State.playing; },\n loading() { return this.state == State.loading; },\n self() { return this; },\n\n current() {\n return this.loaded || this.live && this.live.current;\n },\n\n progress() {\n let audio = this.$refs.audio;\n return audio && Number.isFinite(audio.duration) && audio.duration ?\n audio.pos / audio.duration * 100 : null;\n },\n\n buttonStyle() {\n if(!this.current)\n return;\n return { backgroundImage: `url(${this.current.cover})` }\n },\n },\n\n methods: {\n playlistButtonClass(name) {\n let set = this.sets[name];\n return (set ? (set.length ? \"\" : \"has-text-grey-light \")\n + (this.panel == name ? \"is-info \"\n : this.playlist && this.playlist == this.$refs[name] ? 'is-primary '\n : '') : '')\n + \"button has-text-weight-bold\";\n },\n\n /// Show/hide panel\n togglePanel(panel) {\n this.panel = this.panel == panel ? null : panel;\n },\n\n /// Return True if item is loaded\n isLoaded(item) {\n return this.loaded && this.loaded.src == item.src;\n },\n\n /// Return True if item is loaded\n isPlaying(item) {\n return this.isLoaded(item) && !this.player.paused;\n },\n\n load(playlist, {src=null, item=null}={}) {\n src = src || item.src;\n this.loaded = item;\n this.playlist = playlist ? this.$refs[playlist] : null;\n\n const audio = this.$refs.audio;\n if(src instanceof Array) {\n audio.innerHTML = '';\n for(var s of src) {\n let source = document.createElement(source);\n source.setAttribute('src', s);\n audio.appendChild(source)\n }\n }\n else {\n audio.src = src;\n }\n audio.load();\n },\n\n /// Play a playlist's sound (by playlist name, and sound index)\n play(playlist=null, index=0) {\n if(!playlist)\n playlist = 'queue';\n\n let item = this.$refs[playlist].get(index);\n if(item) {\n this.load(playlist, {item: item});\n this.sets.history.push(item);\n this.$refs.audio.play().catch(e => console.error(e))\n }\n else\n throw `No sound at index ${index} for playlist ${playlist}`;\n },\n\n /// Push items to playlist (by name)\n push(playlist, ...items) {\n this.$refs[playlist].push(...items);\n },\n\n /// Push and play items\n playItems(playlist, ...items) {\n this.push(playlist, ...items);\n\n let index = this.$refs[playlist].findIndex(items[0]);\n this.$refs[playlist].selectedIndex = index;\n this.play(playlist, index);\n },\n\n /// Play live stream\n playLive() {\n this.load(null, {src: this.live.src});\n this.$refs.audio.play().catch(e => console.error(e))\n this.panel = '';\n },\n\n /// Pause\n pause() {\n this.$refs.audio.pause()\n },\n\n //! Play/pause\n togglePlay() {\n if(this.paused)\n this.$refs.audio.play().catch(e => console.error(e))\n else\n this.pause()\n },\n\n //! Pin/Unpin an item\n togglePin(item) {\n let index = this.sets.pin.findIndex(item);\n if(index > -1)\n this.sets.pin.remove(index);\n else {\n this.sets.pin.push(item);\n if(!this.panel)\n this.panel = 'pin';\n }\n },\n\n /// Audio player state change event\n onState(event) {\n const audio = this.$refs.audio;\n this.state = audio.paused ? State.paused : State.playing;\n\n if(event.type == 'ended' && (!this.playlist || this.playlist.selectNext() == -1))\n this.playLive();\n },\n },\n\n mounted() {\n this.sources = this.$slots.sources;\n },\n\n components: { Playlist: _playlist__WEBPACK_IMPORTED_MODULE_2__[\"default\"] },\n});\n\n\n//# sourceURL=webpack:///./assets/public/player.vue?./node_modules/vue-loader/lib??vue-loader-options");
+eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"State\", function() { return State; });\n/* harmony import */ var vue__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! vue */ \"./node_modules/vue/dist/vue.esm.browser.js\");\n/* harmony import */ var _live__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./live */ \"./assets/public/live.js\");\n/* harmony import */ var _playlist__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./playlist */ \"./assets/public/playlist.vue\");\n/* harmony import */ var _playerProgress__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./playerProgress */ \"./assets/public/playerProgress.vue\");\n/* harmony import */ var _sound__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./sound */ \"./assets/public/sound.js\");\n/* harmony import */ var _model__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./model */ \"./assets/public/model.js\");\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n\n\n\n\n\nconst State = {\n paused: 0,\n playing: 1,\n loading: 2,\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n data() {\n let audio = new Audio();\n audio.addEventListener('ended', e => this.onState(e));\n audio.addEventListener('pause', e => this.onState(e));\n audio.addEventListener('playing', e => this.onState(e));\n audio.addEventListener('timeupdate', e => {\n this.currentTime = this.audio.currentTime;\n });\n audio.addEventListener('durationchange', e => {\n this.duration = Number.isFinite(this.audio.duration) ? this.audio.duration : null;\n });\n\n return {\n audio, duration: 0, currentTime: 0,\n\n /// Live instance\n live: this.liveArgs ? new _live__WEBPACK_IMPORTED_MODULE_1__[\"default\"](this.liveArgs) : null,\n /// Loaded item\n loaded: null,\n //! Active panel name\n panel: null,\n //! current playing playlist component\n playlist: null,\n //! players' playlists' sets\n sets: {\n queue: _model__WEBPACK_IMPORTED_MODULE_5__[\"Set\"].storeLoad(_sound__WEBPACK_IMPORTED_MODULE_4__[\"default\"], \"playlist.queue\", { max: 30, unique: true }),\n pin: _model__WEBPACK_IMPORTED_MODULE_5__[\"Set\"].storeLoad(_sound__WEBPACK_IMPORTED_MODULE_4__[\"default\"], \"player.pin\", { max: 30, unique: true }),\n history: _model__WEBPACK_IMPORTED_MODULE_5__[\"Set\"].storeLoad(_sound__WEBPACK_IMPORTED_MODULE_4__[\"default\"], \"player.history\", { max: 30, unique: true }),\n }\n }\n },\n\n props: {\n buttonTitle: String,\n liveArgs: Object,\n },\n\n computed: {\n self() { return this; },\n paused() { return this.state == State.paused; },\n playing() { return this.state == State.playing; },\n loading() { return this.state == State.loading; },\n\n current() {\n return this.loaded || this.live && this.live.current;\n },\n\n buttonStyle() {\n if(!this.current)\n return;\n return { backgroundImage: `url(${this.current.cover})` }\n },\n },\n\n methods: {\n displayTime(seconds) {\n seconds = parseInt(seconds);\n let s = seconds % 60;\n seconds = (seconds - s) / 60;\n let m = seconds % 60;\n let h = (seconds - m) / 60;\n return h ? `${h}:${m}:${s}` : `${m}:${s}`;\n },\n\n playlistButtonClass(name) {\n let set = this.sets[name];\n return (set ? (set.length ? \"\" : \"has-text-grey-light \")\n + (this.panel == name ? \"is-info \"\n : this.playlist && this.playlist == this.$refs[name] ? 'is-primary '\n : '') : '')\n + \"button has-text-weight-bold\";\n },\n\n /// Show/hide panel\n togglePanel(panel) {\n this.panel = this.panel == panel ? null : panel;\n },\n\n /// Return True if item is loaded\n isLoaded(item) {\n return this.loaded && this.loaded.src == item.src;\n },\n\n /// Return True if item is loaded\n isPlaying(item) {\n return this.isLoaded(item) && !this.player.paused;\n },\n\n load(playlist, {src=null, item=null}={}) {\n src = src || item.src;\n this.loaded = item;\n this.playlist = playlist ? this.$refs[playlist] : null;\n\n const audio = this.audio;\n if(src instanceof Array) {\n audio.innerHTML = '';\n for(var s of src) {\n let source = document.createElement(source);\n source.setAttribute('src', s);\n audio.appendChild(source)\n }\n }\n else {\n audio.src = src;\n }\n audio.load();\n },\n\n /// Play a playlist's sound (by playlist name, and sound index)\n play(playlist=null, index=0) {\n if(index < 0)\n return;\n\n if(!playlist)\n playlist = 'queue';\n\n console.log('play', playlist, index, this.audio);\n let item = this.$refs[playlist].get(index);\n if(item) {\n this.load(playlist, {item: item});\n this.sets.history.push(item);\n this.audio.play().catch(e => console.error(e))\n }\n else\n throw `No sound at index ${index} for playlist ${playlist}`;\n },\n\n /// Push items to playlist (by name)\n push(playlist, ...items) {\n this.$refs[playlist].push(...items);\n },\n\n /// Push and play items\n playItems(playlist, ...items) {\n this.push(playlist, ...items);\n\n let index = this.$refs[playlist].findIndex(items[0]);\n this.$refs[playlist].selectedIndex = index;\n this.play(playlist, index);\n },\n\n /// Play live stream\n playLive() {\n this.load(null, {src: this.live.src});\n this.audio.play().catch(e => console.error(e))\n this.panel = '';\n },\n\n /// Pause\n pause() {\n this.audio.pause()\n },\n\n //! Play/pause\n togglePlay() {\n if(this.paused)\n this.audio.play().catch(e => console.error(e))\n else\n this.pause()\n },\n\n //! Pin/Unpin an item\n togglePin(item) {\n let index = this.sets.pin.findIndex(item);\n if(index > -1)\n this.sets.pin.remove(index);\n else {\n this.sets.pin.push(item);\n if(!this.panel)\n this.panel = 'pin';\n }\n },\n\n /// Audio player state change event\n onState(event) {\n const audio = this.audio;\n this.state = audio.paused ? State.paused : State.playing;\n\n if(event.type == 'ended' && (!this.playlist || this.playlist.selectNext() == -1))\n this.playLive();\n },\n },\n\n mounted() {\n this.sources = this.$slots.sources;\n },\n\n components: { Playlist: _playlist__WEBPACK_IMPORTED_MODULE_2__[\"default\"], Progress: _playerProgress__WEBPACK_IMPORTED_MODULE_3__[\"default\"] },\n});\n\n\n//# sourceURL=webpack:///./assets/public/player.vue?./node_modules/vue-loader/lib??vue-loader-options");
+
+/***/ }),
+
+/***/ "./node_modules/vue-loader/lib/index.js?!./assets/public/playerProgress.vue?vue&type=script&lang=js&":
+/*!*********************************************************************************************************************!*\
+ !*** ./node_modules/vue-loader/lib??vue-loader-options!./assets/public/playerProgress.vue?vue&type=script&lang=js& ***!
+ \*********************************************************************************************************************/
+/*! exports provided: default */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+eval("__webpack_require__.r(__webpack_exports__);\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n props: {\n value: Number,\n max: Number,\n format: { type: Function, default: x => x },\n progressClass: { default: 'has-background-primary' },\n vertical: { type: Boolean, default: false },\n },\n\n computed: {\n progressStyle() {\n if(!this.max)\n return null;\n return this.vertical ? { height: (this.max ? this.value * 100 / this.max : 0) + '%' }\n : { width: (this.max ? this.value * 100 / this.max : 0) + '%' };\n },\n },\n\n methods: {\n xToValue(x) { return x * this.max / this.$refs.bar.getBoundingClientRect().width },\n yToValue(y) { return y * this.max / this.$refs.bar.getBoundingClientRect().height },\n\n onClick(event) {\n let rect = event.currentTarget.getBoundingClientRect()\n this.$emit('select', this.vertical ? this.yToValue(event.clientY - rect.y)\n : this.xToValue(event.clientX - rect.x));\n },\n },\n});\n\n\n//# sourceURL=webpack:///./assets/public/playerProgress.vue?./node_modules/vue-loader/lib??vue-loader-options");
/***/ }),
@@ -510,7 +558,19 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) *
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
-eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\n \"div\",\n { staticClass: \"player\" },\n [\n _c(\"Playlist\", {\n directives: [\n {\n name: \"show\",\n rawName: \"v-show\",\n value: _vm.panel == \"history\",\n expression: \"panel == 'history'\"\n }\n ],\n ref: \"history\",\n staticClass: \"panel-menu menu\",\n attrs: {\n name: \"History\",\n editable: true,\n player: _vm.self,\n set: _vm.sets.history,\n listClass: \"menu-list\",\n itemClass: \"menu-item\"\n },\n on: {\n select: function($event) {\n return _vm.play(\"pin\", $event.index)\n }\n },\n scopedSlots: _vm._u([\n {\n key: \"header\",\n fn: function() {\n return [\n _c(\"p\", { staticClass: \"menu-label\" }, [\n _c(\"span\", { staticClass: \"icon\" }, [\n _c(\"span\", { staticClass: \"fa fa-clock\" })\n ]),\n _vm._v(\"\\n History\\n \")\n ])\n ]\n },\n proxy: true\n }\n ])\n }),\n _vm._v(\" \"),\n _c(\"Playlist\", {\n directives: [\n {\n name: \"show\",\n rawName: \"v-show\",\n value: _vm.panel == \"pin\",\n expression: \"panel == 'pin'\"\n }\n ],\n ref: \"pin\",\n staticClass: \"player-panel menu\",\n attrs: {\n name: \"Pinned\",\n editable: true,\n player: _vm.self,\n set: _vm.sets.pin,\n listClass: \"menu-list\",\n itemClass: \"menu-item\"\n },\n on: {\n select: function($event) {\n return _vm.play(\"pin\", $event.index)\n }\n },\n scopedSlots: _vm._u([\n {\n key: \"header\",\n fn: function() {\n return [\n _c(\"p\", { staticClass: \"menu-label\" }, [\n _c(\"span\", { staticClass: \"icon\" }, [\n _c(\"span\", { staticClass: \"fa fa-thumbtack\" })\n ]),\n _vm._v(\"\\n Pinned\\n \")\n ])\n ]\n },\n proxy: true\n }\n ])\n }),\n _vm._v(\" \"),\n _c(\"Playlist\", {\n directives: [\n {\n name: \"show\",\n rawName: \"v-show\",\n value: _vm.panel == \"queue\",\n expression: \"panel == 'queue'\"\n }\n ],\n ref: \"queue\",\n staticClass: \"player-panel menu\",\n attrs: {\n editable: true,\n player: _vm.self,\n set: _vm.sets.queue,\n listClass: \"menu-list\",\n itemClass: \"menu-item\"\n },\n on: {\n select: function($event) {\n return _vm.play(\"queue\", $event.index)\n }\n },\n scopedSlots: _vm._u([\n {\n key: \"header\",\n fn: function() {\n return [\n _c(\"p\", { staticClass: \"menu-label\" }, [\n _c(\"span\", { staticClass: \"icon\" }, [\n _c(\"span\", { staticClass: \"fa fa-list\" })\n ]),\n _vm._v(\"\\n Playlist\\n \")\n ])\n ]\n },\n proxy: true\n }\n ])\n }),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"player-bar media\" }, [\n _c(\n \"div\",\n { staticClass: \"media-left\" },\n [\n _c(\n \"div\",\n {\n staticClass: \"button\",\n attrs: {\n title: _vm.buttonTitle,\n \"aria-label\": _vm.buttonTitle\n },\n on: {\n click: function($event) {\n return _vm.togglePlay()\n }\n }\n },\n [\n _vm.playing\n ? _c(\"span\", { staticClass: \"fas fa-pause\" })\n : _c(\"span\", { staticClass: \"fas fa-play\" })\n ]\n ),\n _vm._v(\" \"),\n _c(\"audio\", {\n ref: \"audio\",\n attrs: { preload: \"metadata\" },\n on: {\n playing: _vm.onState,\n ended: _vm.onState,\n pause: _vm.onState\n }\n }),\n _vm._v(\" \"),\n _vm._t(\"sources\")\n ],\n 2\n ),\n _vm._v(\" \"),\n _vm.current && _vm.current.cover\n ? _c(\"div\", { staticClass: \"media-left media-cover\" }, [\n _c(\"img\", {\n staticClass: \"cover\",\n attrs: { src: _vm.current.cover }\n })\n ])\n : _vm._e(),\n _vm._v(\" \"),\n _c(\n \"div\",\n { staticClass: \"media-content\" },\n [_vm._t(\"content\", null, { current: _vm.current })],\n 2\n ),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"media-right\" }, [\n _vm.loaded\n ? _c(\n \"button\",\n {\n staticClass: \"button has-text-weight-bold\",\n on: {\n click: function($event) {\n return _vm.playLive()\n }\n }\n },\n [_vm._m(0), _vm._v(\" \"), _c(\"span\", [_vm._v(\"Live\")])]\n )\n : _vm._e(),\n _vm._v(\" \"),\n _c(\n \"button\",\n {\n class: _vm.playlistButtonClass(\"history\"),\n on: {\n click: function($event) {\n return _vm.togglePanel(\"history\")\n }\n }\n },\n [\n _vm.sets.history.length\n ? _c(\"span\", { staticClass: \"mr-2 is-size-6\" }, [\n _vm._v(\n \"\\n \" + _vm._s(_vm.sets.history.length)\n )\n ])\n : _vm._e(),\n _vm._v(\" \"),\n _vm._m(1)\n ]\n ),\n _vm._v(\" \"),\n _c(\n \"button\",\n {\n class: _vm.playlistButtonClass(\"pin\"),\n on: {\n click: function($event) {\n return _vm.togglePanel(\"pin\")\n }\n }\n },\n [\n _vm.sets.pin.length\n ? _c(\"span\", { staticClass: \"mr-2 is-size-6\" }, [\n _vm._v(\n \"\\n \" + _vm._s(_vm.sets.pin.length)\n )\n ])\n : _vm._e(),\n _vm._v(\" \"),\n _vm._m(2)\n ]\n ),\n _vm._v(\" \"),\n _c(\n \"button\",\n {\n class: _vm.playlistButtonClass(\"queue\"),\n on: {\n click: function($event) {\n return _vm.togglePanel(\"queue\")\n }\n }\n },\n [\n _vm.sets.queue.length\n ? _c(\"span\", { staticClass: \"mr-2 is-size-6\" }, [\n _vm._v(\n \"\\n \" + _vm._s(_vm.sets.queue.length)\n )\n ])\n : _vm._e(),\n _vm._v(\" \"),\n _vm._m(3)\n ]\n )\n ])\n ]),\n _vm._v(\" \"),\n _vm.progress\n ? _c(\"div\", [_c(\"span\", { style: { width: _vm.progress } })])\n : _vm._e()\n ],\n 1\n )\n}\nvar staticRenderFns = [\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"span\", { staticClass: \"icon has-text-danger\" }, [\n _c(\"span\", { staticClass: \"fa fa-broadcast-tower\" })\n ])\n },\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"span\", { staticClass: \"icon\" }, [\n _c(\"span\", { staticClass: \"fa fa-clock\" })\n ])\n },\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"span\", { staticClass: \"icon\" }, [\n _c(\"span\", { staticClass: \"fa fa-thumbtack\" })\n ])\n },\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"span\", { staticClass: \"icon\" }, [\n _c(\"span\", { staticClass: \"fa fa-list\" })\n ])\n }\n]\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack:///./assets/public/player.vue?./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options");
+eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\n \"div\",\n { staticClass: \"player\" },\n [\n _c(\"Playlist\", {\n directives: [\n {\n name: \"show\",\n rawName: \"v-show\",\n value: _vm.panel == \"history\",\n expression: \"panel == 'history'\"\n }\n ],\n ref: \"history\",\n staticClass: \"player-panel menu\",\n attrs: {\n name: \"History\",\n editable: true,\n player: _vm.self,\n set: _vm.sets.history,\n listClass: \"menu-list\",\n itemClass: \"menu-item\"\n },\n on: {\n select: function($event) {\n return _vm.play(\"pin\", $event.index)\n }\n },\n scopedSlots: _vm._u([\n {\n key: \"header\",\n fn: function() {\n return [\n _c(\"p\", { staticClass: \"menu-label\" }, [\n _c(\"span\", { staticClass: \"icon\" }, [\n _c(\"span\", { staticClass: \"fa fa-clock\" })\n ]),\n _vm._v(\"\\n History\\n \")\n ])\n ]\n },\n proxy: true\n }\n ])\n }),\n _vm._v(\" \"),\n _c(\"Playlist\", {\n directives: [\n {\n name: \"show\",\n rawName: \"v-show\",\n value: _vm.panel == \"pin\",\n expression: \"panel == 'pin'\"\n }\n ],\n ref: \"pin\",\n staticClass: \"player-panel menu\",\n attrs: {\n name: \"Pinned\",\n editable: true,\n player: _vm.self,\n set: _vm.sets.pin,\n listClass: \"menu-list\",\n itemClass: \"menu-item\"\n },\n on: {\n select: function($event) {\n return _vm.play(\"pin\", $event.index)\n }\n },\n scopedSlots: _vm._u([\n {\n key: \"header\",\n fn: function() {\n return [\n _c(\"p\", { staticClass: \"menu-label\" }, [\n _c(\"span\", { staticClass: \"icon\" }, [\n _c(\"span\", { staticClass: \"fa fa-thumbtack\" })\n ]),\n _vm._v(\"\\n Pinned\\n \")\n ])\n ]\n },\n proxy: true\n }\n ])\n }),\n _vm._v(\" \"),\n _c(\"Playlist\", {\n directives: [\n {\n name: \"show\",\n rawName: \"v-show\",\n value: _vm.panel == \"queue\",\n expression: \"panel == 'queue'\"\n }\n ],\n ref: \"queue\",\n staticClass: \"player-panel menu\",\n attrs: {\n editable: true,\n player: _vm.self,\n set: _vm.sets.queue,\n listClass: \"menu-list\",\n itemClass: \"menu-item\"\n },\n on: {\n select: function($event) {\n return _vm.play(\"queue\", $event.index)\n }\n },\n scopedSlots: _vm._u([\n {\n key: \"header\",\n fn: function() {\n return [\n _c(\"p\", { staticClass: \"menu-label\" }, [\n _c(\"span\", { staticClass: \"icon\" }, [\n _c(\"span\", { staticClass: \"fa fa-list\" })\n ]),\n _vm._v(\"\\n Playlist\\n \")\n ])\n ]\n },\n proxy: true\n }\n ])\n }),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"player-bar media\" }, [\n _c(\n \"div\",\n { staticClass: \"media-left\" },\n [\n _c(\n \"div\",\n {\n staticClass: \"button\",\n attrs: {\n title: _vm.buttonTitle,\n \"aria-label\": _vm.buttonTitle\n },\n on: {\n click: function($event) {\n return _vm.togglePlay()\n }\n }\n },\n [\n _vm.playing\n ? _c(\"span\", { staticClass: \"fas fa-pause\" })\n : _c(\"span\", { staticClass: \"fas fa-play\" })\n ]\n ),\n _vm._v(\" \"),\n _vm._t(\"sources\")\n ],\n 2\n ),\n _vm._v(\" \"),\n _vm.current && _vm.current.cover\n ? _c(\"div\", { staticClass: \"media-left media-cover\" }, [\n _c(\"img\", {\n staticClass: \"cover\",\n attrs: { src: _vm.current.cover }\n })\n ])\n : _vm._e(),\n _vm._v(\" \"),\n _c(\n \"div\",\n { staticClass: \"media-content\" },\n [\n _vm._t(\"content\", null, { current: _vm.current }),\n _vm._v(\" \"),\n this.duration\n ? _c(\"Progress\", {\n attrs: {\n value: this.currentTime,\n max: this.duration,\n format: _vm.displayTime\n },\n on: {\n select: function($event) {\n _vm.audio.currentTime = $event\n }\n }\n })\n : _vm._e()\n ],\n 2\n ),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"media-right\" }, [\n _vm.loaded\n ? _c(\n \"button\",\n {\n staticClass: \"button has-text-weight-bold\",\n on: {\n click: function($event) {\n return _vm.playLive()\n }\n }\n },\n [_vm._m(0), _vm._v(\" \"), _c(\"span\", [_vm._v(\"Live\")])]\n )\n : _vm._e(),\n _vm._v(\" \"),\n _c(\n \"button\",\n {\n class: _vm.playlistButtonClass(\"history\"),\n on: {\n click: function($event) {\n return _vm.togglePanel(\"history\")\n }\n }\n },\n [\n _vm.sets.history.length\n ? _c(\"span\", { staticClass: \"mr-2 is-size-6\" }, [\n _vm._v(\n \"\\n \" + _vm._s(_vm.sets.history.length)\n )\n ])\n : _vm._e(),\n _vm._v(\" \"),\n _vm._m(1)\n ]\n ),\n _vm._v(\" \"),\n _c(\n \"button\",\n {\n class: _vm.playlistButtonClass(\"pin\"),\n on: {\n click: function($event) {\n return _vm.togglePanel(\"pin\")\n }\n }\n },\n [\n _vm.sets.pin.length\n ? _c(\"span\", { staticClass: \"mr-2 is-size-6\" }, [\n _vm._v(\n \"\\n \" + _vm._s(_vm.sets.pin.length)\n )\n ])\n : _vm._e(),\n _vm._v(\" \"),\n _vm._m(2)\n ]\n ),\n _vm._v(\" \"),\n _c(\n \"button\",\n {\n class: _vm.playlistButtonClass(\"queue\"),\n on: {\n click: function($event) {\n return _vm.togglePanel(\"queue\")\n }\n }\n },\n [\n _vm.sets.queue.length\n ? _c(\"span\", { staticClass: \"mr-2 is-size-6\" }, [\n _vm._v(\n \"\\n \" + _vm._s(_vm.sets.queue.length)\n )\n ])\n : _vm._e(),\n _vm._v(\" \"),\n _vm._m(3)\n ]\n )\n ])\n ])\n ],\n 1\n )\n}\nvar staticRenderFns = [\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"span\", { staticClass: \"icon has-text-danger\" }, [\n _c(\"span\", { staticClass: \"fa fa-broadcast-tower\" })\n ])\n },\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"span\", { staticClass: \"icon\" }, [\n _c(\"span\", { staticClass: \"fa fa-clock\" })\n ])\n },\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"span\", { staticClass: \"icon\" }, [\n _c(\"span\", { staticClass: \"fa fa-thumbtack\" })\n ])\n },\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"span\", { staticClass: \"icon\" }, [\n _c(\"span\", { staticClass: \"fa fa-list\" })\n ])\n }\n]\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack:///./assets/public/player.vue?./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options");
+
+/***/ }),
+
+/***/ "./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/vue-loader/lib/index.js?!./assets/public/playerProgress.vue?vue&type=template&id=a8a834d4&":
+/*!*******************************************************************************************************************************************************************************************************!*\
+ !*** ./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options!./assets/public/playerProgress.vue?vue&type=template&id=a8a834d4& ***!
+ \*******************************************************************************************************************************************************************************************************/
+/*! exports provided: render, staticRenderFns */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"div\", { staticClass: \"media\" }, [\n _c(\n \"div\",\n { staticClass: \"media-left\" },\n [\n _vm._t(\"value\", [_vm._v(_vm._s(_vm.format(_vm.value)))], {\n value: _vm.value,\n max: _vm.max\n })\n ],\n 2\n ),\n _vm._v(\" \"),\n _c(\n \"div\",\n {\n ref: \"bar\",\n staticClass: \"media-content\",\n on: {\n click: function($event) {\n $event.stopPropagation()\n return _vm.onClick($event)\n }\n }\n },\n [\n _c(\"div\", { class: _vm.progressClass, style: _vm.progressStyle }, [\n _vm._v(\" \")\n ])\n ]\n ),\n _vm._v(\" \"),\n _c(\n \"div\",\n { staticClass: \"media-right\" },\n [\n _vm._t(\"value\", [_vm._v(_vm._s(_vm.format(_vm.max)))], {\n value: _vm.value,\n max: _vm.max\n })\n ],\n 2\n )\n ])\n}\nvar staticRenderFns = []\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack:///./assets/public/playerProgress.vue?./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options");
/***/ }),
diff --git a/assets/public/list.vue b/assets/public/list.vue
index 9a446fe..5d42b76 100644
--- a/assets/public/list.vue
+++ b/assets/public/list.vue
@@ -58,7 +58,7 @@ export default {
select(index) {
this.selectedIndex = index > -1 && this.items.length ? index % this.items.length : -1;
- this.$emit('select', { target: this, item: this.selected, index: this.selectedIndex });
+ this.$emit('select', { item: this.selected, index: this.selectedIndex });
return this.selectedIndex;
},
diff --git a/assets/public/player.vue b/assets/public/player.vue
index 8eeb916..53fb33c 100644
--- a/assets/public/player.vue
+++ b/assets/public/player.vue
@@ -1,6 +1,6 @@
-
-
@@ -50,6 +47,9 @@
-
-
-
@@ -89,6 +86,7 @@
import Vue, { ref } from 'vue';
import Live from './live';
import Playlist from './playlist';
+import Progress from './playerProgress';
import Sound from './sound';
import {Set} from './model';
@@ -101,12 +99,24 @@ export const State = {
export default {
data() {
+ let audio = new Audio();
+ audio.addEventListener('ended', e => this.onState(e));
+ audio.addEventListener('pause', e => this.onState(e));
+ audio.addEventListener('playing', e => this.onState(e));
+ audio.addEventListener('timeupdate', e => {
+ this.currentTime = this.audio.currentTime;
+ });
+ audio.addEventListener('durationchange', e => {
+ this.duration = Number.isFinite(this.audio.duration) ? this.audio.duration : null;
+ });
+
return {
- state: State.paused,
- /// Loaded item
- loaded: null,
+ audio, duration: 0, currentTime: 0,
+
/// Live instance
live: this.liveArgs ? new Live(this.liveArgs) : null,
+ /// Loaded item
+ loaded: null,
//! Active panel name
panel: null,
//! current playing playlist component
@@ -126,21 +136,15 @@ export default {
},
computed: {
+ self() { return this; },
paused() { return this.state == State.paused; },
playing() { return this.state == State.playing; },
loading() { return this.state == State.loading; },
- self() { return this; },
current() {
return this.loaded || this.live && this.live.current;
},
- progress() {
- let audio = this.$refs.audio;
- return audio && Number.isFinite(audio.duration) && audio.duration ?
- audio.pos / audio.duration * 100 : null;
- },
-
buttonStyle() {
if(!this.current)
return;
@@ -149,6 +153,15 @@ export default {
},
methods: {
+ displayTime(seconds) {
+ seconds = parseInt(seconds);
+ let s = seconds % 60;
+ seconds = (seconds - s) / 60;
+ let m = seconds % 60;
+ let h = (seconds - m) / 60;
+ return h ? `${h}:${m}:${s}` : `${m}:${s}`;
+ },
+
playlistButtonClass(name) {
let set = this.sets[name];
return (set ? (set.length ? "" : "has-text-grey-light ")
@@ -178,7 +191,7 @@ export default {
this.loaded = item;
this.playlist = playlist ? this.$refs[playlist] : null;
- const audio = this.$refs.audio;
+ const audio = this.audio;
if(src instanceof Array) {
audio.innerHTML = '';
for(var s of src) {
@@ -195,14 +208,18 @@ export default {
/// 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: item});
this.sets.history.push(item);
- this.$refs.audio.play().catch(e => console.error(e))
+ this.audio.play().catch(e => console.error(e))
}
else
throw `No sound at index ${index} for playlist ${playlist}`;
@@ -225,19 +242,19 @@ export default {
/// Play live stream
playLive() {
this.load(null, {src: this.live.src});
- this.$refs.audio.play().catch(e => console.error(e))
+ this.audio.play().catch(e => console.error(e))
this.panel = '';
},
/// Pause
pause() {
- this.$refs.audio.pause()
+ this.audio.pause()
},
//! Play/pause
togglePlay() {
if(this.paused)
- this.$refs.audio.play().catch(e => console.error(e))
+ this.audio.play().catch(e => console.error(e))
else
this.pause()
},
@@ -256,7 +273,7 @@ export default {
/// Audio player state change event
onState(event) {
- const audio = this.$refs.audio;
+ const audio = this.audio;
this.state = audio.paused ? State.paused : State.playing;
if(event.type == 'ended' && (!this.playlist || this.playlist.selectNext() == -1))
@@ -268,7 +285,7 @@ export default {
this.sources = this.$slots.sources;
},
- components: { Playlist },
+ components: { Playlist, Progress },
}
diff --git a/assets/public/playerProgress.vue b/assets/public/playerProgress.vue
new file mode 100644
index 0000000..d4039d5
--- /dev/null
+++ b/assets/public/playerProgress.vue
@@ -0,0 +1,46 @@
+
+
+
+
+
+
diff --git a/assets/public/styles.scss b/assets/public/styles.scss
index 467c3ae..be552b2 100644
--- a/assets/public/styles.scss
+++ b/assets/public/styles.scss
@@ -181,11 +181,11 @@ a.navbar-item.is-active {
.player-bar {
border-top: 1px $grey-light solid;
- .media-left:not(:last-child) {
+ > .media-left:not(:last-child) {
margin-right: 0em;
}
- .media-cover {
+ > .media-cover {
border-left: 1px black solid;
}
@@ -194,7 +194,7 @@ a.navbar-item.is-active {
height: 2.5em !important;
}
- .media-content {
+ > .media-content {
padding-top: 0.4em;
padding-left: 0.4em;
}