Newer
Older
Eliot Berriot
committed
<template>
<div class="player">
<div v-if="queue.currentTrack" class="track-area ui items">
<div class="ui inverted item">
<div class="ui tiny image">
<img v-if="queue.currentTrack.album.cover" :src="Track.getCover(queue.currentTrack)">
<img v-else src="../../assets/audio/default-cover.png">
</div>
<div class="middle aligned content">
<router-link class="small header discrete link track" :to="{name: 'library.tracks.detail', params: {id: queue.currentTrack.id }}">
Eliot Berriot
committed
{{ queue.currentTrack.title }}
</router-link>
<div class="meta">
<router-link class="artist" :to="{name: 'library.artists.detail', params: {id: queue.currentTrack.artist.id }}">
Eliot Berriot
committed
{{ queue.currentTrack.artist.name }}
</router-link> /
<router-link class="album" :to="{name: 'library.albums.detail', params: {id: queue.currentTrack.album.id }}">
Eliot Berriot
committed
{{ queue.currentTrack.album.title }}
</router-link>
</div>
<div class="description">
<track-favorite-icon :track="queue.currentTrack"></track-favorite-icon>
</div>
</div>
</div>
</div>
<div class="progress-area" v-if="queue.currentTrack">
Eliot Berriot
committed
<div class="ui grid">
<div class="left floated four wide column">
<p class="timer start" @click="queue.audio.setTime(0)">{{queue.audio.state.currentTimeFormat}}</p>
</div>
<div class="right floated four wide column">
<p class="timer total">{{queue.audio.state.durationTimerFormat}}</p>
</div>
</div>
<div ref="progress" class="ui small orange inverted progress" @click="touchProgress">
<div class="bar" :data-percent="queue.audio.state.progress" :style="{ 'width': queue.audio.state.progress + '%' }"></div>
</div>
</div>
<div class="two wide column controls ui grid">
<div
@click="queue.previous()"
title="Previous track"
class="two wide column control"
:disabled="!hasPrevious">
<i :class="['ui', {'disabled': !hasPrevious}, 'step', 'backward', 'big', 'icon']" ></i>
</div>
<div
v-if="!queue.audio.state.playing"
@click="pauseOrPlay"
title="Play track"
class="two wide column control">
<i :class="['ui', 'play', {'disabled': !queue.currentTrack}, 'big', 'icon']"></i>
</div>
<div
v-else
@click="pauseOrPlay"
title="Pause track"
class="two wide column control">
<i :class="['ui', 'pause', {'disabled': !queue.currentTrack}, 'big', 'icon']"></i>
</div>
<div
@click="queue.next()"
title="Next track"
class="two wide column control"
:disabled="!hasNext">
<i :class="['ui', {'disabled': !hasPrevious}, 'step', 'forward', 'big', 'icon']" ></i>
</div>
<div class="two wide column control volume-control">
Eliot Berriot
committed
<i title="Unmute" @click="queue.setVolume(1)" v-if="currentVolume === 0" class="volume off secondary icon"></i>
<i title="Mute" @click="queue.setVolume(0)" v-else-if="currentVolume < 0.5" class="volume down secondary icon"></i>
<i title="Mute" @click="queue.setVolume(0)" v-else class="volume up secondary icon"></i>
<input type="range" step="0.05" min="0" max="1" v-model="sliderVolume" />
Eliot Berriot
committed
</div>
<div class="two wide column control looping">
<i
title="Looping disabled. Click to switch to single-track looping."
v-if="queue.state.looping === 0"
@click="queue.state.looping = 1"
:disabled="!queue.currentTrack"
:class="['ui', {'disabled': !queue.currentTrack}, 'step', 'repeat', 'secondary', 'icon']"></i>
<i
title="Looping on a single track. Click to switch to whole queue looping."
v-if="queue.state.looping === 1"
@click="queue.state.looping = 2"
:disabled="!queue.currentTrack"
class="repeat secondary icon">
<span class="ui circular tiny orange label">1</span>
</i>
<i
title="Looping on whole queue. Click to disable looping."
v-if="queue.state.looping === 2"
@click="queue.state.looping = 0"
:disabled="!queue.currentTrack"
class="repeat orange secondary icon">
</i>
Eliot Berriot
committed
</div>
<div
@click="queue.shuffle()"
:disabled="queue.tracks.length === 0"
title="Shuffle your queue"
class="two wide column control">
<i :class="['ui', 'random', 'secondary', {'disabled': queue.tracks.length === 0}, 'icon']" ></i>
</div>
<div class="one wide column"></div>
<div
@click="queue.clean()"
:disabled="queue.tracks.length === 0"
title="Clear your queue"
class="two wide column control">
<i :class="['ui', 'trash', 'secondary', {'disabled': queue.tracks.length === 0}, 'icon']" ></i>
Eliot Berriot
committed
</div>
</div>
Eliot Berriot
committed
@keydown.space.prevent.exact="pauseOrPlay"
@keydown.ctrl.left.prevent.exact="queue.previous"
@keydown.ctrl.right.prevent.exact="queue.next"
@keydown.ctrl.down.prevent.exact="queue.incrementVolume(-0.1)"
@keydown.ctrl.up.prevent.exact="queue.incrementVolume(0.1)"
@keydown.f.prevent.exact="favoriteTracks.toggle(queue.currentTrack.id)"
@keydown.s.prevent.exact="queue.shuffle"
Eliot Berriot
committed
</div>
</template>
<script>
import GlobalEvents from '@/components/utils/global-events'
Eliot Berriot
committed
import favoriteTracks from '@/favorites/tracks'
Eliot Berriot
committed
import queue from '@/audio/queue'
Eliot Berriot
committed
import radios from '@/radios'
Eliot Berriot
committed
import Track from '@/audio/track'
import TrackFavoriteIcon from '@/components/favorites/TrackFavoriteIcon'
export default {
name: 'player',
components: {
TrackFavoriteIcon,
GlobalEvents
Eliot Berriot
committed
},
data () {
return {
sliderVolume: this.currentVolume,
queue: queue,
Track: Track,
Eliot Berriot
committed
favoriteTracks,
Eliot Berriot
committed
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
radios
}
},
mounted () {
// we trigger the watcher explicitely it does not work otherwise
this.sliderVolume = this.currentVolume
},
methods: {
pauseOrPlay () {
if (this.queue.audio.state.playing) {
this.queue.audio.pause()
} else {
this.queue.audio.play()
}
},
touchProgress (e) {
let time
let target = this.$refs.progress
time = e.layerX / target.offsetWidth * this.queue.audio.state.duration
this.queue.audio.setTime(time)
}
},
computed: {
hasPrevious () {
return this.queue.currentIndex > 0
},
hasNext () {
return this.queue.currentIndex < this.queue.tracks.length - 1
},
currentVolume () {
return this.queue.audio.state.volume
}
},
watch: {
currentVolume (newValue) {
this.sliderVolume = newValue
},
sliderVolume (newValue) {
this.queue.setVolume(parseFloat(newValue))
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
.ui.progress {
margin: 0.5rem 0 1rem;
}
.progress {
cursor: pointer;
.bar {
min-width: 0 !important;
}
}
.ui.inverted.item > .content > .description {
color: rgba(255, 255, 255, 0.9) !important;
}
.ui.item {
.meta {
font-size: 90%;
line-height: 1.2
}
}
.timer.total {
text-align: right;
}
.timer.start {
cursor: pointer
}
.track-area {
.header, .meta, .artist, .album {
color: white !important;
}
}
.controls .icon.big {
cursor: pointer;
font-size: 2em !important;
}
.controls .icon {
cursor: pointer;
vertical-align: middle;
}
.secondary.icon {
font-size: 1.5em;
}
.progress-area .actions {
text-align: center;
}
.volume-control {
position: relative;
.icon {
Eliot Berriot
committed
}
[type="range"] {
Eliot Berriot
committed
position: absolute;
bottom: 5px;
left: 10%;
cursor: pointer;
display: none;
}
&:hover {
[type="range"] {
display: block;
}
}
}
.looping.control {
i {
position: relative;
}
.label {
position: absolute;
font-size: 0.7rem;
bottom: -0.7rem;
right: -0.7rem;
Eliot Berriot
committed
}
}
.ui.feed.icon {
margin: 0;
}
</style>