@@ -27,17 +27,6 @@
- music_note
-
-
-
-
diff --git a/frontend/src/client/showcase.js b/frontend/src/client/showcase.js
index 0fc1c40..21c7055 100644
--- a/frontend/src/client/showcase.js
+++ b/frontend/src/client/showcase.js
@@ -11,11 +11,8 @@ createApp( {
pos: 0,
queuePos: 0,
colourPalette: [],
- startTime: 0,
- offsetTime: 0,
progressBar: 0,
timeTracker: null,
- timeCorrector: null,
visualizationSettings: 'mic',
micAnalyzer: null,
beatDetected: false,
@@ -59,20 +56,16 @@ createApp( {
},
methods: {
startTimeTracker () {
- this.startTime = new Date().getTime();
this.timeTracker = setInterval( () => {
- this.pos += 0.075;
+ this.pos = ( new Date().getTime() - this.playingSong.startTime ) / 1000 + this.oldPos;
this.progressBar = ( this.pos / this.playingSong.duration ) * 1000;
- }, 75 );
-
- this.timeCorrector = setInterval( () => {
- this.pos = this.oldPos + ( new Date().getTime() - this.startTime ) / 1000;
- this.progressBar = ( this.pos / this.playingSong.duration ) * 1000;
- }, 5000 );
+ if ( isNaN( this.progressBar ) ) {
+ this.progressBar = 0;
+ }
+ }, 100 );
},
stopTimeTracker () {
clearInterval( this.timeTracker );
- clearInterval( this.timeCorrector );
this.oldPos = this.pos;
},
getImageData() {
@@ -109,7 +102,6 @@ createApp( {
this.songs = data.data.songQueue ?? [];
this.pos = data.data.pos ?? 0;
this.oldPos = data.data.pos ?? 0;
- this.startTime = new Date().getTime();
this.progressBar = this.pos / this.playingSong.duration * 1000;
this.queuePos = data.data.queuePos ?? 0;
this.getImageData().then( palette => {
@@ -122,7 +114,6 @@ createApp( {
} else if ( data.type === 'pos' ) {
this.pos = data.data;
this.oldPos = data.data;
- this.startTime = new Date().getTime();
this.progressBar = data.data / this.playingSong.duration * 1000;
} else if ( data.type === 'isPlaying' ) {
this.isPlaying = data.data;
@@ -146,6 +137,8 @@ createApp( {
source.onopen = () => {
this.hasLoaded = true;
};
+
+ let self = this;
source.addEventListener( 'error', function( e ) {
if ( e.eventPhase == EventSource.CLOSED ) source.close();
@@ -153,6 +146,11 @@ createApp( {
if ( e.target.readyState == EventSource.CLOSED ) {
console.log( 'disconnected' );
}
+
+ // TODO: Notify about disconnect
+ setTimeout( () => {
+ self.connect();
+ }, 1000 );
}, false );
},
handleBackground() {
@@ -206,28 +204,32 @@ createApp( {
this.setVisualization();
},
setVisualization () {
- if ( this.visualizationSettings === 'bpm' ) {
- if ( this.playingSong.bpm && this.isPlaying ) {
- $( '.beat' ).show();
- $( '.beat' ).css( 'animation-duration', 60 / this.playingSong.bpm );
- $( '.beat' ).css( 'animation-delay', this.pos % ( 60 / this.playingSong.bpm * this.pos ) + this.playingSong.bpmOffset - ( 60 / this.playingSong.bpm * this.pos / 2 ) );
- } else {
+ if ( Object.keys( this.playingSong ).length > 0 ) {
+ if ( this.visualizationSettings === 'bpm' ) {
+ if ( this.playingSong.bpm && this.isPlaying ) {
+ $( '.beat' ).show();
+ $( '.beat' ).css( 'animation-duration', 60 / this.playingSong.bpm );
+ $( '.beat' ).css( 'animation-delay', this.pos % ( 60 / this.playingSong.bpm * this.pos ) + this.playingSong.bpmOffset - ( 60 / this.playingSong.bpm * this.pos / 2 ) );
+ } else {
+ $( '.beat' ).hide();
+ }
+ try {
+ clearInterval( this.micAnalyzer );
+ } catch ( err ) {}
+ } else if ( this.visualizationSettings === 'off' ) {
$( '.beat' ).hide();
+ try {
+ clearInterval( this.micAnalyzer );
+ } catch ( err ) {}
+ } else if ( this.visualizationSettings === 'mic' ) {
+ $( '.beat-manual' ).hide();
+ try {
+ clearInterval( this.micAnalyzer );
+ } catch ( err ) {}
+ this.micAudioHandler();
}
- try {
- clearInterval( this.micAnalyzer );
- } catch ( err ) {}
- } else if ( this.visualizationSettings === 'off' ) {
- $( '.beat' ).hide();
- try {
- clearInterval( this.micAnalyzer );
- } catch ( err ) {}
- } else if ( this.visualizationSettings === 'mic' ) {
- $( '.beat-manual' ).hide();
- try {
- clearInterval( this.micAnalyzer );
- } catch ( err ) {}
- this.micAudioHandler();
+ } else {
+ console.log( 'not playing yet' );
}
},
micAudioHandler () {
@@ -288,9 +290,9 @@ createApp( {
},
mounted() {
this.connect();
- if ( this.visualizationSettings === 'mic' ) {
- this.micAudioHandler();
- }
+ // if ( this.visualizationSettings === 'mic' ) {
+ // this.micAudioHandler();
+ // }
},
watch: {
isPlaying( value ) {
diff --git a/frontend/src/components/player.vue b/frontend/src/components/player.vue
index 347acd6..70a8431 100644
--- a/frontend/src/components/player.vue
+++ b/frontend/src/components/player.vue
@@ -31,7 +31,7 @@
{ setPos( p ) }"
name="player">
@@ -142,6 +142,7 @@ export default {
hasLoadedSongs: false,
isShowingFancyView: false,
notifier: null,
+ isShowingRemainingTime: false,
}
},
components: {
@@ -208,6 +209,22 @@ export default {
}
}, 300 );
},
+ toggleShowMode() {
+ this.isShowingRemainingTime = !this.isShowingRemainingTime;
+ if ( !this.isShowingRemainingTime ) {
+ const minuteCounts = Math.floor( this.playingSong.duration / 60 );
+ this.durationBeautified = String( minuteCounts ) + ':';
+ if ( ( '' + minuteCounts ).length === 1 ) {
+ this.durationBeautified = '0' + minuteCounts + ':';
+ }
+ const secondCounts = Math.floor( this.playingSong.duration - minuteCounts * 60 );
+ if ( ( '' + secondCounts ).length === 1 ) {
+ this.durationBeautified += '0' + secondCounts;
+ } else {
+ this.durationBeautified += secondCounts;
+ }
+ }
+ },
sendUpdate( update ) {
let data = {};
if ( update === 'pos' ) {
@@ -250,14 +267,27 @@ export default {
} else {
this.playbackPosBeautified += secondCount;
}
- }, 0.02 );
- this.progressTracker = setInterval( () => {
- this.sendUpdate( 'pos' );
- }, 5000 );
+
+ if ( this.isShowingRemainingTime ) {
+ const minuteCounts = Math.floor( ( this.playingSong.duration - this.playbackPos ) / 60 );
+ this.durationBeautified = '-' + String( minuteCounts ) + ':';
+ if ( ( '' + minuteCounts ).length === 1 ) {
+ this.durationBeautified = '-0' + minuteCounts + ':';
+ }
+ const secondCounts = Math.floor( ( this.playingSong.duration - this.playbackPos ) - minuteCounts * 60 );
+ if ( ( '' + secondCounts ).length === 1 ) {
+ this.durationBeautified += '0' + secondCounts;
+ } else {
+ this.durationBeautified += secondCounts;
+ }
+ }
+ }, 20 );
+ this.sendUpdate( 'pos' );
this.sendUpdate( 'isPlaying' );
} else if ( action === 'pause' ) {
this.$emit( 'update', { 'type': 'playback', 'status': false } );
musicPlayer.pause();
+ this.sendUpdate( 'pos' );
try {
clearInterval( this.progressTracker );
clearInterval( this.notifier );
diff --git a/frontend/src/config/authKey.txt b/frontend/src/config/authKey.txt
deleted file mode 100644
index 0169bdf..0000000
--- a/frontend/src/config/authKey.txt
+++ /dev/null
@@ -1 +0,0 @@
-gaöwovwef89voawö8p9 odövefw8öoaewpf89wec
\ No newline at end of file
diff --git a/frontend/src/config/config.json b/frontend/src/config/config.json
new file mode 100644
index 0000000..6d4cf6a
--- /dev/null
+++ b/frontend/src/config/config.json
@@ -0,0 +1,5 @@
+{
+ "connectionURL": "http://localhost:3000",
+ "authKey": "gaöwovwef89voawö8p9 odövefw8öoaewpf89wec",
+ "doConnect": true
+}
\ No newline at end of file
{{ song.title }}
{{ song.artist }}
diff --git a/backend/ui/showcase.css b/backend/ui/showcase.css index 8d5bb75..adb209a 100644 --- a/backend/ui/showcase.css +++ b/backend/ui/showcase.css @@ -48,52 +48,18 @@ body { flex-direction: row; } -.playing-bar { - height: 60%; - background-color: white; - 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 ); - } -} - .song-list-wrapper { width: 100%; display: flex; justify-content: center; align-items: center; flex-direction: column; + margin-bottom: 5%; } .song-list { display: flex; flex-direction: row; - justify-content: center; align-items: center; width: 80%; margin: 2px; @@ -107,6 +73,7 @@ body { display: block; margin-left: 10px; margin-right: auto; + width: 65%; } .song-list .song-image { @@ -128,22 +95,20 @@ body { .current-song-wrapper { display: flex; - justify-content: center; align-items: center; flex-direction: column; - height: 55vh; width: 100%; - margin-bottom: 0.5%; - margin-top: 0.25%; + margin-bottom: 2%; + margin-top: 1%; } .current-song { display: flex; - justify-content: center; align-items: center; flex-direction: column; margin-top: 1vh; padding: 1vh; + max-width: 80%; text-align: center; background-color: rgba( 0, 0, 0, 0.4 ); } @@ -157,19 +122,27 @@ body { font-size: 30vh !important; } -#canvas { - display: none; -} - #app { background-color: rgba( 0, 0, 0, 0 ); } -#progress { +#progress, #progress::-webkit-progress-bar { background-color: rgba(45, 28, 145); + color: rgba(45, 28, 145); width: 30vw; border: none; - border-radius: 50px; + border-radius: 0px; + accent-color: white; + -webkit-appearance: none; + appearance: none; +} + +#progress::-moz-progress-bar { + background-color: white; +} + +#progress::-webkit-progress-value { + background-color: white !important; } .mode-selector-wrapper { @@ -198,4 +171,9 @@ body { margin: 0; padding: 0; top: 50%; +} + +.time-until { + width: 30%; + text-align: end; } \ No newline at end of file diff --git a/backend/ui/showcase.js b/backend/ui/showcase.js index 2609216..84325a8 100644 --- a/backend/ui/showcase.js +++ b/backend/ui/showcase.js @@ -11,8 +11,6 @@ createApp( { pos: 0, queuePos: 0, colourPalette: [], - startTime: 0, - offsetTime: 0, progressBar: 0, timeTracker: null, }; @@ -54,11 +52,13 @@ createApp( { }, methods: { startTimeTracker () { - this.startTime = new Date().getTime(); this.timeTracker = setInterval( () => { - this.pos = this.playingSong.startTime - new Date().getTime() / 1000; + this.pos = ( new Date().getTime() - this.playingSong.startTime ) / 1000 + this.oldPos; this.progressBar = ( this.pos / this.playingSong.duration ) * 1000; - }, 75 ); + if ( isNaN( this.progressBar ) ) { + this.progressBar = 0; + } + }, 100 ); }, stopTimeTracker () { clearInterval( this.timeTracker ); @@ -79,13 +79,11 @@ createApp( { this.songs = data.data.songQueue ?? []; this.pos = data.data.pos ?? 0; this.oldPos = data.data.pos ?? 0; - this.startTime = new Date().getTime(); this.progressBar = this.pos / this.playingSong.duration * 1000; this.queuePos = data.data.queuePos ?? 0; } else if ( data.type === 'pos' ) { this.pos = data.data; this.oldPos = data.data; - this.startTime = new Date().getTime(); this.progressBar = data.data / this.playingSong.duration * 1000; } else if ( data.type === 'isPlaying' ) { this.isPlaying = data.data; @@ -102,12 +100,18 @@ createApp( { this.hasLoaded = true; }; + let self = this; + source.addEventListener( 'error', function( e ) { if ( e.eventPhase == EventSource.CLOSED ) source.close(); if ( e.target.readyState == EventSource.CLOSED ) { console.log( 'disconnected' ); } + + setTimeout( () => { + self.connect(); + }, 1000 ); }, false ); }, }, diff --git a/frontend/src/app.js b/frontend/src/app.js index 65c9461..7d7337c 100644 --- a/frontend/src/app.js +++ b/frontend/src/app.js @@ -19,13 +19,14 @@ app.use( session( { resave: false, } ) ); +const conf = JSON.parse( fs.readFileSync( path.join( __dirname + '/config/config.json' ) ) ); // TODO: Import from config -const remoteURL = 'http://localhost:3000'; +const remoteURL = conf.connectionURL ?? 'http://localhost:3000'; let hasConnected = false; const connect = () => { - if ( authKey !== '' ) { + if ( authKey !== '' && conf.doConnect ) { axios.post( remoteURL + '/connect', { 'authKey': authKey } ).then( res => { if ( res.status === 200 ) { console.log( '[ BACKEND INTEGRATION ] Connection successful' ); @@ -42,10 +43,7 @@ const connect = () => { } }; -let authKey = ''; -try { - authKey = '' + fs.readFileSync( path.join( __dirname + '/config/authKey.txt' ) ); -} catch( err ) {}; +let authKey = conf.authKey ?? ''; connect(); @@ -69,8 +67,8 @@ app.get( '/', ( request, response ) => { } ); app.get( '/openSongs', ( req, res ) => { - res.send( '{ "data": [ "/home/janis/Music/KB2022" ] }' ); - // res.send( '{ "data": [ "/mnt/storage/SORTED/Music/audio/KB2022" ] }' ); + // res.send( '{ "data": [ "/home/janis/Music/KB2022" ] }' ); + res.send( '{ "data": [ "/mnt/storage/SORTED/Music/audio/KB2022" ] }' ); // res.send( { 'data': dialog.showOpenDialogSync( { properties: [ 'openDirectory' ], title: 'Open music library folder' } ) } ); } ); @@ -119,6 +117,24 @@ app.get( '/mainNotifier', ( req, res ) => { } ); const sendUpdate = ( update ) => { + if ( update === 'pos' ) { + currentDetails[ 'playingSong' ][ 'startTime' ] = new Date().getTime(); + for ( let client in connectedClients ) { + connectedClients[ client ].write( 'data: ' + JSON.stringify( { 'type': 'playingSong', 'data': currentDetails[ 'playingSong' ] } ) + '\n\n' ); + } + } else if ( update === 'playingSong' ) { + currentDetails[ update ][ 'startTime' ] = new Date().getTime(); + } else if ( update === 'isPlaying' ) { + currentDetails[ 'playingSong' ][ 'startTime' ] = new Date().getTime(); + for ( let client in connectedClients ) { + connectedClients[ client ].write( 'data: ' + JSON.stringify( { 'type': 'playingSong', 'data': currentDetails[ 'playingSong' ] } ) + '\n\n' ); + } + + for ( let client in connectedClients ) { + connectedClients[ client ].write( 'data: ' + JSON.stringify( { 'type': 'pos', 'data': currentDetails[ 'pos' ] } ) + '\n\n' ); + } + } + for ( let client in connectedClients ) { connectedClients[ client ].write( 'data: ' + JSON.stringify( { 'type': update, 'data': currentDetails[ update ] } ) + '\n\n' ); } @@ -126,16 +142,20 @@ const sendUpdate = ( update ) => { // Check if connected and if not, try to authenticate with data from authKey file if ( hasConnected ) { - if ( update === 'pos' ) { - return; - } else if ( update === 'playingSong' ) { - currentDetails[ update ][ 'startTime' ] === new Date().getTime(); + if ( update === 'isPlaying' ) { + axios.post( remoteURL + '/statusUpdate', { 'type': 'playingSong', 'data': currentDetails[ 'playingSong' ], 'authKey': authKey } ).catch( err => { + console.error( err ); + } ); + + axios.post( remoteURL + '/statusUpdate', { 'type': 'pos', 'data': currentDetails[ 'pos' ], 'authKey': authKey } ).catch( err => { + console.error( err ); + } ); + } else if ( update === 'pos' ) { + axios.post( remoteURL + '/statusUpdate', { 'type': 'playingSong', 'data': currentDetails[ 'playingSong' ], 'authKey': authKey } ).catch( err => { + console.error( err ); + } ); } - axios.post( remoteURL + '/statusUpdate', { 'type': update, 'data': currentDetails[ update ], 'authKey': authKey } ).then( res => { - if ( res.status !== 200 ) { - console.log( res ); - } - } ).catch( err => { + axios.post( remoteURL + '/statusUpdate', { 'type': update, 'data': currentDetails[ update ], 'authKey': authKey } ).catch( err => { console.error( err ); } ); } else { diff --git a/frontend/src/client/showcase.css b/frontend/src/client/showcase.css index d2a0090..bc3c4b3 100644 --- a/frontend/src/client/showcase.css +++ b/frontend/src/client/showcase.css @@ -156,19 +156,27 @@ body { font-size: 30vh !important; } -#canvas { - display: none; -} - #app { background-color: rgba( 0, 0, 0, 0 ); } -#progress { +#progress, #progress::-webkit-progress-bar { background-color: rgba(45, 28, 145); + color: rgba(45, 28, 145); width: 30vw; border: none; - border-radius: 50px; + border-radius: 0px; + accent-color: white; + -webkit-appearance: none; + appearance: none; +} + +#progress::-moz-progress-bar { + background-color: white; +} + +#progress::-webkit-progress-value { + background-color: white !important; } .mode-selector-wrapper { diff --git a/frontend/src/client/showcase.html b/frontend/src/client/showcase.html index 4fb3b99..8cc5596 100644 --- a/frontend/src/client/showcase.html +++ b/frontend/src/client/showcase.html @@ -12,7 +12,7 @@ -Designed and developed by Janis Hutz https://janishutz.com
+ Designed and developed by Janis Hutz https://janishutz.com
@@ -64,7 +64,6 @@
-
{{ playbackPosBeautified }}
- {{ durationBeautified }}
+ {{ durationBeautified }}