feat: add songs via Apple Music

This commit is contained in:
2026-09-01 12:51:39 +02:00
parent a2b93e9624
commit 3a0f8210dd
33 changed files with 513 additions and 98 deletions
+7 -6
View File
@@ -1,8 +1,10 @@
import 'musickitjs-v3-types';
import type {
PlayerSourcePlugin,
PlayerSourcePluginInitializer
} from '../interface';
import type {
Song
} from '@/ts/dtype/playlist';
import {
addFromAppleMusic
} from './search';
@@ -13,7 +15,7 @@ import {
export const useMusicKit: PlayerSourcePluginInitializer = ( storefront: string = 'ch' ): Promise<PlayerSourcePlugin> => {
return new Promise( ( resolve, reject ) => {
const init = async ( storefront: string ): Promise<PlayerSourcePlugin> => {
const res = await fetch( import.meta.env.VITE_BACKEND_URL + '/', {
const res = await fetch( import.meta.env.VITE_BACKEND_URL + '/dev-token', {
'credentials': 'include'
} );
@@ -23,8 +25,8 @@ export const useMusicKit: PlayerSourcePluginInitializer = ( storefront: string =
'developerToken': token,
'app': {
'name': 'MusicPlayer',
'build': '4',
'icon': 'https://music.janishutz.com/logo.jpg'
'build': '4'
// 'icon': 'https://music.janishutz.com/logo.jpg'
},
'storefrontId': storefront.toUpperCase()
} );
@@ -51,8 +53,7 @@ export const useMusicKit: PlayerSourcePluginInitializer = ( storefront: string =
'name': 'Apple Music',
'seekTo': controls.seekTo,
'login': login,
// TODO: Implement
'addSongsFromThisSource': async () => await addFromAppleMusic(),
'addSongsFromThisSource': ( cb: ( songs: Song[] ) => void ) => addFromAppleMusic( instance, cb ),
'loading': {
'requiresLocalFiles': false
}
@@ -1,10 +0,0 @@
import type {
MusicKitInstance
} from 'musickitjs-v3-types/MusicKitInstance';
export const searchAlbums = async ( instance: MusicKitInstance, term: string ) => {
const addSelected = ( idx: number ) => {
// TODO: Load album's songs and return them
Promise.resolve();
};
};
+26
View File
@@ -0,0 +1,26 @@
import type {
Artwork
} from 'musickitjs-v3-types/enums';
export interface AppleMusicApiSearchResult {
'results': {
'songs': {
'data': AppleMusicSongData[],
'href': string;
}
};
}
export interface AppleMusicSongData {
'id': string,
'type': string;
'href': string;
'attributes': {
'albumName': string;
'artistName': string;
'artwork': Artwork,
'name': string;
'genreNames': string[];
'durationInMillis': number;
}
}
@@ -4,7 +4,28 @@ import type {
import type {
Song
} from '@/ts/dtype/playlist';
import {
openImportTypePicker
} from '@/composables/importTypePicker';
import {
searchSongs
} from './songs';
export const addFromAppleMusic = async ( instance: MusicKitInstance ): Promise<Song[]> => {
return [];
export const addFromAppleMusic = async ( instance: MusicKitInstance, cb: ( songs: Song[] ) => void ): Promise<void> => {
const songs = await searchSongs( instance, cb );
openImportTypePicker( [
{
'name': 'Songs',
'type': 'cloud',
'addSelected': songs.addSelected,
'search': songs.search
},
{
'name': 'Playlists',
'type': 'cloud',
'addSelected': songs.addSelected,
'search': songs.search
}
] );
};
@@ -1,10 +1,17 @@
import type {
Song
} from '@/ts/dtype/playlist';
import type {
MusicKitInstance
} from 'musickitjs-v3-types/MusicKitInstance';
export const searchPlaylists = async ( instance: MusicKitInstance, userPlaylists: boolean = true ) => {
const addSelected = ( idx: number ) => {
export const searchPlaylists = async ( instance: MusicKitInstance ) => {
// TODO: Get all playlists
const search = async () => {};
const addSelected = async ( idx: number ): Promise<Song[]> => {
// TODO: Load the playlist content and return songs
Promise.resolve();
return [];
};
};
@@ -1,20 +1,46 @@
import type {
AppleMusicApiSearchResult
} from './dtype';
import type {
MusicKitInstance
} from 'musickitjs-v3-types/MusicKitInstance';
import type {
Song
} from '@/ts/dtype/playlist';
export const seachSongs = async ( instance: MusicKitInstance ) => {
const search = async ( term: string ) => {
export const searchSongs = async ( instance: MusicKitInstance, cb: ( songs: Song[] ) => void ) => {
let songs: Song[] = [];
const search = async ( term: string ): Promise<Song[]> => {
const params = {
'term': term,
'types': [ 'songs' ],
'l': 'en-us'
'types': [ 'songs' ]
};
const results = ( ( await instance.api.music( '/v1/catalog/{{storefrontId}}/search', params ) ).data as AppleMusicApiSearchResult ).results.songs.data;
await instance.api.music( '/v1/catalog/{{storefrontId}}/search', params );
songs = [];
for ( const result of results ) {
songs.push( {
'duration': Math.round( result.attributes.durationInMillis / 1000 ),
'name': result.attributes.name,
'additional-info': '',
'artist': result.attributes.artistName,
'artwork': window.MusicKit.formatArtworkURL( result.attributes.artwork, 1000, 1000 ),
'identifier': result.id,
'source': 'applemusic'
} );
}
return songs;
};
const addSelected = ( idx: number ) => {
// TODO: Need to only return the selected song
Promise.resolve();
const addSelected = async ( idx: number ) => {
cb( [ songs[idx]! ] );
};
return {
search,
addSelected
};
};