diff --git a/frontend/src/client/audioProcessing.js b/frontend/src/client/audioProcessing.js
new file mode 100644
index 0000000..e69de29
diff --git a/frontend/src/client/backgroundAnim.css b/frontend/src/client/backgroundAnim.css
index 0b2d7e4..e7e5827 100644
--- a/frontend/src/client/backgroundAnim.css
+++ b/frontend/src/client/backgroundAnim.css
@@ -9,22 +9,14 @@
background: conic-gradient( blue, green, red, blue );
animation: gradientAnim 10s infinite linear;
background-position: center;
+ transition: all 0.1s;
}
.beat {
height: 100%;
width: 100%;
background-color: rgba( 0, 0, 0, 0.2 );
- animation: beatAnim 0.6s infinite linear;
-}
-
-@keyframes beatAnim {
- 0% {
- background-color: rgba( 0, 0, 0, 0.2 );
- }
- 50% {
- background-color: rgba( 0, 0, 0, 0 );
- }
+ display: none;
}
@keyframes gradientAnim {
diff --git a/frontend/src/client/colorPaletteExtractor.js b/frontend/src/client/colorPaletteExtractor.js
index cd9d2f7..5a25c81 100644
--- a/frontend/src/client/colorPaletteExtractor.js
+++ b/frontend/src/client/colorPaletteExtractor.js
@@ -121,7 +121,12 @@ const getColourPalette = ( imageURL ) => {
canvas.width = image.width ?? 500;
canvas.height = image.height ?? 500;
const ctx = canvas.getContext( '2d' );
- ctx.drawImage(image, 0, 0);
+ try {
+ ctx.drawImage( image, 0, 0 );
+ } catch ( err ) {
+ reject( err );
+ return;
+ }
/**
* getImageData returns an array full of RGBA values
diff --git a/frontend/src/client/showcase.html b/frontend/src/client/showcase.html
index d59337c..b7627cb 100644
--- a/frontend/src/client/showcase.html
+++ b/frontend/src/client/showcase.html
@@ -8,6 +8,7 @@
+
diff --git a/frontend/src/client/showcase.js b/frontend/src/client/showcase.js
index 4d84231..9e3860c 100644
--- a/frontend/src/client/showcase.js
+++ b/frontend/src/client/showcase.js
@@ -11,6 +11,11 @@ createApp( {
pos: 0,
queuePos: 0,
colourPalette: [],
+ startTime: 0,
+ offsetTime: 0,
+ progressBar: 0,
+ timeTracker: null,
+ timeCorrector: null,
};
},
computed: {
@@ -25,9 +30,25 @@ createApp( {
}
},
methods: {
+ startTimeTracker () {
+ this.startTime = new Date().getTime();
+ this.timeTracker = setInterval( () => {
+ this.pos += 0.075;
+ 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 );
+ },
+ stopTimeTracker () {
+ clearInterval( this.timeTracker );
+ clearInterval( this.timeCorrector );
+ this.oldPos = this.pos;
+ },
connect() {
let source = new EventSource( '/clientDisplayNotifier', { withCredentials: true } );
-
source.onmessage = ( e ) => {
let data;
try {
@@ -40,15 +61,22 @@ createApp( {
this.playingSong = data.data.playingSong ?? {};
this.songs = data.data.songQueue ?? [];
this.pos = data.data.pos ?? 0;
+ this.oldPos = data.data.pos ?? 0;
+ this.progressBar = this.pos / this.playingSong.duration * 1000;
this.queuePos = data.data.queuePos ?? 0;
+ if ( this.isPlaying ) this.startTimeTracker();
getColourPalette( '/getSongCover?filename=' + data.data.playingSong.filename ).then( palette => {
this.colourPalette = palette;
this.handleBackground();
} ).catch( () => {
- this.colourPalette = [ { 'r': 255, 'g': 0, 'b': 0 }, { 'r': 0, 'g': 255, 'b': 0 }, { 'r': 0, 'g': 0, 'b': 255 } ]
+ this.colourPalette = [ { 'r': 255, 'g': 0, 'b': 0 }, { 'r': 0, 'g': 255, 'b': 0 }, { 'r': 0, 'g': 0, 'b': 255 } ];
+ this.handleBackground();
} );
} 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;
this.handleBackground();
@@ -60,7 +88,8 @@ createApp( {
this.colourPalette = palette;
this.handleBackground();
} ).catch( () => {
- this.colourPalette = [ { 'r': 255, 'g': 0, 'b': 0 }, { 'r': 0, 'g': 255, 'b': 0 }, { 'r': 0, 'g': 0, 'b': 255 } ]
+ this.colourPalette = [ { 'r': 255, 'g': 0, 'b': 0 }, { 'r': 0, 'g': 255, 'b': 0 }, { 'r': 0, 'g': 0, 'b': 255 } ];
+ this.handleBackground();
} );
} else if ( data.type === 'queuePos' ) {
this.queuePos = data.data;
@@ -80,20 +109,39 @@ createApp( {
}, false );
},
handleBackground() {
+ // TODO: Consider using mic and realtime-bpm-analyzer
let colours = {};
- for ( let i = 0; i < 3; i++ ) {
- colours[ i ] = 'rgb(' + this.colourPalette[ i ].r + ',' + this.colourPalette[ i ].g + ',' + this.colourPalette[ i ].b + ')';
- }
- $( '.background' ).css( 'background', `conic-gradient( ${ colours[ 0 ] }, ${ colours[ 1 ] }, ${ colours[ 2 ] }, ${ colours[ 0 ] } } )` );
- if ( this.playingSong.bpm && this.isPlaying ) {
- $( '.beat' ).show();
- $( '.beat' ).css( 'animation-duration', 60 / this.playingSong.bpm );
- } else {
- $( '.beat' ).hide();
+ if ( this.colourPalette[ 0 ] ) {
+ for ( let i = 0; i < 3; i++ ) {
+ colours[ i ] = 'rgb(' + this.colourPalette[ i ].r + ',' + this.colourPalette[ i ].g + ',' + this.colourPalette[ i ].b + ')';
+ }
}
+ $( '#background' ).css( 'background', `conic-gradient( ${ colours[ 0 ] }, ${ colours[ 1 ] }, ${ colours[ 2 ] }, ${ colours[ 0 ] } )` );
+ // 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 ) );
+ // } else {
+ // $( '.beat' ).hide();
+ // }
}
},
mounted() {
this.connect();
+ // Initialize Web Audio API components
+ const audioContext = new ( window.AudioContext || window.webkitAudioContext )();
+ // Start audio analysis
+ navigator.mediaDevices.getUserMedia( { audio: true } ).then( ( stream ) => {
+
+ } );
+ },
+ watch: {
+ isPlaying( value ) {
+ if ( value ) {
+ this.startTimeTracker();
+ } else {
+ this.stopTimeTracker();
+ }
+ }
}
} ).mount( '#app' );
diff --git a/frontend/src/components/player.vue b/frontend/src/components/player.vue
index c3a2742..d9993c3 100644
--- a/frontend/src/components/player.vue
+++ b/frontend/src/components/player.vue
@@ -325,7 +325,9 @@ export default {
},
setPos( pos ) {
let musicPlayer = document.getElementById( 'music-player' );
+ this.playbackPos = pos;
musicPlayer.currentTime = pos;
+ this.sendUpdate( 'pos' );
},
showFancyView() {
this.$emit( 'update', { 'type': 'fancyView', 'status': true } );