Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import BugReportIcon from "@mui/icons-material/BugReport";
import FormatListNumberedIcon from "@mui/icons-material/FormatListNumbered";
import LinkIcon from "@mui/icons-material/Link";
import PowerSettingsNewIcon from "@mui/icons-material/PowerSettingsNew";
import SyncIcon from "@mui/icons-material/Sync";
import WifiIcon from "@mui/icons-material/Wifi";
import {
Alert,
Expand All @@ -34,6 +35,7 @@ import { SettingItem } from "./components/ui/SettingItem";
import { useNcmTheme } from "./hooks/useNcmTheme";
import {
autoConnectAtom,
autoReconnectAtom,
type ConnectionStatus,
connectionErrorAtom,
connectionStatusAtom,
Expand Down Expand Up @@ -104,6 +106,7 @@ export default function App() {
function Main() {
const [wsUrl, setWsUrl] = useAtom(wsUrlAtom);
const [autoConnect, setAutoConnect] = useAtom(autoConnectAtom);
const [autoReconnect, setAutoReconnect] = useAtom(autoReconnectAtom);
const [status, setStatus] = useAtom(connectionStatusAtom);
const [error, setError] = useAtom(connectionErrorAtom);
const [displayError, setDisplayError] = useState(error);
Expand Down Expand Up @@ -265,6 +268,18 @@ function Main() {
}
/>

<SettingItem
icon={<SyncIcon />}
title="断开自动重连"
description="连接断开后自动尝试重新连接(每3秒重试)"
action={
<Switch
checked={autoReconnect}
onChange={(_, checked) => setAutoReconnect(checked)}
/>
}
/>

<SettingItem
icon={<AccessTimeIcon />}
title="播放进度偏移量"
Expand Down
54 changes: 53 additions & 1 deletion src/components/headless/AmllWsClient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useAtom, useAtomValue, useSetAtom } from "jotai";
import { useCallback, useEffect, useRef } from "react";
import {
autoConnectAtom,
autoReconnectAtom,
connectionErrorAtom,
connectionStatusAtom,
lyricAtom,
Expand Down Expand Up @@ -37,10 +38,15 @@ import { AudioDataBus } from "./InfLinkBridge";
export function AmllWsClient() {
const wsRef = useRef<WebSocket | null>(null);
const hasAutoConnected = useRef(false);
const hasAttemptedConnect = useRef(false);
const reconnectTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const isManualDisconnect = useRef(false);
const autoReconnectRef = useRef(false);
const coverManagerRef = useRef(new CoverManager());

const wsUrl = useAtomValue(wsUrlAtom);
const autoConnect = useAtomValue(autoConnectAtom);
const autoReconnect = useAtomValue(autoReconnectAtom);
const [status, setStatus] = useAtom(connectionStatusAtom);
const setError = useSetAtom(connectionErrorAtom);

Expand All @@ -62,9 +68,44 @@ export function AmllWsClient() {
const setRepeatMode = useSetAtom(setRepeatModeAtom);
const setShuffleMode = useSetAtom(setShuffleModeAtom);

const clearReconnectTimer = useCallback(() => {
if (reconnectTimerRef.current) {
clearTimeout(reconnectTimerRef.current);
reconnectTimerRef.current = null;
}
}, []);

const scheduleReconnect = useCallback(() => {
if (!autoReconnectRef.current || isManualDisconnect.current) return;

clearReconnectTimer();

reconnectTimerRef.current = setTimeout(() => {
if (autoReconnectRef.current && !isManualDisconnect.current) {
setStatus("connecting");
}
}, 3000);
}, [setStatus, clearReconnectTimer]);

useEffect(() => {
autoReconnectRef.current = autoReconnect;

if (!autoReconnect) {
clearReconnectTimer();
} else if (
hasAttemptedConnect.current &&
(status === "disconnected" || status === "error")
) {
if (!isManualDisconnect.current) {
scheduleReconnect();
}
}
}, [autoReconnect, status, clearReconnectTimer, scheduleReconnect]);

useEffect(() => {
if (!hasAutoConnected.current && autoConnect && status === "disconnected") {
hasAutoConnected.current = true;
isManualDisconnect.current = false;
setStatus("connecting");
}
}, [autoConnect, status, setStatus]);
Expand All @@ -77,11 +118,15 @@ export function AmllWsClient() {

useEffect(() => {
if (status === "connecting" && !wsRef.current) {
isManualDisconnect.current = false;
hasAttemptedConnect.current = true;
const ws = new WebSocket(wsUrl);
wsRef.current = ws;

ws.onopen = () => {
clearReconnectTimer();
setStatus("connected");
setError("");
ws.send(JSON.stringify({ type: "initialize" }));
};

Expand Down Expand Up @@ -140,6 +185,7 @@ export function AmllWsClient() {
if (wsRef.current === ws) {
setStatus("disconnected");
wsRef.current = null;
scheduleReconnect();
}
};

Expand All @@ -148,9 +194,12 @@ export function AmllWsClient() {
setStatus("error");
setError("无法连接到 AMLL Player,请检查地址是否正确");
wsRef.current = null;
scheduleReconnect();
}
};
} else if (status === "disconnected" && wsRef.current) {
isManualDisconnect.current = true;
clearReconnectTimer();
wsRef.current.close();
wsRef.current = null;
}
Expand All @@ -159,6 +208,8 @@ export function AmllWsClient() {
wsUrl,
setStatus,
setError,
scheduleReconnect,
clearReconnectTimer,
play,
pause,
next,
Expand All @@ -171,12 +222,13 @@ export function AmllWsClient() {

useEffect(() => {
return () => {
clearReconnectTimer();
if (wsRef.current) {
wsRef.current.close();
wsRef.current = null;
}
};
}, []);
}, [clearReconnectTimer]);

useEffect(() => {
if (!songInfo || status !== "connected") return;
Expand Down
6 changes: 6 additions & 0 deletions src/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ export const autoConnectAtom = atomWithStorage(
false,
);

/** 是否在连接断开后自动重连 */
export const autoReconnectAtom = atomWithStorage(
"amll-ws-connector:autoReconnect",
true,
);

/** WebSocket 连接状态 */
export type ConnectionStatus =
| "disconnected"
Expand Down