work on player
This commit is contained in:
parent
a0a1c92cfe
commit
222300945e
|
@ -7411,10 +7411,20 @@ a.navbar-item.is-active {
|
|||
|
||||
.player {
|
||||
box-shadow: 0em 1.5em 2.5em rgba(0, 0, 0, 0.6); }
|
||||
.player .player-panels {
|
||||
height: 0%;
|
||||
transition: height 3s; }
|
||||
.player .player-panels.is-open {
|
||||
height: auto; }
|
||||
.player .player-panel {
|
||||
margin: 0.4em;
|
||||
max-height: 80%;
|
||||
overflow-y: auto; }
|
||||
.player .progress {
|
||||
margin: 0em;
|
||||
padding: 0em;
|
||||
border-color: #209cee;
|
||||
border-style: 'solid'; }
|
||||
.player .player-bar {
|
||||
border-top: 1px #b5b5b5 solid; }
|
||||
.player .player-bar > .media-left:not(:last-child) {
|
||||
|
@ -7431,7 +7441,10 @@ a.navbar-item.is-active {
|
|||
font-size: 1.5rem !important;
|
||||
height: 2.5em;
|
||||
min-width: 2.5em;
|
||||
border-radius: 0px; }
|
||||
border-radius: 0px;
|
||||
transition: background-color 1s; }
|
||||
.player .player-bar .button:focus {
|
||||
background-color: #209cee; }
|
||||
.player .player-bar .title {
|
||||
margin: 0em; }
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -7390,10 +7390,20 @@ a.navbar-item.is-active {
|
|||
|
||||
.player {
|
||||
box-shadow: 0em 1.5em 2.5em rgba(0, 0, 0, 0.6); }
|
||||
.player .player-panels {
|
||||
height: 0%;
|
||||
transition: height 3s; }
|
||||
.player .player-panels.is-open {
|
||||
height: auto; }
|
||||
.player .player-panel {
|
||||
margin: 0.4em;
|
||||
max-height: 80%;
|
||||
overflow-y: auto; }
|
||||
.player .progress {
|
||||
margin: 0em;
|
||||
padding: 0em;
|
||||
border-color: #209cee;
|
||||
border-style: 'solid'; }
|
||||
.player .player-bar {
|
||||
border-top: 1px #b5b5b5 solid; }
|
||||
.player .player-bar > .media-left:not(:last-child) {
|
||||
|
@ -7410,7 +7420,10 @@ a.navbar-item.is-active {
|
|||
font-size: 1.5rem !important;
|
||||
height: 2.5em;
|
||||
min-width: 2.5em;
|
||||
border-radius: 0px; }
|
||||
border-radius: 0px;
|
||||
transition: background-color 1s; }
|
||||
.player .player-bar .button:focus {
|
||||
background-color: #209cee; }
|
||||
.player .player-bar .title {
|
||||
margin: 0em; }
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -39,11 +39,10 @@ export default {
|
|||
|
||||
methods: {
|
||||
get(index) { return this.set.get(index) },
|
||||
find(item) { return this.set.find(item) },
|
||||
findIndex(item) { return this.set.findIndex(item) },
|
||||
find(pred) { return this.set.find(pred) },
|
||||
findIndex(pred) { return this.set.findIndex(pred) },
|
||||
|
||||
push(...items) {
|
||||
let index = this.set.length;
|
||||
for(var item of items)
|
||||
this.set.push(item);
|
||||
},
|
||||
|
@ -62,7 +61,10 @@ export default {
|
|||
return this.selectedIndex;
|
||||
},
|
||||
|
||||
|
||||
unselect() {
|
||||
this.$emit('unselect', { item: this.selected, index: this.selectedIndex});
|
||||
this.selectedIndex = -1;
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -119,7 +119,6 @@ export class Set {
|
|||
}
|
||||
|
||||
get length() { return this.items.length }
|
||||
get(index) { return this.items[index] }
|
||||
|
||||
/**
|
||||
* Fetch multiple items from server
|
||||
|
@ -156,17 +155,24 @@ export class Set {
|
|||
}
|
||||
|
||||
/**
|
||||
* Find item by id
|
||||
* Get item at index
|
||||
*/
|
||||
find(item) {
|
||||
return this.items.find(x => x.id == item.id);
|
||||
get(index) { return this.items[index] }
|
||||
|
||||
/**
|
||||
* Find an item by id or using a predicate function
|
||||
*/
|
||||
find(pred) {
|
||||
return pred instanceof Function ? this.items.find(pred)
|
||||
: this.items.find(x => x.id == pred.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find item index by id
|
||||
* Find item index by id or using a predicate function
|
||||
*/
|
||||
findIndex(item) {
|
||||
return this.items.findIndex(x => x.id == item.id);
|
||||
findIndex(pred) {
|
||||
return pred instanceof Function ? this.items.findIndex(pred)
|
||||
: this.items.findIndex(x => x.id == pred.id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -177,7 +183,7 @@ export class Set {
|
|||
if(this.unique) {
|
||||
let index = this.findIndex(item);
|
||||
if(index > -1)
|
||||
this.items.splice(index,1);
|
||||
return;
|
||||
}
|
||||
if(this.max && this.items.length >= this.max)
|
||||
this.items.splice(0,this.items.length-this.max)
|
||||
|
|
|
@ -1,37 +1,28 @@
|
|||
<template>
|
||||
<div class="player">
|
||||
<Playlist ref="history" class="player-panel menu" v-show="panel == 'history'"
|
||||
name="History"
|
||||
:editable="true" :player="self" :set="sets.history" @select="play('pin', $event.index)"
|
||||
listClass="menu-list" itemClass="menu-item">
|
||||
<template v-slot:header="">
|
||||
<p class="menu-label">
|
||||
<span class="icon"><span class="fa fa-clock"></span></span>
|
||||
History
|
||||
</p>
|
||||
</template>
|
||||
</Playlist>
|
||||
<Playlist ref="pin" class="player-panel menu" v-show="panel == 'pin'"
|
||||
name="Pinned"
|
||||
:editable="true" :player="self" :set="sets.pin" @select="play('pin', $event.index)"
|
||||
listClass="menu-list" itemClass="menu-item">
|
||||
<template v-slot:header="">
|
||||
<p class="menu-label">
|
||||
<span class="icon"><span class="fa fa-thumbtack"></span></span>
|
||||
Pinned
|
||||
</p>
|
||||
</template>
|
||||
</Playlist>
|
||||
<Playlist ref="queue" class="player-panel menu" v-show="panel == 'queue'"
|
||||
:editable="true" :player="self" :set="sets.queue" @select="play('queue', $event.index)"
|
||||
listClass="menu-list" itemClass="menu-item">
|
||||
<template v-slot:header="">
|
||||
<p class="menu-label">
|
||||
<span class="icon"><span class="fa fa-list"></span></span>
|
||||
Playlist
|
||||
</p>
|
||||
</template>
|
||||
</Playlist>
|
||||
<div :class="['player-panels', panel ? 'is-open' : '']">
|
||||
<Playlist ref="pin" class="player-panel menu" v-show="panel == 'pin'"
|
||||
name="Pinned"
|
||||
:editable="true" :player="self" :set="sets.pin" @select="play('pin', $event.index)"
|
||||
listClass="menu-list" itemClass="menu-item">
|
||||
<template v-slot:header="">
|
||||
<p class="menu-label">
|
||||
<span class="icon"><span class="fa fa-thumbtack"></span></span>
|
||||
Pinned
|
||||
</p>
|
||||
</template>
|
||||
</Playlist>
|
||||
<Playlist ref="queue" class="player-panel menu" v-show="panel == 'queue'"
|
||||
:editable="true" :player="self" :set="sets.queue" @select="play('queue', $event.index)"
|
||||
listClass="menu-list" itemClass="menu-item">
|
||||
<template v-slot:header="">
|
||||
<p class="menu-label">
|
||||
<span class="icon"><span class="fa fa-list"></span></span>
|
||||
Playlist
|
||||
</p>
|
||||
</template>
|
||||
</Playlist>
|
||||
</div>
|
||||
|
||||
<div class="player-bar media">
|
||||
<div class="media-left">
|
||||
|
@ -47,24 +38,18 @@
|
|||
</div>
|
||||
<div class="media-content">
|
||||
<slot name="content" :current="current"></slot>
|
||||
<Progress v-if="this.duration" :value="this.currentTime" :max="this.duration"
|
||||
<Progress v-if="loaded && duration" :value="currentTime" :max="this.duration"
|
||||
:format="displayTime"
|
||||
@select="audio.currentTime = $event"></Progress>
|
||||
</div>
|
||||
<div class="media-right">
|
||||
<button class="button has-text-weight-bold" v-if="loaded" @click="playLive()">
|
||||
<span class="icon has-text-danger">
|
||||
<span class="fa fa-broadcast-tower"></span>
|
||||
<span class="icon is-size-6 has-text-danger">
|
||||
<span class="fa fa-circle"></span>
|
||||
</span>
|
||||
<span>Live</span>
|
||||
</button>
|
||||
<button :class="playlistButtonClass('history')"
|
||||
@click="togglePanel('history')">
|
||||
<span class="mr-2 is-size-6" v-if="sets.history.length">
|
||||
{{ sets.history.length }}</span>
|
||||
<span class="icon"><span class="fa fa-clock"></span></span>
|
||||
</button>
|
||||
<button :class="playlistButtonClass('pin')"
|
||||
<button ref="pinPlaylistButton" :class="playlistButtonClass('pin')"
|
||||
@click="togglePanel('pin')">
|
||||
<span class="mr-2 is-size-6" v-if="sets.pin.length">
|
||||
{{ sets.pin.length }}</span>
|
||||
|
@ -86,7 +71,7 @@
|
|||
import Vue, { ref } from 'vue';
|
||||
import Live from './live';
|
||||
import Playlist from './playlist';
|
||||
import Progress from './playerProgress';
|
||||
import Progress from './progress';
|
||||
import Sound from './sound';
|
||||
import {Set} from './model';
|
||||
|
||||
|
@ -111,7 +96,7 @@ export default {
|
|||
});
|
||||
|
||||
return {
|
||||
audio, duration: 0, currentTime: 0,
|
||||
audio, duration: 0, currentTime: 0, state: State.paused,
|
||||
|
||||
/// Live instance
|
||||
live: this.liveArgs ? new Live(this.liveArgs) : null,
|
||||
|
@ -125,7 +110,6 @@ export default {
|
|||
sets: {
|
||||
queue: Set.storeLoad(Sound, "playlist.queue", { max: 30, unique: true }),
|
||||
pin: Set.storeLoad(Sound, "player.pin", { max: 30, unique: true }),
|
||||
history: Set.storeLoad(Sound, "player.history", { max: 30, unique: true }),
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -159,7 +143,11 @@ export default {
|
|||
seconds = (seconds - s) / 60;
|
||||
let m = seconds % 60;
|
||||
let h = (seconds - m) / 60;
|
||||
return h ? `${h}:${m}:${s}` : `${m}:${s}`;
|
||||
|
||||
let [ss,mm,hh] = [s.toString().padStart(2, '0'),
|
||||
m.toString().padStart(2, '0'),
|
||||
h.toString().padStart(2, '0')];
|
||||
return h ? `${hh}:${mm}:${ss}` : `${mm}:${ss}`;
|
||||
},
|
||||
|
||||
playlistButtonClass(name) {
|
||||
|
@ -172,24 +160,23 @@ export default {
|
|||
},
|
||||
|
||||
/// Show/hide panel
|
||||
togglePanel(panel) {
|
||||
this.panel = this.panel == panel ? null : panel;
|
||||
},
|
||||
|
||||
togglePanel(panel) { this.panel = this.panel == panel ? null : panel },
|
||||
/// Return True if item is loaded
|
||||
isLoaded(item) {
|
||||
return this.loaded && this.loaded.src == item.src;
|
||||
},
|
||||
|
||||
isLoaded(item) { return this.loaded && this.loaded.id == item.id },
|
||||
/// Return True if item is loaded
|
||||
isPlaying(item) {
|
||||
return this.isLoaded(item) && !this.player.paused;
|
||||
isPlaying(item) { return this.isLoaded(item) && !this.paused },
|
||||
|
||||
_setPlaylist(playlist) {
|
||||
this.playlist = playlist ? this.$refs[playlist] : null;
|
||||
for(var p in this.sets)
|
||||
if(p != playlist)
|
||||
this.$refs[p].unselect();
|
||||
},
|
||||
|
||||
load(playlist, {src=null, item=null}={}) {
|
||||
src = src || item.src;
|
||||
this.loaded = item;
|
||||
this.playlist = playlist ? this.$refs[playlist] : null;
|
||||
this._setPlaylist(playlist);
|
||||
|
||||
const audio = this.audio;
|
||||
if(src instanceof Array) {
|
||||
|
@ -217,8 +204,7 @@ export default {
|
|||
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.load(playlist, {item});
|
||||
this.audio.play().catch(e => console.error(e))
|
||||
}
|
||||
else
|
||||
|
@ -266,8 +252,7 @@ export default {
|
|||
this.sets.pin.remove(index);
|
||||
else {
|
||||
this.sets.pin.push(item);
|
||||
if(!this.panel)
|
||||
this.panel = 'pin';
|
||||
this.$refs.pinPlaylistButton.focus();
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
<template>
|
||||
<div class="media">
|
||||
<div class="media-left">
|
||||
<slot name="value" :value="value" :max="max">{{ format(value) }}</slot>
|
||||
</div>
|
||||
<div ref="bar" class="media-content" @click.stop="onClick">
|
||||
<div :class="progressClass" :style="progressStyle"> </div>
|
||||
</div>
|
||||
<div class="media-right">
|
||||
<slot name="value" :value="value" :max="max">{{ format(max) }}</slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: Number,
|
||||
max: Number,
|
||||
format: { type: Function, default: x => x },
|
||||
progressClass: { default: 'has-background-primary' },
|
||||
vertical: { type: Boolean, default: false },
|
||||
},
|
||||
|
||||
computed: {
|
||||
progressStyle() {
|
||||
if(!this.max)
|
||||
return null;
|
||||
return this.vertical ? { height: (this.max ? this.value * 100 / this.max : 0) + '%' }
|
||||
: { width: (this.max ? this.value * 100 / this.max : 0) + '%' };
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
xToValue(x) { return x * this.max / this.$refs.bar.getBoundingClientRect().width },
|
||||
yToValue(y) { return y * this.max / this.$refs.bar.getBoundingClientRect().height },
|
||||
|
||||
onClick(event) {
|
||||
let rect = event.currentTarget.getBoundingClientRect()
|
||||
this.$emit('select', this.vertical ? this.yToValue(event.clientY - rect.y)
|
||||
: this.xToValue(event.clientX - rect.x));
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
67
assets/public/progress.vue
Normal file
67
assets/public/progress.vue
Normal file
|
@ -0,0 +1,67 @@
|
|||
<template>
|
||||
<div class="media">
|
||||
<div class="media-left">
|
||||
<slot name="value" :value="valueDisplay" :max="max">{{ format(valueDisplay) }}</slot>
|
||||
</div>
|
||||
<div ref="bar" class="media-content" @click.stop="onClick" @mouseleave.stop="onMouseMove"
|
||||
@mousemove.stop="onMouseMove">
|
||||
<div :class="progressClass" :style="progressStyle"> </div>
|
||||
</div>
|
||||
<div class="media-right">
|
||||
<slot name="value" :value="valueDisplay" :max="max">{{ format(max) }}</slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
hoverValue: null,
|
||||
}
|
||||
},
|
||||
|
||||
props: {
|
||||
value: Number,
|
||||
max: Number,
|
||||
format: { type: Function, default: x => x },
|
||||
progressClass: { default: 'has-background-primary' },
|
||||
vertical: { type: Boolean, default: false },
|
||||
},
|
||||
|
||||
computed: {
|
||||
valueDisplay() { return this.hoverValue === null ? this.value : this.hoverValue; },
|
||||
|
||||
progressStyle() {
|
||||
if(!this.max)
|
||||
return null;
|
||||
let value = this.max ? this.valueDisplay * 100 / this.max : 0;
|
||||
return this.vertical ? { height: `${value}%` } : { width: `${value}%` };
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
xToValue(x) { return x * this.max / this.$refs.bar.getBoundingClientRect().width },
|
||||
yToValue(y) { return y * this.max / this.$refs.bar.getBoundingClientRect().height },
|
||||
|
||||
valueFromEvent(event) {
|
||||
let rect = event.currentTarget.getBoundingClientRect()
|
||||
return this.vertical ? this.yToValue(event.clientY - rect.y)
|
||||
: this.xToValue(event.clientX - rect.x);
|
||||
},
|
||||
|
||||
onClick(event) {
|
||||
this.$emit('select', this.valueFromEvent(event));
|
||||
},
|
||||
|
||||
onMouseMove(event) {
|
||||
if(event.type == 'mouseleave')
|
||||
this.hoverValue = null;
|
||||
else {
|
||||
this.hoverValue = this.valueFromEvent(event);
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
<div class="media-left" v-if="hasAction('play')">
|
||||
<button class="button" @click="$emit('togglePlay')">
|
||||
<div class="icon">
|
||||
<span class="fa fa-pause" v-if="playing || loading"></span>
|
||||
<span class="fa fa-pause" v-if="playing"></span>
|
||||
<span class="fa fa-play" v-else></span>
|
||||
</div>
|
||||
</button>
|
||||
|
@ -16,7 +16,7 @@
|
|||
</slot>
|
||||
</div>
|
||||
<div class="media-right">
|
||||
<button class="button" v-if="player.$refs.pin != $parent" @click.stop="player.togglePin(item)">
|
||||
<button class="button" v-if="player.sets.pin != $parent.set" @click.stop="player.togglePin(item)">
|
||||
<span class="icon is-small">
|
||||
<span :class="(pinned ? '' : 'has-text-grey-light ') + 'fa fa-thumbtack'"></span>
|
||||
</span>
|
||||
|
@ -43,9 +43,8 @@ export default {
|
|||
computed: {
|
||||
item() { return this.data instanceof Model ? this.data : new Sound(this.data || {}); },
|
||||
loaded() { return this.player && this.player.isLoaded(this.item) },
|
||||
playing() { return this.player && this.player.playing && this.loaded },
|
||||
playing() { return this.player && this.player.isPlaying(this.item) },
|
||||
paused() { return this.player && this.player.paused && this.loaded },
|
||||
loading() { return this.player && this.player.loading && this.loaded },
|
||||
pinned() { return this.player && this.player.sets.pin.find(this.item) },
|
||||
},
|
||||
|
||||
|
|
|
@ -172,12 +172,27 @@ a.navbar-item.is-active {
|
|||
.player {
|
||||
box-shadow: 0em 1.5em 2.5em rgba(0, 0, 0, 0.6);
|
||||
|
||||
.player-panels {
|
||||
height: 0%;
|
||||
transition: height 3s;
|
||||
}
|
||||
.player-panels.is-open {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.player-panel {
|
||||
margin: 0.4em;
|
||||
max-height: 80%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.progress {
|
||||
margin: 0em;
|
||||
padding: 0em;
|
||||
border-color: $info;
|
||||
border-style: 'solid';
|
||||
}
|
||||
|
||||
.player-bar {
|
||||
border-top: 1px $grey-light solid;
|
||||
|
||||
|
@ -204,6 +219,11 @@ a.navbar-item.is-active {
|
|||
height: 2.5em;
|
||||
min-width: 2.5em;
|
||||
border-radius: 0px;
|
||||
transition: background-color 1s;
|
||||
}
|
||||
|
||||
.button:focus {
|
||||
background-color: $info;
|
||||
}
|
||||
|
||||
.title {
|
||||
|
|
Loading…
Reference in New Issue
Block a user