mirror of
https://github.com/janishutz/MusicPlayer.git
synced 2026-09-10 14:25:24 +02:00
feat: redesign main page, get player working (mostly) with Apple Music source
This commit is contained in:
@@ -23,6 +23,9 @@ import {
|
||||
import type {
|
||||
RepeatMode
|
||||
} from '../dtype/player';
|
||||
import type {
|
||||
Song
|
||||
} from '../dtype/playlist';
|
||||
import {
|
||||
playIndex
|
||||
} from './playlists';
|
||||
@@ -118,12 +121,29 @@ const getSources = (): string[] => {
|
||||
return Object.keys( sources );
|
||||
};
|
||||
|
||||
const addToSongList = ( songs: Song[] ) => {
|
||||
addSongList( songs );
|
||||
|
||||
if ( currentSource.value === '' ) {
|
||||
playIndex( 0 );
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Add songs to the playlist from given source
|
||||
* @param source - The ID of the source to add from
|
||||
*/
|
||||
const addSongFromSource = async ( source: string ) => {
|
||||
sources[source]!.addSongsFromThisSource( addSongList );
|
||||
const addSongFromSource = async ( source: string ): Promise<boolean> => {
|
||||
if ( sources[source]!.authorized.value ) {
|
||||
sources[source]!.addSongsFromThisSource( addToSongList );
|
||||
} else {
|
||||
// TODO: Make this interact with user
|
||||
sources[source]!.login!();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
export default {
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
import {
|
||||
currentSource,
|
||||
isPlaying,
|
||||
queue,
|
||||
queueIdx,
|
||||
rawQueue,
|
||||
shuffle
|
||||
shuffle,
|
||||
sources
|
||||
} from '../state';
|
||||
import {
|
||||
duration,
|
||||
playbackPercentage
|
||||
} from '../status-tracking';
|
||||
import type {
|
||||
Song
|
||||
} from '@/ts/dtype/playlist';
|
||||
@@ -23,6 +30,10 @@ export const addSongList = ( songs: Song[], first: boolean = false ) => {
|
||||
export const clearQueue = () => {
|
||||
queue.value = [];
|
||||
rawQueue.value = [];
|
||||
sources[currentSource.value]?.stop();
|
||||
duration.value = -1;
|
||||
playbackPercentage.value = 1;
|
||||
isPlaying.value = false;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
currentSource,
|
||||
isPlaying,
|
||||
queue,
|
||||
queueIdx,
|
||||
repeat,
|
||||
@@ -21,9 +22,15 @@ export const playIndex = async ( idx: number ) => {
|
||||
idx = repeat.value === 'all' ? queue.value.length - 1 : -1;
|
||||
}
|
||||
|
||||
if ( idx < 0 ) return false;
|
||||
|
||||
sources[currentSource.value]?.stop();
|
||||
|
||||
if ( idx < 0 ) {
|
||||
isPlaying.value = false;
|
||||
currentSource.value = '';
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
stopTracking();
|
||||
|
||||
const nextSong = queue.value[ idx ]!;
|
||||
@@ -33,4 +40,6 @@ export const playIndex = async ( idx: number ) => {
|
||||
sources[currentSource.value]?.playSong( nextSong.identifier );
|
||||
|
||||
startTracking();
|
||||
|
||||
isPlaying.value = true;
|
||||
};
|
||||
|
||||
+5
-2
@@ -1,3 +1,6 @@
|
||||
import type {
|
||||
Ref
|
||||
} from 'vue';
|
||||
import type {
|
||||
Song
|
||||
} from '@/ts/dtype/playlist';
|
||||
@@ -91,9 +94,9 @@ export interface PlayerSourcePlugin {
|
||||
'getDuration': () => number;
|
||||
|
||||
/**
|
||||
* Check if source is available for playback
|
||||
* Check if source is authorized
|
||||
*/
|
||||
'available': () => boolean;
|
||||
'authorized': Ref<boolean>;
|
||||
|
||||
/**
|
||||
* Called when user adds another song to the playlist via this source.
|
||||
|
||||
@@ -2,6 +2,9 @@ import type {
|
||||
PlayerSourcePlugin,
|
||||
PlayerSourcePluginInitializer
|
||||
} from '../interface';
|
||||
import {
|
||||
ref
|
||||
} from 'vue';
|
||||
|
||||
export const useLocalPlayer: PlayerSourcePluginInitializer = async (): Promise<PlayerSourcePlugin> => {
|
||||
const player = document.createElement( 'audio' );
|
||||
@@ -12,7 +15,7 @@ export const useLocalPlayer: PlayerSourcePluginInitializer = async (): Promise<P
|
||||
};
|
||||
|
||||
return {
|
||||
'available': () => true,
|
||||
'authorized': ref( true ),
|
||||
'id': 'local',
|
||||
'name': 'Local Disk',
|
||||
'play': player.play,
|
||||
|
||||
@@ -43,7 +43,7 @@ export const useMusicKit: PlayerSourcePluginInitializer = ( storefront: string =
|
||||
};
|
||||
|
||||
return {
|
||||
'available': controls.getLoggedIn,
|
||||
'authorized': controls.loggedIn,
|
||||
'play': controls.play,
|
||||
'pause': controls.pause,
|
||||
'stop': controls.stop,
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import type {
|
||||
MusicKitInstance
|
||||
} from 'musickitjs-v3-types/MusicKitInstance';
|
||||
import {
|
||||
ref
|
||||
} from 'vue';
|
||||
|
||||
export const musicKitPlayback = ( musickitInstance: MusicKitInstance ) => {
|
||||
const play = () => {
|
||||
@@ -27,16 +30,18 @@ export const musicKitPlayback = ( musickitInstance: MusicKitInstance ) => {
|
||||
};
|
||||
|
||||
const getPlaybackPos = (): number => {
|
||||
return musickitInstance.currentPlaybackProgress;
|
||||
return musickitInstance.currentPlaybackTime / musickitInstance.currentPlaybackDuration;
|
||||
};
|
||||
|
||||
const getDuration = (): number => {
|
||||
return musickitInstance.currentPlaybackDuration;
|
||||
};
|
||||
|
||||
const getLoggedIn = (): boolean => {
|
||||
return musickitInstance.isAuthorized;
|
||||
};
|
||||
const loggedIn = ref( musickitInstance.isAuthorized );
|
||||
|
||||
musickitInstance.addEventListener( 'authorizationStatusDidChange', () => {
|
||||
loggedIn.value = musickitInstance.isAuthorized;
|
||||
} );
|
||||
|
||||
return {
|
||||
play,
|
||||
@@ -46,6 +51,6 @@ export const musicKitPlayback = ( musickitInstance: MusicKitInstance ) => {
|
||||
stop,
|
||||
getPlaybackPos,
|
||||
getDuration,
|
||||
getLoggedIn
|
||||
loggedIn
|
||||
};
|
||||
};
|
||||
|
||||
+11
-1
@@ -18,9 +18,19 @@ export interface AppleMusicSongData {
|
||||
'attributes': {
|
||||
'albumName': string;
|
||||
'artistName': string;
|
||||
'artwork': Artwork,
|
||||
'artwork': Artwork;
|
||||
'name': string;
|
||||
'genreNames': string[];
|
||||
'durationInMillis': number;
|
||||
}
|
||||
}
|
||||
|
||||
export interface AppleMusicPlaylistData {
|
||||
'id': string;
|
||||
'type': string;
|
||||
'href': string;
|
||||
'attributes': {
|
||||
'artwork': Artwork;
|
||||
'name': string;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import type {
|
||||
AppleMusicPlaylistData
|
||||
} from './dtype';
|
||||
import type {
|
||||
MusicKitInstance
|
||||
} from 'musickitjs-v3-types/MusicKitInstance';
|
||||
@@ -6,10 +9,33 @@ import type {
|
||||
} from '@/ts/dtype/playlist';
|
||||
|
||||
export const searchPlaylists = async ( instance: MusicKitInstance, cb: ( songs: Song[] ) => void ) => {
|
||||
// TODO: Get all playlists
|
||||
let playlists: Song[] = [];
|
||||
let filtered: Song[] = [];
|
||||
|
||||
const search = async ( term: string ): Promise<Song[]> => {
|
||||
return [];
|
||||
if ( playlists.length === 0 ) {
|
||||
playlists = ( ( await instance.api.music( '/v1/me/library/playlists', {
|
||||
'limit': 100
|
||||
} ) ).data as {
|
||||
'data': AppleMusicPlaylistData[]
|
||||
} ).data.map( val => {
|
||||
return {
|
||||
'artist': 'Playlist',
|
||||
'artwork': val.attributes.artwork ? window.MusicKit.formatArtworkURL( val.attributes.artwork, 1000, 100 ) : '',
|
||||
'duration': 0,
|
||||
'identifier': val.id,
|
||||
'additional-info': '',
|
||||
'name': val.attributes.name,
|
||||
'source': 'applemusic'
|
||||
};
|
||||
} );
|
||||
}
|
||||
|
||||
filtered = playlists.filter( val => {
|
||||
return val.name.includes( term );
|
||||
} );
|
||||
|
||||
return filtered;
|
||||
};
|
||||
|
||||
const addSelected = async ( idx: number ) => {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type {
|
||||
AppleMusicApiSearchResult
|
||||
AppleMusicApiSearchResult,
|
||||
AppleMusicSongData
|
||||
} from './dtype';
|
||||
import type {
|
||||
MusicKitInstance
|
||||
@@ -16,7 +17,16 @@ export const searchSongs = async ( instance: MusicKitInstance, cb: ( songs: Song
|
||||
'term': term,
|
||||
'types': [ 'songs' ]
|
||||
};
|
||||
const results = ( ( await instance.api.music( '/v1/catalog/{{storefrontId}}/search', params ) ).data as AppleMusicApiSearchResult ).results.songs.data;
|
||||
|
||||
let results: AppleMusicSongData[] = [];
|
||||
|
||||
try {
|
||||
results = ( ( await instance.api.music( '/v1/catalog/{{storefrontId}}/search', params ) ).data as AppleMusicApiSearchResult ).results.songs.data;
|
||||
} catch {
|
||||
console.debug( 'Failed results: got', results );
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
songs = [];
|
||||
|
||||
|
||||
@@ -24,12 +24,15 @@ export const startTracking = () => {
|
||||
}
|
||||
|
||||
duration.value = sources[currentSource.value]?.getDuration() ?? -1;
|
||||
setTimeout( () => {
|
||||
duration.value = sources[currentSource.value]?.getDuration() ?? -1;
|
||||
}, 2000 );
|
||||
};
|
||||
|
||||
const tracker = () => {
|
||||
playbackPercentage.value = sources[currentSource.value]?.getPlaybackPos() ?? -1;
|
||||
|
||||
if ( playbackPercentage.value > duration.value ) {
|
||||
if ( playbackPercentage.value > 0.995 && duration.value > 0 ) {
|
||||
if ( repeat.value === 'one' ) {
|
||||
sources[currentSource.value]?.seekTo( 0 );
|
||||
} else {
|
||||
@@ -42,8 +45,9 @@ const tracker = () => {
|
||||
export const stopTracking = () => {
|
||||
try {
|
||||
clearInterval( interval );
|
||||
interval = -1;
|
||||
} catch {
|
||||
// empty
|
||||
}
|
||||
|
||||
interval = -1;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user