mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-02-14 08:12:22 +00:00
Implement enhanced server switching with credential persistence and auto-login
Co-authored-by: retardgerman <78982850+retardgerman@users.noreply.github.com>
This commit is contained in:
@@ -8,6 +8,13 @@ import { ListItem } from "./list/ListItem";
|
||||
|
||||
interface Server {
|
||||
address: string;
|
||||
serverName?: string;
|
||||
serverId?: string;
|
||||
lastUsername?: string;
|
||||
savedCredentials?: {
|
||||
username: string;
|
||||
password: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface PreviousServersListProps {
|
||||
@@ -26,6 +33,20 @@ export const PreviousServersList: React.FC<PreviousServersListProps> = ({
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const getServerDisplayName = (server: Server) => {
|
||||
if (server.serverName) {
|
||||
return `${server.serverName}`;
|
||||
}
|
||||
return server.address;
|
||||
};
|
||||
|
||||
const getServerSubtitle = (server: Server) => {
|
||||
if (server.lastUsername) {
|
||||
return `${server.address} • ${server.lastUsername}`;
|
||||
}
|
||||
return server.address;
|
||||
};
|
||||
|
||||
if (!previousServers.length) return null;
|
||||
|
||||
return (
|
||||
@@ -35,7 +56,9 @@ export const PreviousServersList: React.FC<PreviousServersListProps> = ({
|
||||
<ListItem
|
||||
key={s.address}
|
||||
onPress={() => onServerSelect(s)}
|
||||
title={s.address}
|
||||
title={getServerDisplayName(s)}
|
||||
subtitle={getServerSubtitle(s)}
|
||||
icon={s.savedCredentials ? "key" : "server"}
|
||||
showArrow
|
||||
/>
|
||||
))}
|
||||
|
||||
@@ -10,6 +10,13 @@ import { ListItem } from "../list/ListItem";
|
||||
|
||||
interface Server {
|
||||
address: string;
|
||||
serverName?: string;
|
||||
serverId?: string;
|
||||
lastUsername?: string;
|
||||
savedCredentials?: {
|
||||
username: string;
|
||||
password: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface Props extends ViewProps {}
|
||||
@@ -38,6 +45,23 @@ export const ServerSwitcher: React.FC<Props> = ({ ...props }) => {
|
||||
}
|
||||
};
|
||||
|
||||
const getServerDisplayName = (server: Server) => {
|
||||
if (server.serverName) {
|
||||
return `${server.serverName} (${server.address})`;
|
||||
}
|
||||
return server.address;
|
||||
};
|
||||
|
||||
const getServerSubtitle = (server: Server) => {
|
||||
if (server.lastUsername) {
|
||||
const hasCredentials = !!server.savedCredentials;
|
||||
return hasCredentials
|
||||
? `${server.lastUsername} • Auto-login available`
|
||||
: `Last user: ${server.lastUsername}`;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
if (!previousServers.length) {
|
||||
return (
|
||||
<View {...props}>
|
||||
@@ -55,7 +79,9 @@ export const ServerSwitcher: React.FC<Props> = ({ ...props }) => {
|
||||
<ListItem
|
||||
key={server.address}
|
||||
onPress={() => handleServerSwitch(server)}
|
||||
title={server.address}
|
||||
title={getServerDisplayName(server)}
|
||||
subtitle={getServerSubtitle(server)}
|
||||
icon={server.savedCredentials ? "key" : "server"}
|
||||
showArrow
|
||||
disabled={switchingServer === server.address}
|
||||
/>
|
||||
|
||||
@@ -30,6 +30,13 @@ import { store } from "@/utils/store";
|
||||
|
||||
interface Server {
|
||||
address: string;
|
||||
serverName?: string;
|
||||
serverId?: string;
|
||||
lastUsername?: string;
|
||||
savedCredentials?: {
|
||||
username: string;
|
||||
password: string;
|
||||
};
|
||||
}
|
||||
|
||||
export const apiAtom = atom<Api | null>(null);
|
||||
@@ -41,7 +48,8 @@ interface JellyfinContextValue {
|
||||
setServer: (server: Server) => Promise<void>;
|
||||
removeServer: () => void;
|
||||
switchServer: (server: Server) => Promise<void>;
|
||||
login: (username: string, password: string) => Promise<void>;
|
||||
addNewServer: (server: Server) => Promise<void>;
|
||||
login: (username: string, password: string, saveCredentials?: boolean) => Promise<void>;
|
||||
logout: () => Promise<void>;
|
||||
initiateQuickConnect: () => Promise<string | undefined>;
|
||||
}
|
||||
@@ -181,6 +189,21 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
||||
|
||||
if (!apiInstance?.basePath) throw new Error("Failed to connect");
|
||||
|
||||
// Get server info to obtain serverId and serverName
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${server.address}/System/Info/Public`,
|
||||
{ mode: "cors" }
|
||||
);
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
server.serverId = data.Id;
|
||||
server.serverName = data.ServerName;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("Could not get server info:", error);
|
||||
}
|
||||
|
||||
setApi(apiInstance);
|
||||
storage.set("serverUrl", server.address);
|
||||
},
|
||||
@@ -216,9 +239,11 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
||||
mutationFn: async ({
|
||||
username,
|
||||
password,
|
||||
saveCredentials = true,
|
||||
}: {
|
||||
username: string;
|
||||
password: string;
|
||||
saveCredentials?: boolean;
|
||||
}) => {
|
||||
if (!api || !jellyfin) throw new Error("API not initialized");
|
||||
|
||||
@@ -231,6 +256,26 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
||||
setApi(jellyfin.createApi(api?.basePath, auth.data?.AccessToken));
|
||||
storage.set("token", auth.data?.AccessToken);
|
||||
|
||||
// Save credentials to the current server if requested
|
||||
if (saveCredentials && api.basePath) {
|
||||
const previousServers = JSON.parse(
|
||||
storage.getString("previousServers") || "[]",
|
||||
) as Server[];
|
||||
|
||||
const updatedServers = previousServers.map((server) => {
|
||||
if (server.address === api.basePath) {
|
||||
return {
|
||||
...server,
|
||||
lastUsername: username,
|
||||
savedCredentials: { username, password }
|
||||
};
|
||||
}
|
||||
return server;
|
||||
});
|
||||
|
||||
storage.set("previousServers", JSON.stringify(updatedServers));
|
||||
}
|
||||
|
||||
const recentPluginSettings = await refreshStreamyfinPluginSettings();
|
||||
if (recentPluginSettings?.jellyseerrServerUrl?.value) {
|
||||
const jellyseerrApi = new JellyseerrApi(
|
||||
@@ -300,9 +345,28 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
||||
|
||||
const switchServerMutation = useMutation({
|
||||
mutationFn: async (server: Server) => {
|
||||
// First logout the current user
|
||||
// Get current server info for comparison
|
||||
const currentServerId = await getCurrentServerId();
|
||||
|
||||
// If switching to same server (different URL), try auto-login with saved credentials
|
||||
if (server.serverId && server.serverId === currentServerId && server.savedCredentials) {
|
||||
try {
|
||||
// Just set the new server without logging out
|
||||
await setServerMutation.mutateAsync(server);
|
||||
// Auto-login with saved credentials
|
||||
await loginMutation.mutateAsync({
|
||||
username: server.savedCredentials.username,
|
||||
password: server.savedCredentials.password,
|
||||
saveCredentials: false, // Don't save again
|
||||
});
|
||||
return;
|
||||
} catch (error) {
|
||||
console.warn("Auto-login failed, falling back to manual login:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// For different servers or if auto-login fails, do the normal logout → set server flow
|
||||
await logoutMutation.mutateAsync();
|
||||
// Then set the new server
|
||||
await setServerMutation.mutateAsync(server);
|
||||
},
|
||||
onError: (error) => {
|
||||
@@ -310,6 +374,59 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
||||
},
|
||||
});
|
||||
|
||||
const addNewServerMutation = useMutation({
|
||||
mutationFn: async (server: Server) => {
|
||||
// Add a new server to the list without switching to it
|
||||
const previousServers = JSON.parse(
|
||||
storage.getString("previousServers") || "[]",
|
||||
) as Server[];
|
||||
|
||||
// Get server info first
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${server.address}/System/Info/Public`,
|
||||
{ mode: "cors" }
|
||||
);
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
server.serverId = data.Id;
|
||||
server.serverName = data.ServerName;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("Could not get server info:", error);
|
||||
}
|
||||
|
||||
const updatedServers = [
|
||||
server,
|
||||
...previousServers.filter((s: Server) => s.address !== server.address),
|
||||
];
|
||||
storage.set(
|
||||
"previousServers",
|
||||
JSON.stringify(updatedServers.slice(0, 5)),
|
||||
);
|
||||
},
|
||||
onError: (error) => {
|
||||
console.error("Failed to add new server:", error);
|
||||
},
|
||||
});
|
||||
|
||||
const getCurrentServerId = async (): Promise<string | null> => {
|
||||
if (!api?.basePath) return null;
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${api.basePath}/System/Info/Public`,
|
||||
{ mode: "cors" }
|
||||
);
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
return data.Id;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("Could not get current server ID:", error);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
const [initialLoaded, setInitialLoaded] = useState(false);
|
||||
|
||||
@@ -354,8 +471,9 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
||||
setServer: (server) => setServerMutation.mutateAsync(server),
|
||||
removeServer: () => removeServerMutation.mutateAsync(),
|
||||
switchServer: (server) => switchServerMutation.mutateAsync(server),
|
||||
login: (username, password) =>
|
||||
loginMutation.mutateAsync({ username, password }),
|
||||
addNewServer: (server) => addNewServerMutation.mutateAsync(server),
|
||||
login: (username, password, saveCredentials = true) =>
|
||||
loginMutation.mutateAsync({ username, password, saveCredentials }),
|
||||
logout: () => logoutMutation.mutateAsync(),
|
||||
initiateQuickConnect,
|
||||
};
|
||||
|
||||
@@ -35,7 +35,9 @@
|
||||
"servers": "Servers",
|
||||
"quick_switch": "Quick Switch Servers",
|
||||
"switch_server": "Switch Server",
|
||||
"no_previous_servers": "No previous servers available"
|
||||
"no_previous_servers": "No previous servers available",
|
||||
"add_new_server": "Add New Server",
|
||||
"auto_login_available": "Auto-login available"
|
||||
},
|
||||
"home": {
|
||||
"no_internet": "No Internet",
|
||||
|
||||
Reference in New Issue
Block a user