apple-music integration working but unstable

This commit is contained in:
2023-11-17 09:54:04 +01:00
parent aa64d07570
commit da4924b453
4 changed files with 120 additions and 74 deletions

View File

@@ -54,6 +54,9 @@ createApp( {
}, },
methods: { methods: {
startTimeTracker () { startTimeTracker () {
try {
clearInterval( this.timeTracker );
} catch ( err ) {}
this.timeTracker = setInterval( () => { this.timeTracker = setInterval( () => {
this.pos = ( new Date().getTime() - this.playingSong.startTime ) / 1000 + this.oldPos; this.pos = ( new Date().getTime() - this.playingSong.startTime ) / 1000 + this.oldPos;
this.progressBar = ( this.pos / this.playingSong.duration ) * 1000; this.progressBar = ( this.pos / this.playingSong.duration ) * 1000;

3
frontend/.gitignore vendored
View File

@@ -23,4 +23,5 @@ pnpm-debug.log*
*.sw? *.sw?
#Electron-builder output #Electron-builder output
/dist_electron /dist_electron
test

View File

@@ -58,49 +58,45 @@ const app = Vue.createApp( {
build: '2' build: '2'
}, },
storefrontId: 'CH', storefrontId: 'CH',
} ); } ).then( () => {
this.config.devToken = token; this.config.devToken = token;
this.musicKit = MusicKit.getInstance(); this.musicKit = MusicKit.getInstance();
if ( this.musicKit.isAuthorized ) { if ( this.musicKit.isAuthorized ) {
this.isLoggedIn = true; this.isLoggedIn = true;
this.config.userToken = this.musicKit.musicUserToken; this.config.userToken = this.musicKit.musicUserToken;
}
this.musicKit.addEventListener( 'playbackStateDidChange', ( e ) => {
console.log( 'Playback state changed: ', e );
} );
this.musicKit.addEventListener( 'queueItemsDidChange', ( event ) => {
console.log( 'Queue items changed:', event );
} );
this.musicKit.addEventListener( 'playbackError', ( event ) => {
console.log( 'Playback Error:', event );
} );
this.musicKit.addEventListener( 'mediaItemDidChange', ( e ) => {
// Assemble this.playingSong
// TODO: Also add additional items to queue if there are new
// items that weren't previously shown (limitation of MusicKitJS).
this.playingSong = {
'artist': e.item.attributes.artistName,
'title': e.item.attributes.name,
'year': e.item.attributes.releaseDate,
// Think about bpm analysis
// 'bpm': metadata[ 'common' ][ 'bpm' ],
'genre': e.item.attributes.genreNames,
'duration': Math.round( e.item.attributes.durationInMillis / 1000 ),
'filename': this.songQueue[ this.musicKit.player.nowPlayingItemIndex ].filename,
'coverArtOrigin': 'api',
'hasCoverArt': true,
'queuePos': this.musicKit.player.nowPlayingItemIndex,
} }
let url = e.item.attributes.artwork.url; this.musicKit.shuffleMode = MusicKit.PlayerShuffleMode.off;
url = url.replace( '{w}', e.item.attributes.artwork.width ); this.musicKit.addEventListener( 'nowPlayingItemDidChange', ( e ) => {
url = url.replace( '{h}', e.item.attributes.artwork.height ); this.control( 'play' );
this.playingSong[ 'coverArtURL' ] = url; // Assemble this.playingSong
this.queuePos = this.musicKit.player.nowPlayingItemIndex; // TODO: Also add additional items to queue if there are new
this.sendUpdate( 'playingSong' ); // items that weren't previously shown (limitation of MusicKitJS).
this.sendUpdate( 'pos' ); if ( e.item ) {
this.sendUpdate( 'queuePos' ); this.playingSong = {
'artist': e.item.attributes.artistName,
'title': e.item.attributes.name,
'year': e.item.attributes.releaseDate,
// Think about bpm analysis
// 'bpm': metadata[ 'common' ][ 'bpm' ],
'genre': e.item.attributes.genreNames,
'duration': Math.round( e.item.attributes.durationInMillis / 1000 ),
'filename': this.songQueue[ this.musicKit.nowPlayingItemIndex ].filename,
'coverArtOrigin': 'api',
'hasCoverArt': true,
'queuePos': this.musicKit.nowPlayingItemIndex,
}
let url = e.item.attributes.artwork.url;
url = url.replace( '{w}', e.item.attributes.artwork.width );
url = url.replace( '{h}', e.item.attributes.artwork.height );
this.playingSong[ 'coverArtURL' ] = url;
this.queuePos = this.musicKit.nowPlayingItemIndex;
this.sendUpdate( 'playingSong' );
this.sendUpdate( 'pos' );
this.sendUpdate( 'queuePos' );
}
} );
this.apiGetRequest( 'https://api.music.apple.com/v1/me/library/playlists', this.playlistHandler );
} ); } );
this.apiGetRequest( 'https://api.music.apple.com/v1/me/library/playlists', this.playlistHandler );
} ); } );
} }
} ); } );
@@ -147,14 +143,14 @@ const app = Vue.createApp( {
this.musicKit.setQueue( { playlist: id } ).then( () => { this.musicKit.setQueue( { playlist: id } ).then( () => {
try { try {
this.control( 'play' ); this.control( 'play' );
const songQueue = this.musicKit.player.queue.items; const songQueue = this.musicKit.queue.items;
for ( let item in songQueue ) { for ( let item in songQueue ) {
this.songQueue[ item ] = { this.songQueue[ item ] = {
'artist': songQueue[ item ].attributes.artistName, 'artist': songQueue[ item ].attributes.artistName,
'title': songQueue[ item ].attributes.name, 'title': songQueue[ item ].attributes.name,
'year': songQueue[ item ].attributes.releaseDate, 'year': songQueue[ item ].attributes.releaseDate,
'genre': songQueue[ item ].attributes.genreNames, 'genre': songQueue[ item ].attributes.genreNames,
'duration': Math.round( songQueue[ item ].attributes.durationInMillis / 1000 ), 'duration': Math.round( songQueue[ item ].durationInMillis / 1000 ),
'filename': songQueue[ item ].id, 'filename': songQueue[ item ].id,
'coverArtOrigin': 'api', 'coverArtOrigin': 'api',
'hasCoverArt': true, 'hasCoverArt': true,
@@ -205,6 +201,7 @@ const app = Vue.createApp( {
this.calcProgressPos(); this.calcProgressPos();
this.calcPlaybackPos(); this.calcPlaybackPos();
this.musicKit.seekToTime( this.pos ); this.musicKit.seekToTime( this.pos );
this.sendUpdate( 'pos' );
} }
}, },
calcProgressPos() { calcProgressPos() {
@@ -224,7 +221,7 @@ const app = Vue.createApp( {
} else if ( update === 'songQueue' ) { } else if ( update === 'songQueue' ) {
data = this.songQueue; data = this.songQueue;
} else if ( update === 'queuePos' ) { } else if ( update === 'queuePos' ) {
this.queuePos = this.musicKit.player.nowPlayingItemIndex >= 0 ? this.musicKit.player.nowPlayingItemIndex : 0; this.queuePos = this.musicKit.nowPlayingItemIndex >= 0 ? this.musicKit.nowPlayingItemIndex : 0;
data = this.queuePos; data = this.queuePos;
} }
let fetchOptions = { let fetchOptions = {
@@ -239,18 +236,30 @@ const app = Vue.createApp( {
console.error( err ); console.error( err );
} ); } );
}, },
// TODO: Reload queue if shuffle enabled
control( action ) { control( action ) {
if ( action === 'play' ) { if ( action === 'play' ) {
if( !this.musicKit || !this.isPlaying ) {
this.musicKit.play().then( () => {
this.sendUpdate( 'pos' );
} ).catch( err => {
console.log( 'player failed to start' );
console.log( err );
} );
} else {
this.musicKit.pause().then( () => {
this.musicKit.play().catch( err => {
console.log( 'player failed to start' );
console.log( err );
} );
} );
}
this.isPlaying = true; this.isPlaying = true;
this.musicKit.player.play().catch( err => {
console.log( 'player failed to start' );
console.log( err );
} );
try { try {
clearInterval( this.progressTracker ); clearInterval( this.progressTracker );
} catch( err ) {}; } catch( err ) {};
this.progressTracker = setInterval( () => { this.progressTracker = setInterval( () => {
this.pos = parseInt( this.musicKit.player.currentPlaybackTime ); this.pos = parseInt( this.musicKit.currentPlaybackTime );
const minuteCount = Math.floor( this.pos / 60 ); const minuteCount = Math.floor( this.pos / 60 );
this.playbackPosBeautified = minuteCount + ':'; this.playbackPosBeautified = minuteCount + ':';
@@ -277,7 +286,7 @@ const app = Vue.createApp( {
this.durationBeautified += secondCounts; this.durationBeautified += secondCounts;
} }
} }
}, 20 ); }, 200 );
this.sendUpdate( 'pos' ); this.sendUpdate( 'pos' );
this.sendUpdate( 'isPlaying' ); this.sendUpdate( 'isPlaying' );
} else if ( action === 'pause' ) { } else if ( action === 'pause' ) {
@@ -290,13 +299,13 @@ const app = Vue.createApp( {
this.isPlaying = false; this.isPlaying = false;
this.sendUpdate( 'isPlaying' ); this.sendUpdate( 'isPlaying' );
} else if ( action === 'replay10' ) { } else if ( action === 'replay10' ) {
this.musicKit.seekToTime( this.musicKit.player.currentPlaybackTime > 10 ? musicPlayer.player.currentPlaybackTime - 10 : 0 ); this.musicKit.seekToTime( this.musicKit.currentPlaybackTime > 10 ? musicPlayer.player.currentPlaybackTime - 10 : 0 );
this.pos = this.musicKit.player.currentPlaybackTime; this.pos = this.musicKit.currentPlaybackTime;
this.sendUpdate( 'pos' ); this.sendUpdate( 'pos' );
} else if ( action === 'forward10' ) { } else if ( action === 'forward10' ) {
if ( this.musicKit.player.currentPlaybackTime < ( this.playingSong.duration - 10 ) ) { if ( this.musicKit.currentPlaybackTime < ( this.playingSong.duration - 10 ) ) {
this.musicKit.seekToTime( this.musicKit.player.currentPlaybackTime + 10 ); this.musicKit.seekToTime( this.musicKit.currentPlaybackTime + 10 );
this.pos = this.musicKit.player.currentPlaybackTime; this.pos = this.musicKit.currentPlaybackTime;
this.sendUpdate( 'pos' ); this.sendUpdate( 'pos' );
// Get currently playing song and get duration from there // Get currently playing song and get duration from there
} else { } else {
@@ -314,47 +323,53 @@ const app = Vue.createApp( {
this.musicKit.seekToTime( 0 ); this.musicKit.seekToTime( 0 );
this.sendUpdate( 'pos' ); this.sendUpdate( 'pos' );
} else if ( action === 'next' ) { } else if ( action === 'next' ) {
this.sendUpdate( 'queuePos' ); this.musicKit.skipToNextItem().then( () => {
this.musicKit.skipToNextItem(); this.sendUpdate( 'queuePos' );
this.control( 'play' ); } );
} else if ( action === 'previous' ) { } else if ( action === 'previous' ) {
if ( this.pos > 3 ) { if ( this.pos > 3 ) {
this.pos = 0; this.pos = 0;
this.musicKit.seekToTime( 0 ); this.musicKit.seekToTime( 0 ).then( () => {
this.sendUpdate( 'pos' ); this.sendUpdate( 'pos' );
this.sendUpdate( 'queuePos' ); this.sendUpdate( 'queuePos' );
this.control( 'play' ); this.control( 'play' );
} );
} else { } else {
this.musicKit.skipToPreviousItem(); this.musicKit.skipToPreviousItem().then( () => {
this.control( 'play' ); this.sendUpdate( 'queuePos' );
} );
} }
} else if ( action === 'shuffleOff' ) { } else if ( action === 'shuffleOff' ) {
this.isShuffleEnabled = false; this.isShuffleEnabled = false;
this.musicKit.PlayerShuffleMode = 'off'; this.musicKit.shuffleMode = MusicKit.PlayerShuffleMode.off;
} else if ( action === 'shuffleOn' ) { } else if ( action === 'shuffleOn' ) {
this.musicKit.PlayerShuffleMode = 'songs'; this.musicKit.shuffleMode = MusicKit.PlayerShuffleMode.songs;
this.isShuffleEnabled = true; this.isShuffleEnabled = true;
} else if ( action === 'repeatOne' ) { } else if ( action === 'repeatOne' ) {
this.repeatMode = 'one'; this.repeatMode = 'one';
this.musicKit.PlayerRepeatMode = 'one'; this.musicKit.repeatMode = MusicKit.PlayerRepeatMode.one;
} else if ( action === 'repeatAll' ) { } else if ( action === 'repeatAll' ) {
this.musicKit.PlayerRepeatMode = 'all'; this.musicKit.repeatMode = MusicKit.PlayerRepeatMode.all;
this.repeatMode = 'all'; this.repeatMode = 'all';
} else if ( action === 'repeatOff' ) { } else if ( action === 'repeatOff' ) {
this.musicKit.PlayerRepeatMode = 'none'; this.musicKit.repeatMode = MusicKit.PlayerRepeatMode.none;
this.repeatMode = 'off'; this.repeatMode = 'off';
} }
}, },
play( song ) { play( song ) {
// TODO: Look into this... seems to cancel the playlist and then autoplay similar music
// TODO: Also look at stability of player
let foundSong = 0; let foundSong = 0;
for ( let s in this.songQueue ) { for ( let s in this.songQueue ) {
if ( this.songQueue[ s ] === song ) { if ( this.songQueue[ s ] === song ) {
foundSong = s; foundSong = s;
} }
} }
this.control( 'stop' ); this.musicKit.play().then( () => {
this.musicKit.player.changeToMediaAtIndex( foundSong ); this.musicKit.changeToMediaAtIndex( foundSong ).catch( ( err ) => {
this.control( 'play' ); console.log( err );
} );
} );
}, },
}, },
watch: { watch: {

File diff suppressed because one or more lines are too long