mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-01-15 23:59:08 +00:00
@@ -1,15 +1,18 @@
|
||||
import { useInterval } from "@/hooks/useInterval";
|
||||
import { Api, Jellyfin } from "@jellyfin/sdk";
|
||||
import { UserDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||
import axios from "axios";
|
||||
import axios, { AxiosError } from "axios";
|
||||
import { router, useSegments } from "expo-router";
|
||||
import { atom, useAtom } from "jotai";
|
||||
import React, {
|
||||
createContext,
|
||||
ReactNode,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from "react";
|
||||
import { Platform } from "react-native";
|
||||
@@ -29,6 +32,7 @@ interface JellyfinContextValue {
|
||||
removeServer: () => void;
|
||||
login: (username: string, password: string) => Promise<void>;
|
||||
logout: () => Promise<void>;
|
||||
initiateQuickConnect: () => Promise<string | undefined>;
|
||||
}
|
||||
|
||||
const JellyfinContext = createContext<JellyfinContextValue | undefined>(
|
||||
@@ -51,7 +55,6 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
||||
}) => {
|
||||
const [jellyfin, setJellyfin] = useState<Jellyfin | undefined>(undefined);
|
||||
const [deviceId, setDeviceId] = useState<string | undefined>(undefined);
|
||||
const [isConnected, setIsConnected] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
@@ -69,6 +72,88 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
||||
|
||||
const [api, setApi] = useAtom(apiAtom);
|
||||
const [user, setUser] = useAtom(userAtom);
|
||||
const [isPolling, setIsPolling] = useState<boolean>(false);
|
||||
const [secret, setSecret] = useState<string | null>(null);
|
||||
|
||||
const headers = useMemo(() => {
|
||||
if (!deviceId) return {};
|
||||
return {
|
||||
authorization: `MediaBrowser Client="Streamyfin", Device=${
|
||||
Platform.OS === "android" ? "Android" : "iOS"
|
||||
}, DeviceId="${deviceId}", Version="0.8.4"`,
|
||||
};
|
||||
}, [deviceId]);
|
||||
|
||||
const initiateQuickConnect = useCallback(async () => {
|
||||
if (!api || !deviceId) return;
|
||||
try {
|
||||
const response = await api.axiosInstance.post(
|
||||
api.basePath + "/QuickConnect/Initiate",
|
||||
null,
|
||||
{
|
||||
headers,
|
||||
}
|
||||
);
|
||||
if (response?.status === 200) {
|
||||
setSecret(response?.data?.Secret);
|
||||
setIsPolling(true);
|
||||
console.log("Initiating quick connect");
|
||||
return response.data?.Code;
|
||||
} else {
|
||||
throw new Error("Failed to initiate quick connect");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
}, [api, deviceId, headers]);
|
||||
|
||||
const pollQuickConnect = useCallback(async () => {
|
||||
if (!api || !secret) return;
|
||||
|
||||
try {
|
||||
const response = await api.axiosInstance.get(
|
||||
`${api.basePath}/QuickConnect/Connect?Secret=${secret}`
|
||||
);
|
||||
|
||||
console.log("Polling quick connect");
|
||||
if (response.status === 200) {
|
||||
if (response.data.Authenticated) {
|
||||
setIsPolling(false);
|
||||
|
||||
const authResponse = await api.axiosInstance.post(
|
||||
api.basePath + "/Users/AuthenticateWithQuickConnect",
|
||||
{
|
||||
secret,
|
||||
},
|
||||
{
|
||||
headers,
|
||||
}
|
||||
);
|
||||
|
||||
const { AccessToken, User } = authResponse.data;
|
||||
api.accessToken = AccessToken;
|
||||
console.log("Quick connect authenticated", AccessToken, User.Id);
|
||||
setUser(User);
|
||||
await AsyncStorage.setItem("token", AccessToken);
|
||||
await AsyncStorage.setItem("user", JSON.stringify(User));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} catch (error) {
|
||||
if (error instanceof AxiosError && error.response?.status === 400) {
|
||||
setIsPolling(false);
|
||||
setSecret(null);
|
||||
throw new Error("The code has expired. Please try again.");
|
||||
} else {
|
||||
console.error("Error polling Quick Connect:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}, [api, secret, headers]);
|
||||
|
||||
useInterval(pollQuickConnect, isPolling ? 1000 : null);
|
||||
|
||||
const discoverServers = async (url: string): Promise<Server[]> => {
|
||||
const servers = await jellyfin?.discovery.getRecommendedServerCandidates(
|
||||
@@ -199,6 +284,7 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
||||
login: (username, password) =>
|
||||
loginMutation.mutateAsync({ username, password }),
|
||||
logout: () => logoutMutation.mutateAsync(),
|
||||
initiateQuickConnect,
|
||||
};
|
||||
|
||||
useProtectedRoute(user, isLoading || isFetching);
|
||||
|
||||
Reference in New Issue
Block a user