some progress, interrupted because MusicKit bugs

This commit is contained in:
2024-06-11 13:41:03 +02:00
parent 17225d07bc
commit ce82014826
19 changed files with 1554 additions and 3 deletions

View File

@@ -5,6 +5,8 @@
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200">
<!-- TODO: Update URL -->
<script src="/musickit.js"></script>
<title>MusicPlayer</title>
</head>
<body>

View File

@@ -8,6 +8,7 @@
"name": "musicplayerv2-gui",
"version": "0.0.0",
"dependencies": {
"musickit-typescript": "^1.2.4",
"pinia": "^2.1.7",
"vue": "^3.4.15",
"vue-router": "^4.2.5"
@@ -2387,6 +2388,12 @@
"dev": true,
"license": "MIT"
},
"node_modules/musickit-typescript": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/musickit-typescript/-/musickit-typescript-1.2.4.tgz",
"integrity": "sha512-3+/20Pi2zOVAHfUFf631LU2NwaC/qEHBBksM+YQzQ/fff4tIMPX5WJ6We/WXmwTHkAkHIOEitJW4cRPnvVAq+A==",
"license": "MIT"
},
"node_modules/nanoid": {
"version": "3.3.7",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",

View File

@@ -12,6 +12,7 @@
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
},
"dependencies": {
"musickit-typescript": "^1.2.4",
"pinia": "^2.1.7",
"vue": "^3.4.15",
"vue-router": "^4.2.5"

File diff suppressed because one or more lines are too long

View File

@@ -11,7 +11,7 @@
<div class="controls-wrapper">
<span class="material-symbols-outlined controls next-previous" @click="control( 'previous' )" id="previous">skip_previous</span>
<span class="material-symbols-outlined controls forward-back" @click="control( 'back' )" :style="'rotate: -' + 360 * clickCountBack + 'deg;'">replay_10</span>
<span class="material-symbols-outlined controls" v-if="!isPlaying" @click="playPause()" id="play-pause">pause</span>
<span class="material-symbols-outlined controls" v-if="isPlaying" @click="playPause()" id="play-pause">pause</span>
<span class="material-symbols-outlined controls" v-else @click="playPause()" id="play-pause">play_arrow</span>
<span class="material-symbols-outlined controls forward-back" @click="control( 'forward' )" :style="'rotate: ' + 360 * clickCountForward + 'deg;'">forward_10</span>
<span class="material-symbols-outlined controls next-previous" @click="control( 'next' )" id="next">skip_next</span>
@@ -29,6 +29,7 @@
import { ref } from 'vue';
import playlistView from '@/components/playlistView.vue';
import MusicKitJSWrapper from '@/scripts/player';
const isPlaying = ref( false );
const repeatMode = ref( '' );
@@ -37,12 +38,18 @@
const clickCountForward = ref( 0 );
const clickCountBack = ref( 0 );
const isShowingFullScreenPlayer = ref( false );
const player = new MusicKitJSWrapper();
const emits = defineEmits( [ 'playerStateChange' ] );
const playPause = () => {
isPlaying.value = !isPlaying.value;
// TODO: Execute function on player
if ( isPlaying.value ) {
player.play();
} else {
player.pause();
}
}
const control = ( action: string ) => {
@@ -176,6 +183,11 @@
font-size: 2.5rem;
color: var( --primary-color );
cursor: pointer;
transition: all 0.5s ease-in-out;
}
.close-fullscreen:hover {
transform: scale( 1.25 );
}
.hidden .close-fullscreen {

View File

@@ -9,4 +9,6 @@ const app = createApp(App)
app.use(createPinia())
app.use(router)
localStorage.setItem( 'url', 'http://localhost:8081' );
app.mount('#app')

View File

@@ -0,0 +1,7 @@
const subscribe = ( handler: ( data: any ) => {} ): string => {
return '';
}
const unsubscribe = ( id: string ) => {
}

View File

@@ -0,0 +1,294 @@
interface Song {
/**
* The ID. Either the apple music ID, or if from local disk, an ID starting in local_
*/
id: string;
/**
* The cover image as a URL
*/
cover: string;
/**
* The artist of the song
*/
artist: string;
/**
* The name of the song
*/
title: string;
/**
* Duration of the song in milliseconds
*/
duration: number;
/**
* (OPTIONAL) The genres this song belongs to. Can be displayed on the showcase screen, but requires settings there
*/
genres?: string[];
/**
* (OPTIONAL) This will be displayed in brackets on the showcase screens
*/
additionalInfo?: string;
}
interface Config {
devToken: string;
userToken: string;
}
class MusicKitJSWrapper {
playingSongID: number;
playlist: Song[];
queue: number[];
config: Config;
musicKit: any;
isLoggedIn: boolean;
constructor () {
this.playingSongID = 0;
this.playlist = [];
this.queue = [];
this.config = {
devToken: '',
userToken: '',
};
this.isLoggedIn = false;
const self = this;
if ( !window.MusicKit ) {
document.addEventListener( 'musickitloaded', () => {
self.init();
} );
} else {
this.init();
}
}
logIn () {
if ( !this.musicKit.isAuthorized ) {
this.musicKit.authorize().then( () => {
this.isLoggedIn = true;
this.init();
} );
} else {
this.musicKit.authorize().then( () => {
this.isLoggedIn = true;
this.init();
} );
}
}
init () {
fetch( localStorage.getItem( 'url' ) + '/getAppleMusicDevToken', { credentials: 'include' } ).then( res => {
if ( res.status === 200 ) {
res.text().then( token => {
// MusicKit global is now defined
MusicKit.configure( {
developerToken: token,
app: {
name: 'MusicPlayer',
build: '2'
},
storefrontId: 'CH',
} ).then( () => {
this.config.devToken = token;
this.musicKit = MusicKit.getInstance();
if ( this.musicKit.isAuthorized ) {
this.isLoggedIn = true;
this.config.userToken = this.musicKit.musicUserToken;
}
this.musicKit.shuffleMode = MusicKit.PlayerShuffleMode.off;
this.apiGetRequest( 'https://api.music.apple.com/v1/me/library/playlists', this.handleAPIReturns );
} );
} );
}
} );
}
handleAPIReturns ( data: object ) {
console.log( data );
}
getUserPlaylists () {
}
apiGetRequest ( url: string, callback: ( data: object ) => void ) {
if ( this.config.devToken != '' && this.config.userToken != '' ) {
fetch( url, {
method: 'GET',
headers: {
'Authorization': `Bearer ${ this.config.devToken }`,
'Music-User-Token': this.config.userToken
}
} ).then( res => {
if ( res.status === 200 ) {
res.json().then( json => {
try {
callback( { 'status': 'ok', 'data': json } );
} catch( err ) { /* empty */}
} );
} else {
try {
callback( { 'status': 'error', 'error': res.status } );
} catch( err ) { /* empty */}
}
} );
} else return false;
}
/**
* Start playing the song at the current songID.
* @returns {void}
*/
play (): void {
}
/**
* Start playing the current song
* @returns {void}
*/
pause (): void {
}
/**
* Skip to the next song
* @returns {void}
*/
skip (): void {
}
/**
* Return to start of song, or if within four seconds of start of the song, go to previous song.
* @returns {void}
*/
previous (): void {
}
/**
* Go to a specific position in the song. If position > song duration, go to next song
* @param {number} pos The position in milliseconds since start of the song
* @returns {void}
*/
goToPos ( pos: number ): void {
}
// TODO: think about queue handling
/**
* Set, if the queue should be shuffled
* @param {boolean} enable True to enable shuffle, false to disable
* @returns {void}
*/
shuffle ( enable: boolean ): void {
}
/**
* Set the repeat mode
* @param {string} repeatType The repeat type. Can be '', '_on' or '_one_on'
* @returns {void}
*/
repeat ( repeatType: string ): void {
}
/**
* Set the playlist to play.
* @param {Song[]} pl Playlist to play. An array of songs
* @returns {void}
*/
setPlaylist ( pl: Song[] ): void {
}
/**
* Set which song (by Song-ID) to play.
* @param {string} id The song ID (apple music ID or internal ID, if from local drive)
* @returns {void}
*/
setCurrentlyPlayingSongID ( id: string ): void {
}
/**
* Insert a song into the currently playing playlist
* @param {Song} song A song using the Song object
* @param {number} pos Position in the queue to insert it into
* @returns {void}
*/
insertSong ( song: Song, pos: number ): void {
}
/**
* Remove a song from the queue
* @param {string} id Song ID to remove.
* @returns {void}
*/
removeSong ( id: string ): void {
}
/**
* Get the playlist, as it will play
* @returns {Song[]}
*/
getOrderedPlaylist (): Song[] {
return this.playlist;
}
/**
* Get the playlist, ignoring order specified by the queue.
* @returns {Song[]}
*/
getPlaylist (): Song[] {
return this.playlist;
}
/**
* Get the position of the playback head. Returns time in ms
* @returns {number}
*/
getPlaybackPos (): number {
return 0;
}
/**
* Returns the currently playing song object
* @returns {Song}
*/
getPlayingSong (): Song {
return this.playlist[ this.playingSongID ];
}
/**
* Returns the ID of the currently playing song
* @returns {string}
*/
getPlayingSongID (): string {
return this.playlist[ this.playingSongID ].id;
}
/**
* Returns the index in the playlist of the currently playing song
* @returns {number}
*/
getPlayingIndex (): number {
return this.playingSongID;
}
}
export default MusicKitJSWrapper;

View File

@@ -62,7 +62,7 @@
bottom: 10px;
left: 10px;
background-color: var( --secondary-color );
transition: all 1s;
transition: all 0.75s ease-in-out;
}
.full-screen-player {

View File

@@ -1,6 +1,6 @@
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
"include": ["env.d.ts", "src/**/*", "src/**/*.vue", "public/musickit.js"],
"exclude": ["src/**/__tests__/*"],
"compilerOptions": {
"composite": true,