add lots more features

This commit is contained in:
2023-10-22 14:42:20 +02:00
parent 9b282fd4ab
commit ddd9b789e8
5 changed files with 539 additions and 29 deletions

View File

@@ -29,7 +29,6 @@ app.get( '/openSongs', ( req, res ) => {
app.get( '/indexDirs', ( req, res ) => { app.get( '/indexDirs', ( req, res ) => {
if ( req.query.dir ) { if ( req.query.dir ) {
if ( indexedData[ req.query.dir ] ) { if ( indexedData[ req.query.dir ] ) {
console.log( 'using cache' );
res.send( indexedData[ req.query.dir ] ); res.send( indexedData[ req.query.dir ] );
} else { } else {
fs.readdir( req.query.dir, { encoding: 'utf-8' }, ( err, dat ) => { fs.readdir( req.query.dir, { encoding: 'utf-8' }, ( err, dat ) => {
@@ -48,7 +47,7 @@ app.get( '/indexDirs', ( req, res ) => {
'year': metadata[ 'common' ][ 'year' ], 'year': metadata[ 'common' ][ 'year' ],
'bpm': metadata[ 'common' ][ 'bpm' ], 'bpm': metadata[ 'common' ][ 'bpm' ],
'genre': metadata[ 'common' ][ 'genre' ], 'genre': metadata[ 'common' ][ 'genre' ],
'duration': metadata[ 'format' ][ 'duration' ], 'duration': Math.round( metadata[ 'format' ][ 'duration' ] ),
'isLossless': metadata[ 'format' ][ 'lossless' ], 'isLossless': metadata[ 'format' ][ 'lossless' ],
'sampleRate': metadata[ 'format' ][ 'sampleRate' ], 'sampleRate': metadata[ 'format' ][ 'sampleRate' ],
'bitrate': metadata[ 'format' ][ 'bitrate' ], 'bitrate': metadata[ 'format' ][ 'bitrate' ],

View File

@@ -1,10 +1,18 @@
<template> <template>
<div class="media-pool"> <div class="media-pool">
<div v-if="hasLoadedSongs" style="width: 100%;" class="song-list-wrapper"> <div v-if="hasLoadedSongs" style="width: 100%;" class="song-list-wrapper">
<div v-for="song in songQueue" class="song-list"> <div v-for="song in songQueue" class="song-list" :class="[ isPlaying ? ( currentlyPlaying === song.filename ? 'playing': 'not-playing' ) : 'not-playing', !isPlaying && currentlyPlaying === song.filename ? 'active-song': undefined ]">
<span class="material-symbols-outlined song-image" v-if="!loadCoverArtPreview || !song.hasCoverArt">music_note</span> <span class="material-symbols-outlined song-image" v-if="!loadCoverArtPreview || !song.hasCoverArt">music_note</span>
<img v-else :src="'http://localhost:8081/getSongCover?filename=' + song.filename" class="song-image"> <img v-else :src="'http://localhost:8081/getSongCover?filename=' + song.filename" class="song-image">
<div v-if="currentlyPlaying === song.filename && isPlaying" class="playing-symbols">
<div class="playing-symbols-wrapper">
<div class="playing-bar" id="bar-1"></div>
<div class="playing-bar" id="bar-2"></div>
<div class="playing-bar" id="bar-3"></div>
</div>
</div>
<span class="material-symbols-outlined play-icon" @click="play( song )">play_arrow</span> <span class="material-symbols-outlined play-icon" @click="play( song )">play_arrow</span>
<span class="material-symbols-outlined pause-icon" @click="pause( song )">pause</span>
<h3>{{ song.title }}</h3> <h3>{{ song.title }}</h3>
</div> </div>
</div> </div>
@@ -21,6 +29,61 @@
</template> </template>
<style> <style>
.playing-symbols {
position: absolute;
left: 9.95vw;
display: flex;
justify-content: center;
align-items: center;
flex-direction: row;
width: 5vw;
height: 5vw;
background-color: rgba( 0, 0, 0, 0.6 );
}
.playing-symbols-wrapper {
width: 4vw;
height: 5vw;
display: flex;
justify-content: center;
align-items: center;
flex-direction: row;
}
.playing-bar {
height: 60%;
background-color: var( --primary-color );
width: 10%;
border-radius: 50px;
margin: auto;
}
#bar-1 {
animation: music-playing 0.9s infinite ease-in-out;
}
#bar-2 {
animation: music-playing 0.9s infinite ease-in-out;
animation-delay: 0.3s;
}
#bar-3 {
animation: music-playing 0.9s infinite ease-in-out;
animation-delay: 0.6s;
}
@keyframes music-playing {
0% {
transform: scaleY( 1 );
}
50% {
transform: scaleY( 0.5 );
}
100% {
transform: scaleY( 1 );
}
}
.loading-spinner { .loading-spinner {
animation: spin 2s infinite linear; animation: spin 2s infinite linear;
} }
@@ -84,7 +147,7 @@
font-size: 5vw; font-size: 5vw;
} }
.play-icon { .play-icon, .pause-icon {
display: none; display: none;
width: 5vw; width: 5vw;
height: 5vw; height: 5vw;
@@ -92,15 +155,32 @@
object-position: center; object-position: center;
font-size: 5vw; font-size: 5vw;
cursor: pointer; cursor: pointer;
user-select: none;
}
.playing:hover .pause-icon {
display: block;
}
.playing:hover .playing-symbols {
display: none;
} }
.song-list:hover .song-image { .song-list:hover .song-image {
display: none; display: none;
} }
.song-list:hover .play-icon { .not-playing:hover .play-icon {
display: block; display: block;
} }
.active-song .pause-icon {
display: block;
}
.active-song .song-image, .active-song:hover .pause-icon {
display: none;
}
</style> </style>
<script> <script>
@@ -111,15 +191,72 @@
hasLoadedSongs: false, hasLoadedSongs: false,
isLoadingSongs: false, isLoadingSongs: false,
loadCoverArtPreview: true, loadCoverArtPreview: true,
allSongs: [],
songQueue: [], songQueue: [],
loadedDirs: [], loadedDirs: [],
allowedFiletypes: [ '.mp3', '.wav' ], allowedFiletypes: [ '.mp3', '.wav' ],
currentlyPlaying: '', currentlyPlaying: '',
isPlaying: false,
songPos: 0,
repeat: false,
} }
}, },
methods: { methods: {
getLoadedDirs () { update( status ) {
if ( status.type === 'playback' ) {
this.isPlaying = status.status;
} else if ( status.type === 'next' ) {
if ( this.songPos < this.songQueue.length - 1 ) {
this.songPos += 1;
this.queueHandler( 'load' );
} else {
this.songPos = 0;
if ( this.repeat ) {
this.queueHandler( 'load' );
} else {
this.isPlaying = false;
this.currentlyPlaying = '';
this.$emit( 'com', { 'type': 'startPlayback', 'song': this.songQueue[ 0 ], 'autoplay': false } );
this.$emit( 'com', { 'type': 'pause' } );
}
}
} else if ( status.type === 'previous' ) {
if ( this.songPos > 0 ) {
this.songPos -= 1;
} else {
this.songPos = this.songQueue.length - 1;
}
this.queueHandler( 'load' );
} else if ( status.type === 'shuffle' ) {
this.queueHandler( 'shuffle' );
} else if ( status.type === 'shuffleOff' ) {
this.queueHandler( 'shuffleOff' );
} else if ( status.type === 'repeat' ) {
this.repeat = true;
} else if ( status.type === 'repeatOff' ) {
this.repeat = false;
}
},
queueHandler ( command ) {
if ( command === 'load' ) {
this.play( this.songQueue[ this.songPos ] );
} else if ( command === 'shuffle' ) {
let processArray = JSON.parse( JSON.stringify( this.allSongs ) );
let newOrder = [];
for ( let i = 0; i < this.allSongs.length; i++ ) {
let randNum = Math.floor( Math.random() * this.allSongs.length );
while ( newOrder.includes( randNum ) ) {
randNum = Math.floor( Math.random() * this.allSongs.length );
}
newOrder.push( randNum );
}
this.songQueue = [];
for ( let el in newOrder ) {
this.songQueue.push( processArray[ newOrder[ el ] ] );
}
} else if ( command === 'shuffleOff' ) {
this.songQueue = JSON.parse( JSON.stringify( this.allSongs ) );
}
}, },
loadSongs() { loadSongs() {
this.isLoadingSongs = true; this.isLoadingSongs = true;
@@ -139,17 +276,35 @@
res.json().then( json => { res.json().then( json => {
for ( let song in json ) { for ( let song in json ) {
this.songQueue.push( json[ song ] ); this.songQueue.push( json[ song ] );
this.allSongs.push( json[ song ] );
} }
this.queueHandler();
this.isLoadingSongs = false; this.isLoadingSongs = false;
this.hasLoadedSongs = true; this.hasLoadedSongs = true;
this.$emit( 'com', { 'type': 'songsLoaded' } );
} ); } );
} }
} ); } );
} }
}, },
play( song ) { play( song ) {
this.$emit( 'playing', song ); if ( song.filename === this.currentlyPlaying ) {
this.$emit( 'com', { 'type': 'play', 'song': song } );
} else {
for ( let s in this.songQueue ) {
if ( this.songQueue[ s ][ 'filename' ] === song.filename ) {
this.songPos = parseInt( s );
} }
} }
this.$emit( 'com', { 'type': 'startPlayback', 'song': song } );
}
this.currentlyPlaying = song.filename;
this.update( { 'type': 'playback', 'status': true } );
},
pause( song ) {
this.update( { 'type': 'playback', 'status': false } );
this.$emit( 'com', { 'type': 'pause', 'song': song } );
},
}
} }
</script> </script>

