Compare commits

...
Sign in to create a new pull request.

2 commits

4 changed files with 83 additions and 12 deletions

View file

@ -31,6 +31,8 @@ public class ServersController : ControllerBase
var servers = await _serverService.GetServers(); var servers = await _serverService.GetServers();
_logger.LogDebug("Servers found: {}", servers.Length);
return Ok(servers); return Ok(servers);
} }

View file

@ -176,7 +176,7 @@ public class JellyfinApiClient
private string GetAuthHeader() private string GetAuthHeader()
{ {
var header = "Client=Test, Device=Test, DeviceId=Test, Version=1"; var header = "Client=JellyGlass, Device=JellyGlass, DeviceId=JellyGlass, Version=1";
if (_apiKey != String.Empty) if (_apiKey != String.Empty)
{ {

View file

@ -0,0 +1,43 @@
import axios, { AxiosError } from "axios";
const DeviceInfoString = "MediaBrowser Client=JellyGlass, Device=JellyGlass, DeviceId=JellyGlass, Version=1";
interface QuickConnectInfo {
Code: string;
Secret: string;
Authenticated: boolean
}
export interface QuickConnectAuth {
AccessToken: string;
}
export const BeginQuickConnect = async (url: string, callback: (apiToken: string) => void, isCancelled: () => boolean, onError: (e: AxiosError) => void): Promise<string> => {
const response = await axios.post<QuickConnectInfo>(`${url}/QuickConnect/Initiate`, {}, { headers: { Authorization: DeviceInfoString } });
setTimeout(() => { PollQuickConnect(url, response.data, callback, isCancelled, onError) });
return response.data.Code;
}
const PollQuickConnect = async (url: string, info: QuickConnectInfo, callback: (apiToken: string) => void, isCancelled: () => boolean, onError: (e: AxiosError) => void) => {
if (isCancelled()) {
return;
}
try {
const response = await axios.get<QuickConnectInfo>(`${url}/QuickConnect/Connect?secret=${info.Secret}`, { headers: { Authorization: DeviceInfoString } });
if (response.data.Authenticated) {
const authResponse = await axios.post<QuickConnectAuth>(`${url}/Users/AuthenticateWithQuickConnect`, { Secret: response.data.Secret })
callback(authResponse.data.AccessToken);
}
else {
setTimeout(() => { PollQuickConnect(url, response.data, callback, isCancelled, onError) }, 5000)
}
}
catch (e) {
console.log(e);
onError(e as AxiosError);
}
}

View file

@ -1,17 +1,20 @@
import { useEffect } from "react"; import { useEffect, useState } from "react";
import { Button, Form, Spinner, Table } from "react-bootstrap"; import { Button, Form, Modal, Spinner, Table } from "react-bootstrap";
import { AddServer, getServerList, RemoveServer, type Server } from "../../../Lib/Servers"; import { AddServer, getServerList, RemoveServer, type Server } from "../../../Lib/Servers";
import { useImmer } from "use-immer"; import { useImmer } from "use-immer";
import styles from "../Management.module.scss"; import styles from "../Management.module.scss";
import { BeginQuickConnect } from "../../../Lib/QuickConnect";
import type { AxiosError } from "axios";
const ServerManagement = () => { const ServerManagement = () => {
const [servers, setServers] = useImmer<Array<Server> | undefined>(undefined); const [servers, setServers] = useImmer<Array<Server> | undefined>(undefined);
const [isCancelled, setIsCancelled] = useState(false);
const [quickConnectCode, setQuickConnectCode] = useState<string | undefined>(undefined);
const [addServerInfo, setAddServerInfo] = useImmer({ const [addServerInfo, setAddServerInfo] = useImmer({
url: "", url: "",
owner: "", owner: "",
apiToken: ""
}); });
useEffect(() => { useEffect(() => {
@ -37,8 +40,25 @@ const ServerManagement = () => {
}) })
} }
const onServerAdd = () => { const onQuickConnect = () => {
AddServer(addServerInfo.owner, addServerInfo.url, addServerInfo.apiToken).then(result => { setIsCancelled(false);
BeginQuickConnect(addServerInfo.url, onServerAdd, () => isCancelled, onQuickConnectError).then(code => {
setQuickConnectCode(code);
}).catch(err => {
console.log(err);
alert(err);
});
}
const onQuickConnectError = (e: AxiosError) => {
setIsCancelled(true);
setQuickConnectCode(undefined);
alert(e);
}
const onServerAdd = (apiToken: string) => {
setQuickConnectCode(undefined);
AddServer(addServerInfo.owner, addServerInfo.url, apiToken).then(result => {
if (result.errored) { if (result.errored) {
alert("Server was added, but is not working. Check the logs for details"); alert("Server was added, but is not working. Check the logs for details");
} }
@ -52,7 +72,6 @@ const ServerManagement = () => {
} }
setAddServerInfo(draft => { setAddServerInfo(draft => {
draft.apiToken = "";
draft.owner = ""; draft.owner = "";
draft.url = ""; draft.url = "";
}); });
@ -74,11 +93,7 @@ const ServerManagement = () => {
<Form.Label>Url</Form.Label> <Form.Label>Url</Form.Label>
<Form.Control type="text" placeholder="Url" onChange={e => setAddServerInfo(draft => { draft.url = e.target.value })} value={addServerInfo.url} /> <Form.Control type="text" placeholder="Url" onChange={e => setAddServerInfo(draft => { draft.url = e.target.value })} value={addServerInfo.url} />
</Form.Group> </Form.Group>
<Form.Group className="mb-3"> <Button className={styles.formButton} onClick={() => onQuickConnect()}>Quick Connect</Button>
<Form.Label>ApiToken</Form.Label>
<Form.Control type="text" placeholder="ApiToken" onChange={e => setAddServerInfo(draft => { draft.apiToken = e.target.value })} value={addServerInfo.apiToken} />
</Form.Group>
<Button className={styles.formButton} onClick={() => onServerAdd()}>Add Server</Button>
</Form> </Form>
<Table className={styles.table} bordered striped> <Table className={styles.table} bordered striped>
<thead> <thead>
@ -107,6 +122,17 @@ const ServerManagement = () => {
} }
</tbody> </tbody>
</Table> </Table>
<Modal show={quickConnectCode !== undefined} onHide={() => { setQuickConnectCode(undefined); setIsCancelled(true); }}>
<Modal.Header>
<Modal.Title>Jellyfin quick connect code</Modal.Title>
</Modal.Header>
<Modal.Body>
Your quick connect code is {quickConnectCode}
</Modal.Body>
<Modal.Footer>
<Button onClick={() => { setQuickConnectCode(undefined); setIsCancelled(true); }}>Cancel</Button>
</Modal.Footer>
</Modal>
</> </>
) )
} }