first commit
Some checks failed
Build / run (push) Has been cancelled

This commit is contained in:
maher
2025-10-29 11:42:25 +01:00
commit 703f50a09d
4595 changed files with 385164 additions and 0 deletions

View File

@@ -0,0 +1,47 @@
import {useEffect, useRef, useState} from 'react';
import {usePlayerActions} from '@common/player/hooks/use-player-actions';
import {usePlayerStore} from '@common/player/hooks/use-player-store';
interface Props {
precision: 'ms' | 'seconds';
disabled?: boolean;
}
export function useCurrentTime(
{precision, disabled}: Props = {precision: 'ms', disabled: false},
) {
const timeRef = useRef(0);
const {subscribe, getCurrentTime} = usePlayerActions();
const providerKey = usePlayerStore(s =>
s.providerName && s.cuedMedia?.id
? `${s.providerName}+${s.cuedMedia.id}`
: null,
);
const [currentTime, setCurrentTime] = useState(() => getCurrentTime());
useEffect(() => {
let unsubscribe: () => void;
if (!disabled) {
unsubscribe = subscribe({
progress: ({currentTime}) => {
const time =
precision === 'ms' ? currentTime : Math.floor(currentTime);
if (timeRef.current !== time) {
setCurrentTime(time);
timeRef.current = time;
}
},
});
}
return () => unsubscribe?.();
}, [precision, subscribe, disabled]);
// update current time when media or provider changes
useEffect(() => {
if (providerKey) {
setCurrentTime(getCurrentTime());
}
}, [providerKey, getCurrentTime]);
return currentTime;
}

View File

@@ -0,0 +1,5 @@
import {usePlayerStore} from '@common/player/hooks/use-player-store';
export function useIsMediaCued(mediaId: string | number): boolean {
return usePlayerStore(s => s.cuedMedia?.id === mediaId);
}

View File

@@ -0,0 +1,14 @@
import {usePlayerStore} from '@common/player/hooks/use-player-store';
export function useIsMediaPlaying(
mediaId: string | number,
groupId?: string | number
): boolean {
return usePlayerStore(s => {
return (
s.isPlaying &&
s.cuedMedia?.id === mediaId &&
(!groupId || groupId === s.cuedMedia.groupId)
);
});
}

View File

@@ -0,0 +1,55 @@
import {useContext, useMemo} from 'react';
import {PlayerStoreContext} from '@common/player/player-context';
import {MediaItem} from '@common/player/media-item';
export type PlayerActions = ReturnType<typeof usePlayerActions>;
export function usePlayerActions() {
const store = useContext(PlayerStoreContext);
return useMemo(() => {
const s = store.getState();
const overrideQueueAndPlay = async (
mediaItems: MediaItem[],
queuePointer?: number
) => {
s.stop();
await s.overrideQueue(mediaItems, queuePointer);
return s.play();
};
return {
play: s.play,
playNext: s.playNext,
playPrevious: s.playPrevious,
pause: s.pause,
subscribe: s.subscribe,
emit: s.emit,
getCurrentTime: s.getCurrentTime,
seek: s.seek,
toggleRepeatMode: s.toggleRepeatMode,
toggleShuffling: s.toggleShuffling,
getState: store.getState,
setVolume: s.setVolume,
setMuted: s.setMuted,
appendToQueue: s.appendToQueue,
removeFromQueue: s.removeFromQueue,
enterFullscreen: s.enterFullscreen,
exitFullscreen: s.exitFullscreen,
toggleFullscreen: s.toggleFullscreen,
enterPip: s.enterPip,
exitPip: s.exitPip,
setTextTrackVisibility: s.setTextTrackVisibility,
setCurrentTextTrack: s.setCurrentTextTrack,
setCurrentAudioTrack: s.setCurrentAudioTrack,
setIsSeeking: s.setIsSeeking,
setControlsVisible: s.setControlsVisible,
cue: s.cue,
overrideQueueAndPlay,
overrideQueue: s.overrideQueue,
setPlaybackRate: s.setPlaybackRate,
setPlaybackQuality: s.setPlaybackQuality,
};
}, [store]);
}

View File

@@ -0,0 +1,29 @@
import {useCallback, useRef} from 'react';
import {usePlayerActions} from '@common/player/hooks/use-player-actions';
export function usePlayerClickHandler() {
const clickRef = useRef(0);
const player = usePlayerActions();
const togglePlay = useCallback(() => {
if (player.getState().isPlaying) {
player.pause();
} else {
player.play();
}
}, [player]);
return useCallback(() => {
if (!player.getState().providerReady) return;
clickRef.current += 1;
togglePlay();
if (clickRef.current === 1) {
setTimeout(() => {
if (clickRef.current > 1) {
player.toggleFullscreen();
}
clickRef.current = 0;
}, 300);
}
}, [player, togglePlay]);
}

View File

@@ -0,0 +1,29 @@
import {StoreApi} from 'zustand';
import {useContext} from 'react';
import {PlayerStoreContext} from '@common/player/player-context';
import {PlayerState} from '@common/player/state/player-state';
import {FullscreenSlice} from '@common/player/state/fullscreen/fullscreen-slice';
import {PipSlice} from '@common/player/state/pip/pip-slice';
import {useStoreWithEqualityFn} from 'zustand/traditional';
type ExtractState<S> = S extends {
getState: () => infer T;
}
? T
: never;
type UsePlayerStore = {
(): ExtractState<StoreApi<PlayerState>>;
<U>(
selector: (
state: ExtractState<StoreApi<PlayerState & FullscreenSlice & PipSlice>>
) => U,
equalityFn?: (a: U, b: U) => boolean
): U;
};
// @ts-ignore
export const usePlayerStore: UsePlayerStore = (selector, equalityFn) => {
const store = useContext(PlayerStoreContext);
return useStoreWithEqualityFn(store, selector, equalityFn);
};