mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-01-15 23:59:08 +00:00
feat: remotecontrol (#705)
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { apiAtom, getOrSetDeviceId } from "@/providers/JellyfinProvider";
|
||||
import { getSessionApi } from "@jellyfin/sdk/lib/utils/api";
|
||||
import { useRouter } from "expo-router";
|
||||
import { useAtomValue } from "jotai";
|
||||
import React, {
|
||||
createContext,
|
||||
@@ -12,6 +13,12 @@ import React, {
|
||||
} from "react";
|
||||
import { AppState, type AppStateStatus } from "react-native";
|
||||
|
||||
interface WebSocketMessage {
|
||||
MessageType: string;
|
||||
Data: any;
|
||||
// Add other fields as needed
|
||||
}
|
||||
|
||||
interface WebSocketProviderProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
@@ -19,6 +26,9 @@ interface WebSocketProviderProps {
|
||||
interface WebSocketContextType {
|
||||
ws: WebSocket | null;
|
||||
isConnected: boolean;
|
||||
lastMessage: WebSocketMessage | null;
|
||||
sendMessage: (message: any) => void;
|
||||
clearLastMessage: () => void;
|
||||
}
|
||||
|
||||
const WebSocketContext = createContext<WebSocketContextType | null>(null);
|
||||
@@ -27,7 +37,8 @@ export const WebSocketProvider = ({ children }: WebSocketProviderProps) => {
|
||||
const api = useAtomValue(apiAtom);
|
||||
const [ws, setWs] = useState<WebSocket | null>(null);
|
||||
const [isConnected, setIsConnected] = useState(false);
|
||||
|
||||
const [lastMessage, setLastMessage] = useState<WebSocketMessage | null>(null);
|
||||
const router = useRouter();
|
||||
const deviceId = useMemo(() => {
|
||||
return getOrSetDeviceId();
|
||||
}, []);
|
||||
@@ -48,6 +59,7 @@ export const WebSocketProvider = ({ children }: WebSocketProviderProps) => {
|
||||
let keepAliveInterval: number | null = null;
|
||||
|
||||
newWebSocket.onopen = () => {
|
||||
console.log("WebSocket connection opened");
|
||||
setIsConnected(true);
|
||||
keepAliveInterval = setInterval(() => {
|
||||
if (newWebSocket.readyState === WebSocket.OPEN) {
|
||||
@@ -56,9 +68,23 @@ export const WebSocketProvider = ({ children }: WebSocketProviderProps) => {
|
||||
}, 30000);
|
||||
};
|
||||
|
||||
let reconnectAttempts = 0;
|
||||
const maxReconnectAttempts = 5;
|
||||
const reconnectDelay = 10000;
|
||||
|
||||
newWebSocket.onerror = (e) => {
|
||||
console.error("WebSocket error:", e);
|
||||
setIsConnected(false);
|
||||
|
||||
if (reconnectAttempts < maxReconnectAttempts) {
|
||||
reconnectAttempts++;
|
||||
setTimeout(() => {
|
||||
console.log(`WebSocket reconnect attempt ${reconnectAttempts}`);
|
||||
connectWebSocket();
|
||||
}, reconnectDelay);
|
||||
} else {
|
||||
console.warn("Max WebSocket reconnect attempts reached.");
|
||||
}
|
||||
};
|
||||
|
||||
newWebSocket.onclose = () => {
|
||||
@@ -67,7 +93,15 @@ export const WebSocketProvider = ({ children }: WebSocketProviderProps) => {
|
||||
}
|
||||
setIsConnected(false);
|
||||
};
|
||||
|
||||
newWebSocket.onmessage = (e) => {
|
||||
try {
|
||||
const message = JSON.parse(e.data);
|
||||
console.log("[WS] Received message:", message);
|
||||
setLastMessage(message); // Store the last message in context
|
||||
} catch (error) {
|
||||
console.error("Error parsing WebSocket message:", error);
|
||||
}
|
||||
};
|
||||
setWs(newWebSocket);
|
||||
|
||||
return () => {
|
||||
@@ -78,6 +112,41 @@ export const WebSocketProvider = ({ children }: WebSocketProviderProps) => {
|
||||
};
|
||||
}, [api, deviceId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!lastMessage) {
|
||||
return;
|
||||
}
|
||||
if (lastMessage.MessageType === "Play") {
|
||||
handlePlayCommand(lastMessage.Data);
|
||||
}
|
||||
}, [lastMessage, router]);
|
||||
|
||||
const handlePlayCommand = useCallback(
|
||||
(data: any) => {
|
||||
if (!data || !data.ItemIds || !data.ItemIds.length) {
|
||||
console.warn("[WS] Received Play command with no items");
|
||||
return;
|
||||
}
|
||||
|
||||
const itemId = data.ItemIds[0];
|
||||
console.log(`[WS] Handling Play command for item: ${itemId}`);
|
||||
|
||||
router.push({
|
||||
pathname: "/(auth)/player/direct-player",
|
||||
params: {
|
||||
itemId: itemId,
|
||||
playCommand: data.PlayCommand || "PlayNow",
|
||||
audioIndex: data.AudioStreamIndex?.toString(),
|
||||
subtitleIndex: data.SubtitleStreamIndex?.toString(),
|
||||
mediaSourceId: data.MediaSourceId || "",
|
||||
bitrateValue: "",
|
||||
offline: "false",
|
||||
},
|
||||
});
|
||||
},
|
||||
[router],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const cleanup = connectWebSocket();
|
||||
return cleanup;
|
||||
@@ -126,9 +195,23 @@ export const WebSocketProvider = ({ children }: WebSocketProviderProps) => {
|
||||
ws?.close();
|
||||
};
|
||||
}, [ws, connectWebSocket]);
|
||||
|
||||
const sendMessage = useCallback(
|
||||
(message: any) => {
|
||||
if (ws && isConnected) {
|
||||
ws.send(JSON.stringify(message));
|
||||
} else {
|
||||
console.warn("Cannot send message: WebSocket is not connected");
|
||||
}
|
||||
},
|
||||
[ws, isConnected],
|
||||
);
|
||||
const clearLastMessage = useCallback(() => {
|
||||
setLastMessage(null);
|
||||
}, []);
|
||||
return (
|
||||
<WebSocketContext.Provider value={{ ws, isConnected }}>
|
||||
<WebSocketContext.Provider
|
||||
value={{ ws, isConnected, lastMessage, sendMessage, clearLastMessage }}
|
||||
>
|
||||
{children}
|
||||
</WebSocketContext.Provider>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user