View File

@@ -1,62 +1,263 @@
<template> <template>
<div class="player"> <div class="player">
<div class="controls"> <div class="controls">
<div v-if="audioLoaded"> <span class="material-symbols-outlined control-icon" :class="audioLoaded ? 'active': 'inactive'" @click="control( 'previous' )">skip_previous</span>
<span class="material-symbols-outlined control-icon" v-if="!isPlaying" @click="control( 'play' )">play_arrow</span> <span class="material-symbols-outlined control-icon" :class="audioLoaded ? 'active': 'inactive'" @click="control( 'replay10' )">replay_10</span>
<span class="material-symbols-outlined control-icon" v-else @click="control( 'pause' )">pause</span> <span class="material-symbols-outlined control-icon play-pause" v-if="!isPlaying && audioLoaded" @click="control( 'play' )">play_arrow</span>
</div> <span class="material-symbols-outlined control-icon play-pause" v-else-if="isPlaying && audioLoaded" @click="control( 'pause' )">pause</span>
<span class="material-symbols-outlined control-icon" style="cursor: default;" v-else>play_disabled</span> <span class="material-symbols-outlined control-icon play-pause" style="cursor: default;" v-else>play_disabled</span>
<span class="material-symbols-outlined control-icon" :class="audioLoaded ? 'active': 'inactive'" @click="control( 'forward10' )">forward_10</span>
<span class="material-symbols-outlined control-icon" :class="audioLoaded ? 'active': 'inactive'" @click="control( 'next' )" style="margin-right: 1vw;">skip_next</span>
<span class="material-symbols-outlined control-icon" :class="hasLoadedSongs ? 'active': 'inactive'" v-if="!isShuffleEnabled" @click="control( 'shuffleOn' )">shuffle</span>
<span class="material-symbols-outlined control-icon" :class="hasLoadedSongs ? 'active': 'inactive'" v-else @click="control( 'shuffleOff' )">shuffle_on</span>
<span class="material-symbols-outlined control-icon" :class="hasLoadedSongs ? 'active': 'inactive'" v-if="repeatMode === 'off'" @click="control( 'repeatOne' )">repeat</span>
<span class="material-symbols-outlined control-icon" :class="hasLoadedSongs ? 'active': 'inactive'" v-else-if="repeatMode === 'one'" @click="control( 'repeatAll' )">repeat_one_on</span>
<span class="material-symbols-outlined control-icon" :class="hasLoadedSongs ? 'active': 'inactive'" v-else-if="repeatMode === 'all'" @click="control( 'repeatOff' )">repeat_on</span>
</div> </div>
<div class="song-info"> <div class="song-info">
<audio v-if="audioLoaded" :src="'http://localhost:8081/getSongFile?filename=' + playingSong.filename" id="music-player"></audio> <audio v-if="audioLoaded" :src="'http://localhost:8081/getSongFile?filename=' + playingSong.filename" id="music-player"></audio>
<div class="song-info-wrapper">
<div v-if="audioLoaded">
<span class="material-symbols-outlined image" v-if="!playingSong.hasCoverArt">music_note</span>
<img v-else :src="'http://localhost:8081/getSongCover?filename=' + playingSong.filename" class="image">
</div>
<span class="material-symbols-outlined image" v-else>music_note</span>
<div class="name">
<h3>{{ playingSong.title ?? 'No song selected' }}</h3>
<p>{{ playingSong.artist }}</p>
</div>
</div>
<div class="playback-pos-info">
<div style="margin-right: auto;">{{ playbackPosBeautified }}</div>
<div>{{ durationBeautified }}</div>
</div>
<sliderView :active="audioLoaded" :position="playbackPos" :duration="playingSong.duration" @pos="( p ) => { setPos( p ) }"></sliderView>
</div> </div>
</div> </div>
</template> </template>
<style> <style>
.song-info { .song-info {
background-color: var( --accent-background ); background-color: #8e9ced;
height: 80%; height: 80%;
width: 50%; width: 50%;
margin-left: auto;
margin-right: auto;
position: relative;
}
.image {
width: 6vh;
height: 6vh;
object-fit: cover;
object-position: center;
font-size: 6vh;
margin-left: 1vh;
margin-top: 1vh;
}
.name {
margin-left: auto;
margin-right: auto;
}
.song-info-wrapper {
display: flex;
flex-direction: row;
}
.song-info-wrapper h3 {
margin: 0;
margin-bottom: 0.5vh;
margin-top: 1vh;
}
.controls {
margin-left: 5%;
display: flex;
justify-content: center;
align-items: center;
} }
.control-icon { .control-icon {
cursor: pointer; cursor: pointer;
font-size: 3vh;
user-select: none;
}
.play-pause {
font-size: 5vh;
}
.inactive {
color: gray;
cursor: default;
}
.player {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
.playback-pos-info {
display: flex;
flex-direction: row;
width: 99%;
margin-left: 0.5%;
position: absolute;
bottom: 17px;
} }
</style> </style>
<script> <script>
import SliderView from './sliderView.vue';
export default { export default {
data() { data() {
return { return {
playingSong: {}, playingSong: {},
audioLoaded: false, audioLoaded: false,
isPlaying: false, isPlaying: false,
isShuffleEnabled: false,
repeatMode: 'off',
progressTracker: null,
playbackPos: 0,
playbackPosBeautified: '00:00',
durationBeautified: '--:--',
hasLoadedSongs: false,
} }
}, },
components: {
SliderView,
},
methods: { methods: {
play( song ) { play( song, autoplay, doCrossFade = false ) {
this.playingSong = song; this.playingSong = song;
this.audioLoaded = true; this.audioLoaded = true;
this.init(); this.init( doCrossFade, autoplay );
}, },
init() { init( doCrossFade, autoplay ) {
this.control( 'reset' );
// TODO: make it support cross-fade
setTimeout( () => { setTimeout( () => {
document.getElementById( 'music-player' ).play(); if ( autoplay ) {
this.control( 'play' );
this.isPlaying = true; this.isPlaying = true;
}
const minuteCount = Math.floor( this.playingSong.duration / 60 );
this.durationBeautified = minuteCount + ':';
if ( ( '' + minuteCount ).length === 1 ) {
this.durationBeautified = '0' + minuteCount + ':';
}
const secondCount = Math.floor( this.playingSong.duration - minuteCount * 60 );
if ( ( '' + secondCount ).length === 1 ) {
this.durationBeautified += '0' + secondCount;
} else {
this.durationBeautified += secondCount;
}
let musicPlayer = document.getElementById( 'music-player' );
musicPlayer.onended = () => {
if ( musicPlayer.currentTime >= this.playingSong.duration - 1 ) {
if ( this.repeatMode !== 'one' ) {
this.control( 'next' );
} else {
musicPlayer.currentTime = 0;
this.control( 'play' );
}
}
}
}, 300 ); }, 300 );
}, },
control( action ) { control( action ) {
if ( document.getElementById( 'music-player' ) ) { // https://css-tricks.com/lets-create-a-custom-audio-player/
let musicPlayer = document.getElementById( 'music-player' );
if ( musicPlayer ) {
if ( action === 'play' ) { if ( action === 'play' ) {
document.getElementById( 'music-player' ).play(); this.$emit( 'update', { 'type': 'playback', 'status': true } );
musicPlayer.play();
this.isPlaying = true; this.isPlaying = true;
this.progressTracker = setInterval( () => {
this.playbackPos = musicPlayer.currentTime;
const minuteCount = Math.floor( this.playbackPos / 60 );
this.playbackPosBeautified = minuteCount + ':';
if ( ( '' + minuteCount ).length === 1 ) {
this.playbackPosBeautified = '0' + minuteCount + ':';
}
const secondCount = Math.floor( this.playbackPos - minuteCount * 60 );
if ( ( '' + secondCount ).length === 1 ) {
this.playbackPosBeautified += '0' + secondCount;
} else {
this.playbackPosBeautified += secondCount;
}
}, 0.02 );
} else if ( action === 'pause' ) { } else if ( action === 'pause' ) {
document.getElementById( 'music-player' ).pause(); this.$emit( 'update', { 'type': 'playback', 'status': false } );
musicPlayer.pause();
try {
clearInterval( this.progressTracker );
} catch ( err ) {};
this.isPlaying = false; this.isPlaying = false;
} else if ( action === 'replay10' ) {
musicPlayer.currentTime = musicPlayer.currentTime > 10 ? musicPlayer.currentTime - 10 : 0;
} else if ( action === 'forward10' ) {
if ( musicPlayer.currentTime < ( musicPlayer.duration - 10 ) ) {
musicPlayer.currentTime = musicPlayer.currentTime + 10;
} else {
if ( this.repeatMode !== 'one' ) {
this.control( 'next' );
} else {
musicPlayer.currentTime = 0;
} }
} }
} else if ( action === 'reset' ) {
clearInterval( this.progressTracker );
this.playbackPos = 0;
musicPlayer.currentTime = 0;
} else if ( action === 'next' ) {
this.$emit( 'update', { 'type': 'next' } );
} else if ( action === 'previous' ) {
this.$emit( 'update', { 'type': 'previous' } );
} else if ( action === 'shuffleOff' ) {
this.$emit( 'update', { 'type': 'shuffleOff' } );
this.isShuffleEnabled = false;
} else if ( action === 'shuffleOn' ) {
this.$emit( 'update', { 'type': 'shuffle' } );
this.isShuffleEnabled = true;
} else if ( action === 'repeatOne' ) {
this.repeatMode = 'one';
} else if ( action === 'repeatAll' ) {
this.$emit( 'update', { 'type': 'repeat' } );
this.repeatMode = 'all';
} else if ( action === 'repeatOff' ) {
this.$emit( 'update', { 'type': 'repeatOff' } );
this.repeatMode = 'off';
} }
} else if ( action === 'songsLoaded' ) {
this.hasLoadedSongs = true;
} else if ( action === 'shuffleOff' ) {
this.$emit( 'update', { 'type': 'shuffleOff' } );
this.isShuffleEnabled = false;
} else if ( action === 'shuffleOn' ) {
this.$emit( 'update', { 'type': 'shuffle' } );
this.isShuffleEnabled = true;
} else if ( action === 'repeatOne' ) {
this.repeatMode = 'one';
} else if ( action === 'repeatAll' ) {
this.$emit( 'update', { 'type': 'repeat' } );
this.repeatMode = 'all';
} else if ( action === 'repeatOff' ) {
this.$emit( 'update', { 'type': 'repeatOff' } );
this.repeatMode = 'off';
}
},
setPos( pos ) {
let musicPlayer = document.getElementById( 'music-player' );
musicPlayer.currentTime = pos;
},
} }
} }
</script> </script>

View File

@@ -0,0 +1,144 @@
<template>
<div style="width: 100%; height: 100%;">
<progress id="progress-slider" :value="sliderProgress" max="1000" @mousedown="( e ) => { setPos( e ) }" :class="active ? '' : 'slider-inactive'"></progress>
<div v-if="active" id="slider-knob" @mousedown="( e ) => { startMove( e ) }"
:style="'left: ' + ( parseInt( originalPos ) + parseInt( sliderPos ) ) + 'px;'">
<div id="slider-knob-style"></div>
</div>
<div v-else id="slider-knob" class="slider-inactive" style="left: 0;">
<div id="slider-knob-style"></div>
</div>
<div id="drag-support" @mousemove="e => { handleDrag( e ) }" @mouseup="() => { stopMove(); }"></div>
</div>
</template>
<style scoped>
#progress-slider {
width: 100%;
margin: 0;
position: absolute;
left: 0;
bottom: 0;
height: 5px;
cursor: pointer;
background-color: #baf4c9;
}
#progress-slider::-webkit-progress-value {
background-color: #baf4c9;
}
#slider-knob {
height: 20px;
width: 10px;
display: flex;
justify-content: flex-start;
align-items: flex-end;
position: absolute;
bottom: 0;
left: 0;
z-index: 2;
cursor: grab;
}
#slider-knob-style {
background-color: #baf4c9;
height: 15px;
width: 5px;
}
#drag-support {
display: none;
opacity: 0;
height: 100vh;
width: 100vw;
position: fixed;
top: 0;
left: 0;
z-index: 10;
cursor: grabbing;
}
.drag-support-active {
display: block !important;
}
.slider-inactive {
cursor: default !important;
}
</style>
<script>
export default {
props: {
style: {
type: Object,
},
position: {
type: Number,
default: 0,
},
duration: {
type: Number,
default: 100
},
active: {
type: Boolean,
default: true,
}
},
data () {
return {
offset: 0,
isDragging: false,
sliderPos: 0,
originalPos: 0,
sliderProgress: 0,
}
},
methods: {
handleDrag( e ) {
if ( this.isDragging ) {
if ( 0 < this.originalPos + e.screenX - this.offset && this.originalPos + e.screenX - this.offset < document.getElementById( 'progress-slider' ).clientWidth - 5 ) {
this.sliderPos = e.screenX - this.offset;
this.calcProgressPos();
}
}
},
startMove( e ) {
this.offset = e.screenX;
this.isDragging = true;
document.getElementById( 'drag-support' ).classList.add( 'drag-support-active' );
},
stopMove() {
this.originalPos += parseInt( this.sliderPos );
this.isDragging = false;
this.offset = 0;
this.sliderPos = 0;
document.getElementById( 'drag-support' ).classList.remove( 'drag-support-active' );
this.calcPlaybackPos();
},
setPos ( e ) {
if ( this.active ) {
this.originalPos = e.offsetX;
this.calcProgressPos();
this.calcPlaybackPos();
}
},
calcProgressPos() {
this.sliderProgress = Math.ceil( ( this.originalPos + parseInt( this.sliderPos ) ) / ( document.getElementById( 'progress-slider' ).clientWidth - 5 ) * 1000 );
},
calcPlaybackPos() {
this.$emit( 'pos', Math.round( ( this.originalPos + parseInt( this.sliderPos ) ) / ( document.getElementById( 'progress-slider' ).clientWidth - 5 ) * this.duration ) );
}
},
watch: {
position() {
if ( !this.isDragging ) {
this.sliderProgress = Math.ceil( this.position / this.duration * 1000 + 2 );
this.originalPos = Math.ceil( this.position / this.duration * ( document.getElementById( 'progress-slider' ).clientWidth - 5 ) );
}
}
}
}
</script>

