27
common/resources/client/player/utils/guess-player-provider.ts
Executable file
27
common/resources/client/player/utils/guess-player-provider.ts
Executable file
@@ -0,0 +1,27 @@
|
||||
import {MediaItem} from '@common/player/media-item';
|
||||
import {IS_IOS} from '@common/utils/platform';
|
||||
|
||||
const hlsRegex = /\.(m3u8)($|\?)/i;
|
||||
const dashRegex = /\.(mpd)($|\?)/i;
|
||||
const audioRegex =
|
||||
/\.(m4a|mp4a|mpga|mp2|mp2a|mp3|m2a|m3a|wav|weba|aac|oga|spx|flac)($|\?)/i;
|
||||
const youtubeUrlRegex =
|
||||
/(?:youtu\.be|youtube|youtube\.com|youtube-nocookie\.com)\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=|)((?:\w|-){11})/;
|
||||
const youtubeIdRegex = /^((?:\w|-){11})$/;
|
||||
export function guessPlayerProvider(src: string): MediaItem['provider'] {
|
||||
if (youtubeUrlRegex.test(src) || youtubeIdRegex.test(src)) {
|
||||
return 'youtube';
|
||||
} else if (audioRegex.test(src)) {
|
||||
return 'htmlAudio';
|
||||
} else if (hlsRegex.test(src)) {
|
||||
if (IS_IOS) {
|
||||
return 'htmlVideo';
|
||||
} else {
|
||||
return 'hls';
|
||||
}
|
||||
} else if (dashRegex.test(src)) {
|
||||
return 'dash';
|
||||
} else {
|
||||
return 'htmlVideo';
|
||||
}
|
||||
}
|
||||
35
common/resources/client/player/utils/init-player-media-session.ts
Executable file
35
common/resources/client/player/utils/init-player-media-session.ts
Executable file
@@ -0,0 +1,35 @@
|
||||
import {Optional} from 'utility-types';
|
||||
import {PlayerState} from '@common/player/state/player-state';
|
||||
import {PlayerStoreOptions} from '@common/player/state/player-store-options';
|
||||
|
||||
export function initPlayerMediaSession(
|
||||
state: () => PlayerState,
|
||||
options: PlayerStoreOptions
|
||||
) {
|
||||
if ('mediaSession' in navigator) {
|
||||
const actionHandlers: Optional<
|
||||
Record<MediaSessionAction, MediaSessionActionHandler>
|
||||
> = {
|
||||
play: () => state().play(),
|
||||
pause: () => state().pause(),
|
||||
previoustrack: () => state().playPrevious(),
|
||||
nexttrack: () => state().playNext(),
|
||||
stop: () => state().stop(),
|
||||
seekbackward: () => state().seek(state().getCurrentTime() - 10),
|
||||
seekforward: () => state().seek(state().getCurrentTime() + 10),
|
||||
seekto: details => state().seek(details.seekTime || 0),
|
||||
};
|
||||
for (const key in actionHandlers) {
|
||||
try {
|
||||
navigator.mediaSession.setActionHandler(
|
||||
key as MediaSessionAction,
|
||||
actionHandlers[key as MediaSessionAction]!
|
||||
);
|
||||
} catch (error) {}
|
||||
}
|
||||
const cuedMedia = state().cuedMedia;
|
||||
if (cuedMedia) {
|
||||
options.setMediaSessionMetadata?.(cuedMedia);
|
||||
}
|
||||
}
|
||||
}
|
||||
6
common/resources/client/player/utils/is-same-media.ts
Executable file
6
common/resources/client/player/utils/is-same-media.ts
Executable file
@@ -0,0 +1,6 @@
|
||||
import {MediaItem} from '@common/player/media-item';
|
||||
|
||||
export function isSameMedia(a?: MediaItem, b?: MediaItem): boolean {
|
||||
if (!a || !b) return false;
|
||||
return a.id === b.id && a.groupId === b.groupId;
|
||||
}
|
||||
33
common/resources/client/player/utils/player-local-storage.ts
Executable file
33
common/resources/client/player/utils/player-local-storage.ts
Executable file
@@ -0,0 +1,33 @@
|
||||
import {getFromLocalStorage} from '@common/utils/hooks/local-storage';
|
||||
import {PlayerStoreOptions} from '@common/player/state/player-store-options';
|
||||
import {PlayerState} from '@common/player/state/player-state';
|
||||
|
||||
export interface PersistablePlayerState {
|
||||
muted?: PlayerState['muted'];
|
||||
repeat?: PlayerState['repeat'];
|
||||
shuffling?: PlayerState['shuffling'];
|
||||
volume?: PlayerState['volume'];
|
||||
}
|
||||
|
||||
export interface PlayerInitialData {
|
||||
state?: PersistablePlayerState;
|
||||
queue?: PlayerState['originalQueue'];
|
||||
cuedMediaId?: string | number;
|
||||
}
|
||||
|
||||
export function getPlayerStateFromLocalStorage(
|
||||
id: string | number,
|
||||
options?: PlayerStoreOptions
|
||||
): PlayerInitialData {
|
||||
const defaultVolume = options?.defaultVolume || 30;
|
||||
return {
|
||||
state: {
|
||||
muted: getFromLocalStorage(`player.${id}.muted`) ?? false,
|
||||
repeat: getFromLocalStorage(`player.${id}.repeat`) ?? 'all',
|
||||
shuffling: getFromLocalStorage(`player.${id}.shuffling`) ?? false,
|
||||
volume: getFromLocalStorage(`player.${id}.volume`) ?? defaultVolume,
|
||||
},
|
||||
queue: getFromLocalStorage(`player.${id}.queue`, []),
|
||||
cuedMediaId: getFromLocalStorage(`player.${id}.cuedMediaId`),
|
||||
};
|
||||
}
|
||||
19
common/resources/client/player/utils/reset-media-session.ts
Executable file
19
common/resources/client/player/utils/reset-media-session.ts
Executable file
@@ -0,0 +1,19 @@
|
||||
export function resetMediaSession() {
|
||||
if ('mediaSession' in navigator) {
|
||||
const actionHandlers: MediaSessionAction[] = [
|
||||
'play',
|
||||
'pause',
|
||||
'previoustrack',
|
||||
'nexttrack',
|
||||
'stop',
|
||||
'seekbackward',
|
||||
'seekforward',
|
||||
'seekto',
|
||||
];
|
||||
actionHandlers.forEach(action =>
|
||||
navigator.mediaSession.setActionHandler(action, null)
|
||||
);
|
||||
navigator.mediaSession.metadata = null;
|
||||
navigator.mediaSession.playbackState = 'none';
|
||||
}
|
||||
}
|
||||
3
common/resources/client/player/utils/youtube-id-from-src.ts
Executable file
3
common/resources/client/player/utils/youtube-id-from-src.ts
Executable file
@@ -0,0 +1,3 @@
|
||||
export function youtubeIdFromSrc(src: string) {
|
||||
return src.match(/((?:\w|-){11})/)?.[0];
|
||||
}
|
||||
Reference in New Issue
Block a user