OPUS сборка

This commit is contained in:
RoyceDa
2026-04-10 17:54:48 +02:00
parent b596d36543
commit ba12db3c72
2 changed files with 50 additions and 13 deletions

View File

@@ -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}