OPUS сборка
This commit is contained in:
@@ -16,6 +16,7 @@ export interface PlayerContextValue {
|
||||
totalDuration: number;
|
||||
currentMessageId: string | null;
|
||||
lastMessageId: string | null;
|
||||
lastError: string | null;
|
||||
}
|
||||
|
||||
export const PlayerContext = createContext<PlayerContextValue | null>(null);
|
||||
@@ -40,6 +41,7 @@ export function PlayerProvider(props: PlayerProviderProps) {
|
||||
const [isPlaying, setIsPlaying] = useState(false);
|
||||
const [duration, setDurationState] = useState(0);
|
||||
const [totalDuration, setTotalDuration] = useState(0);
|
||||
const [lastError, setLastError] = useState<string | null>(null);
|
||||
|
||||
const [currentMessageId, setCurrentMessageId] = useState<string | null>(null);
|
||||
const [lastMessageId, setLastMessageId] = useState<string | null>(null);
|
||||
@@ -64,6 +66,22 @@ export function PlayerProvider(props: PlayerProviderProps) {
|
||||
setTotalDuration(safe);
|
||||
};
|
||||
|
||||
const decodeMediaError = (err: MediaError | null) => {
|
||||
if (!err) return "Unknown media error";
|
||||
switch (err.code) {
|
||||
case MediaError.MEDIA_ERR_ABORTED:
|
||||
return "Playback aborted";
|
||||
case MediaError.MEDIA_ERR_NETWORK:
|
||||
return "Network error while loading audio";
|
||||
case MediaError.MEDIA_ERR_DECODE:
|
||||
return "Audio decode error";
|
||||
case MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED:
|
||||
return "Audio source is not supported";
|
||||
default:
|
||||
return `Unknown media error (${err.code})`;
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const audio = audioRef.current;
|
||||
if (!audio) return;
|
||||
@@ -89,6 +107,7 @@ export function PlayerProvider(props: PlayerProviderProps) {
|
||||
if (isLoadingRef.current) return;
|
||||
if (isSeekingRef.current) return;
|
||||
if (rafTimeUpdateRef.current != null) return;
|
||||
|
||||
rafTimeUpdateRef.current = requestAnimationFrame(() => {
|
||||
rafTimeUpdateRef.current = null;
|
||||
if (!isLoadingRef.current && !isSeekingRef.current) {
|
||||
@@ -103,9 +122,7 @@ export function PlayerProvider(props: PlayerProviderProps) {
|
||||
const onSeeked = () => {
|
||||
if (isSeekingRef.current) {
|
||||
isSeekingRef.current = false;
|
||||
if (!isLoadingRef.current) {
|
||||
commitDuration(audio.currentTime || 0);
|
||||
}
|
||||
if (!isLoadingRef.current) commitDuration(audio.currentTime || 0);
|
||||
return;
|
||||
}
|
||||
if (isLoadingRef.current) return;
|
||||
@@ -113,9 +130,20 @@ export function PlayerProvider(props: PlayerProviderProps) {
|
||||
};
|
||||
|
||||
const onCanPlay = () => {
|
||||
if (isLoadingRef.current) {
|
||||
isLoadingRef.current = false;
|
||||
}
|
||||
if (isLoadingRef.current) isLoadingRef.current = false;
|
||||
};
|
||||
|
||||
const onError = (_e: Event) => {
|
||||
const message = decodeMediaError(audio.error);
|
||||
setLastError(message);
|
||||
|
||||
console.error("Audio playback error", {
|
||||
message,
|
||||
mediaError: audio.error,
|
||||
currentSrc: audio.currentSrc,
|
||||
readyState: audio.readyState,
|
||||
networkState: audio.networkState,
|
||||
});
|
||||
};
|
||||
|
||||
audio.addEventListener("play", onPlay);
|
||||
@@ -126,6 +154,7 @@ export function PlayerProvider(props: PlayerProviderProps) {
|
||||
audio.addEventListener("durationchange", onDurationChange);
|
||||
audio.addEventListener("seeked", onSeeked);
|
||||
audio.addEventListener("canplay", onCanPlay);
|
||||
audio.addEventListener("error", onError);
|
||||
|
||||
return () => {
|
||||
audio.removeEventListener("play", onPlay);
|
||||
@@ -136,6 +165,7 @@ export function PlayerProvider(props: PlayerProviderProps) {
|
||||
audio.removeEventListener("durationchange", onDurationChange);
|
||||
audio.removeEventListener("seeked", onSeeked);
|
||||
audio.removeEventListener("canplay", onCanPlay);
|
||||
audio.removeEventListener("error", onError);
|
||||
|
||||
if (rafTimeUpdateRef.current != null) {
|
||||
cancelAnimationFrame(rafTimeUpdateRef.current);
|
||||
@@ -162,6 +192,12 @@ export function PlayerProvider(props: PlayerProviderProps) {
|
||||
const el = audioRef.current;
|
||||
if (!el) return;
|
||||
|
||||
// чтобы не было warning о неиспользуемых args при строгих правилах
|
||||
void artist;
|
||||
void title;
|
||||
|
||||
setLastError(null);
|
||||
|
||||
if (objectUrlRef.current) {
|
||||
URL.revokeObjectURL(objectUrlRef.current);
|
||||
objectUrlRef.current = null;
|
||||
@@ -176,7 +212,6 @@ export function PlayerProvider(props: PlayerProviderProps) {
|
||||
isSeekingRef.current = false;
|
||||
|
||||
el.src = audioSrc;
|
||||
|
||||
durationRef.current = 0;
|
||||
|
||||
const msgId = messageId ?? null;
|
||||
@@ -185,6 +220,7 @@ export function PlayerProvider(props: PlayerProviderProps) {
|
||||
|
||||
isPlayingRef.current = true;
|
||||
setIsPlaying(true);
|
||||
|
||||
const prevDuration = durationRef.current;
|
||||
requestAnimationFrame(() => {
|
||||
if (durationRef.current === prevDuration) {
|
||||
@@ -192,9 +228,10 @@ export function PlayerProvider(props: PlayerProviderProps) {
|
||||
}
|
||||
});
|
||||
|
||||
void el.play().catch(() => {
|
||||
void el.play().catch((err) => {
|
||||
isLoadingRef.current = false;
|
||||
commitPlaying(false);
|
||||
setLastError(err instanceof Error ? err.message : "play() failed");
|
||||
});
|
||||
};
|
||||
|
||||
@@ -210,8 +247,9 @@ export function PlayerProvider(props: PlayerProviderProps) {
|
||||
|
||||
commitPlaying(true);
|
||||
|
||||
void el.play().catch(() => {
|
||||
void el.play().catch((err) => {
|
||||
commitPlaying(false);
|
||||
setLastError(err instanceof Error ? err.message : "resume() failed");
|
||||
});
|
||||
};
|
||||
|
||||
@@ -252,6 +290,7 @@ export function PlayerProvider(props: PlayerProviderProps) {
|
||||
totalDuration,
|
||||
currentMessageId,
|
||||
lastMessageId,
|
||||
lastError,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
|
||||
Reference in New Issue
Block a user