mirror of
https://github.com/janishutz/MusicPlayer.git
synced 2026-09-10 14:25:24 +02:00
feat: add songs via Apple Music
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
addSongList,
|
||||
clearQueue,
|
||||
shuffleList
|
||||
} from './playlists/add';
|
||||
import {
|
||||
@@ -117,7 +118,7 @@ const getSources = (): string[] => {
|
||||
};
|
||||
|
||||
const addSongFromSource = async ( source: string ) => {
|
||||
addSongList( await sources[source]!.addSongsFromThisSource() );
|
||||
sources[source]!.addSongsFromThisSource( addSongList );
|
||||
};
|
||||
|
||||
export default {
|
||||
@@ -133,6 +134,7 @@ export default {
|
||||
addSongFromSource,
|
||||
next,
|
||||
prev,
|
||||
clearQueue,
|
||||
queue,
|
||||
queueIdx,
|
||||
duration,
|
||||
|
||||
+1
-1
@@ -94,5 +94,5 @@ export interface PlayerSourcePlugin {
|
||||
* Called when user adds another song to the playlist via this source.
|
||||
* You may use the provided interface elements (search bar and popups) to e.g. ask if user wants to use a playlist, album, etc
|
||||
*/
|
||||
'addSongsFromThisSource': () => Promise<Song[]>;
|
||||
'addSongsFromThisSource': ( cb: ( songs: Song[] ) => void ) => void;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
};
|
||||
@@ -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
|
||||
};
|
||||
};
|
||||
|
||||
@@ -11,6 +11,12 @@ import type {
|
||||
import type {
|
||||
RepeatMode
|
||||
} from '../dtype/player';
|
||||
import {
|
||||
useLocalPlayer
|
||||
} from './plugins/local';
|
||||
import {
|
||||
useMusicKit
|
||||
} from './plugins/musickit';
|
||||
|
||||
export const sources: {
|
||||
[key: string]: PlayerSourcePlugin
|
||||
@@ -29,3 +35,19 @@ export const isPlaying = ref( false );
|
||||
export const shuffle = ref( false );
|
||||
|
||||
export const repeat: Ref<RepeatMode> = ref( 'off' );
|
||||
|
||||
const initSources = async () => {
|
||||
try {
|
||||
sources['applemusic'] = await useMusicKit();
|
||||
} catch {
|
||||
console.warn( 'MusicKitJS intialization failed' );
|
||||
}
|
||||
|
||||
try {
|
||||
sources['local'] = await useLocalPlayer();
|
||||
} catch {
|
||||
console.warn( 'Local Player intialization failed' );
|
||||
}
|
||||
};
|
||||
|
||||
initSources();
|
||||
|
||||
Reference in New Issue
Block a user