View File

@@ -3,11 +3,11 @@
<div class="top-bar"> <div class="top-bar">
<img src="@/assets/logo.png" alt="logo" class="logo"> <img src="@/assets/logo.png" alt="logo" class="logo">
<div class="player-wrapper"> <div class="player-wrapper">
<Player ref="player"></Player> <Player ref="player" @update="( info ) => { handleUpdates( info ) }"></Player>
</div> </div>
</div> </div>
<div class="pool-wrapper"> <div class="pool-wrapper">
<mediaPool @playing="( song ) => { handlePlaying( song ) }"></mediaPool> <mediaPool @com="( info ) => { handleCom( info ) }" ref="pool"></mediaPool>
</div> </div>
</div> </div>
</template> </template>
@@ -20,17 +20,21 @@
.pool-wrapper { .pool-wrapper {
height: 84vh; height: 84vh;
margin-top: 16vh;
} }
.top-bar { .top-bar {
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
position: fixed;
z-index: 8;
width: 99%; width: 99%;
height: 15vh; height: 15vh;
display: flex; display: flex;
align-items: center; align-items: center;
flex-direction: row; flex-direction: row;
border: var( --primary-color ) 2px solid; border: var( --primary-color ) 2px solid;
background-color: var( --background-color );
} }
.player-wrapper { .player-wrapper {
@@ -66,9 +70,16 @@
} }
}, },
methods: { methods: {
handlePlaying ( song ) { handleCom ( data ) {
this.$refs.player.play( song ); if ( data.type === 'startPlayback' ) {
this.$refs.player.play( data.song, data.autoplay === undefined ? true : data.autoplay );
} else {
this.$refs.player.control( data.type );
} }
},
handleUpdates ( data ) {
this.$refs.pool.update( data );
},
} }
} }
</script> </script>