Tweak some things so that we can embed Web UI into Tauri
This commit is contained in:
parent
950ed816d1
commit
137d12cb9c
9 changed files with 105 additions and 44 deletions
|
|
@ -308,6 +308,22 @@ pub struct ApiAddTorrentResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct OnlyFiles(Vec<usize>);
|
pub struct OnlyFiles(Vec<usize>);
|
||||||
|
pub struct InitialPeers(pub Vec<SocketAddr>);
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Default)]
|
||||||
|
pub struct TorrentAddQueryParams {
|
||||||
|
pub overwrite: Option<bool>,
|
||||||
|
pub output_folder: Option<String>,
|
||||||
|
pub sub_folder: Option<String>,
|
||||||
|
pub only_files_regex: Option<String>,
|
||||||
|
pub only_files: Option<OnlyFiles>,
|
||||||
|
pub peer_connect_timeout: Option<u64>,
|
||||||
|
pub peer_read_write_timeout: Option<u64>,
|
||||||
|
pub initial_peers: Option<InitialPeers>,
|
||||||
|
// Will force interpreting the content as a URL.
|
||||||
|
pub is_url: Option<bool>,
|
||||||
|
pub list_only: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Serialize for OnlyFiles {
|
impl Serialize for OnlyFiles {
|
||||||
fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
|
||||||
|
|
@ -348,8 +364,6 @@ impl<'de> Deserialize<'de> for OnlyFiles {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct InitialPeers(pub Vec<SocketAddr>);
|
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for InitialPeers {
|
impl<'de> Deserialize<'de> for InitialPeers {
|
||||||
fn deserialize<D>(deserializer: D) -> std::prelude::v1::Result<Self, D::Error>
|
fn deserialize<D>(deserializer: D) -> std::prelude::v1::Result<Self, D::Error>
|
||||||
where
|
where
|
||||||
|
|
@ -378,23 +392,8 @@ impl Serialize for InitialPeers {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Default)]
|
|
||||||
pub struct TorrentAddQueryParams {
|
|
||||||
pub overwrite: Option<bool>,
|
|
||||||
pub output_folder: Option<String>,
|
|
||||||
pub sub_folder: Option<String>,
|
|
||||||
pub only_files_regex: Option<String>,
|
|
||||||
pub only_files: Option<OnlyFiles>,
|
|
||||||
pub peer_connect_timeout: Option<u64>,
|
|
||||||
pub peer_read_write_timeout: Option<u64>,
|
|
||||||
pub initial_peers: Option<InitialPeers>,
|
|
||||||
// Will force interpreting the content as a URL.
|
|
||||||
pub is_url: Option<bool>,
|
|
||||||
pub list_only: Option<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TorrentAddQueryParams {
|
impl TorrentAddQueryParams {
|
||||||
fn into_add_torrent_options(self) -> AddTorrentOptions {
|
pub fn into_add_torrent_options(self) -> AddTorrentOptions {
|
||||||
AddTorrentOptions {
|
AddTorrentOptions {
|
||||||
overwrite: self.overwrite.unwrap_or(false),
|
overwrite: self.overwrite.unwrap_or(false),
|
||||||
only_files_regex: self.only_files_regex,
|
only_files_regex: self.only_files_regex,
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,7 @@ impl Serialize for ApiError {
|
||||||
error_kind: &'a str,
|
error_kind: &'a str,
|
||||||
human_readable: String,
|
human_readable: String,
|
||||||
status: u16,
|
status: u16,
|
||||||
|
status_text: String,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
id: Option<usize>,
|
id: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
@ -85,6 +86,7 @@ impl Serialize for ApiError {
|
||||||
},
|
},
|
||||||
human_readable: format!("{self}"),
|
human_readable: format!("{self}"),
|
||||||
status: self.status().as_u16(),
|
status: self.status().as_u16(),
|
||||||
|
status_text: self.status().to_string(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
if let ApiErrorKind::TorrentNotFound(id) = &self.kind {
|
if let ApiErrorKind::TorrentNotFound(id) = &self.kind {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ pub mod dht_utils;
|
||||||
pub mod file_ops;
|
pub mod file_ops;
|
||||||
pub mod http_api;
|
pub mod http_api;
|
||||||
pub mod http_api_client;
|
pub mod http_api_client;
|
||||||
mod http_api_error;
|
pub mod http_api_error;
|
||||||
pub mod peer_connection;
|
pub mod peer_connection;
|
||||||
pub mod peer_info_reader;
|
pub mod peer_info_reader;
|
||||||
pub mod session;
|
pub mod session;
|
||||||
|
|
|
||||||
20
crates/librqbit/webui/dist/assets/index.js
vendored
20
crates/librqbit/webui/dist/assets/index.js
vendored
File diff suppressed because one or more lines are too long
1
crates/librqbit/webui/dist/index.html
vendored
1
crates/librqbit/webui/dist/index.html
vendored
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|
|
||||||
2
crates/librqbit/webui/dist/manifest.json
vendored
2
crates/librqbit/webui/dist/manifest.json
vendored
|
|
@ -4,7 +4,7 @@
|
||||||
"src": "assets/logo.svg"
|
"src": "assets/logo.svg"
|
||||||
},
|
},
|
||||||
"index.html": {
|
"index.html": {
|
||||||
"file": "assets/index-3dee43e7.js",
|
"file": "assets/index-48d4950c.js",
|
||||||
"isEntry": true,
|
"isEntry": true,
|
||||||
"src": "index.html"
|
"src": "index.html"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,19 @@ export interface ListTorrentsResponse {
|
||||||
torrents: Array<TorrentId>;
|
torrents: Array<TorrentId>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TorrentAddQueryParams {
|
||||||
|
overwrite?: boolean | null;
|
||||||
|
output_folder?: string | null;
|
||||||
|
sub_folder?: string | null;
|
||||||
|
only_files_regex?: string | null;
|
||||||
|
only_files?: string,
|
||||||
|
peer_connect_timeout?: number | null;
|
||||||
|
peer_read_write_timeout?: number | null;
|
||||||
|
initial_peers?: string | null;
|
||||||
|
is_url?: boolean | null;
|
||||||
|
list_only?: boolean | null;
|
||||||
|
}
|
||||||
|
|
||||||
// Interface for the Torrent Stats API response
|
// Interface for the Torrent Stats API response
|
||||||
export interface LiveTorrentStats {
|
export interface LiveTorrentStats {
|
||||||
snapshot: {
|
snapshot: {
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
import { StrictMode, createContext, useContext, useEffect, useRef, useState } from 'react';
|
import { createContext, useContext, useEffect, useRef, useState } from 'react';
|
||||||
import ReactDOM from 'react-dom/client';
|
|
||||||
import { ProgressBar, Button, Container, Row, Col, Alert, Modal, Form, Spinner } from 'react-bootstrap';
|
import { ProgressBar, Button, Container, Row, Col, Alert, Modal, Form, Spinner } from 'react-bootstrap';
|
||||||
import { AddTorrentResponse, TorrentDetails, TorrentId, TorrentStats, ErrorDetails, STATE_INITIALIZING, STATE_LIVE, STATE_PAUSED, STATE_ERROR, RqbitAPI } from './api-types';
|
import { AddTorrentResponse, TorrentDetails, TorrentId, TorrentStats, ErrorDetails as ApiErrorDetails, STATE_INITIALIZING, STATE_LIVE, STATE_PAUSED, STATE_ERROR, RqbitAPI } from './api-types';
|
||||||
|
|
||||||
declare const API: RqbitAPI;
|
declare const API: RqbitAPI;
|
||||||
|
|
||||||
interface Error {
|
interface Error {
|
||||||
text: string,
|
text: string,
|
||||||
details?: ErrorDetails,
|
details?: ApiErrorDetails,
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ContextType {
|
interface ContextType {
|
||||||
|
|
@ -352,7 +351,7 @@ export const RqbitWebUI = () => {
|
||||||
</AppContext.Provider >
|
</AppContext.Provider >
|
||||||
}
|
}
|
||||||
|
|
||||||
const ErrorDetails = (props: { details: ErrorDetails }) => {
|
const ErrorDetails = (props: { details: ApiErrorDetails }) => {
|
||||||
let { details } = props;
|
let { details } = props;
|
||||||
if (!details) {
|
if (!details) {
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -431,22 +430,71 @@ const UploadButton = ({ buttonText, onClick, data, resetData, variant }) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const UrlPromptModal: React.FC<{
|
||||||
|
show: boolean,
|
||||||
|
setUrl: (_: string) => void,
|
||||||
|
cancel: () => void,
|
||||||
|
}> = ({ show, setUrl, cancel }) => {
|
||||||
|
let [inputValue, setInputValue] = useState('');
|
||||||
|
return <Modal show={show} onHide={cancel} size='lg'>
|
||||||
|
<Modal.Header closeButton>
|
||||||
|
<Modal.Title>Add torrent</Modal.Title>
|
||||||
|
</Modal.Header>
|
||||||
|
<Modal.Body>
|
||||||
|
<Form>
|
||||||
|
<Form.Group className="mb-3" controlId="url">
|
||||||
|
<Form.Label>Enter magnet or HTTP(S) URL to the .torrent</Form.Label>
|
||||||
|
<Form.Control value={inputValue} placeholder="magnet:?xt=urn:btih:..." onChange={(u) => { setInputValue(u.target.value) }} />
|
||||||
|
</Form.Group>
|
||||||
|
</Form>
|
||||||
|
</Modal.Body>
|
||||||
|
<Modal.Footer>
|
||||||
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
onClick={() => { setUrl(inputValue); setInputValue(''); }}
|
||||||
|
disabled={inputValue.length == 0}>
|
||||||
|
OK
|
||||||
|
</Button>
|
||||||
|
<Button variant="secondary" onClick={cancel}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</Modal.Footer>
|
||||||
|
</Modal >
|
||||||
|
}
|
||||||
|
|
||||||
const MagnetInput = () => {
|
const MagnetInput = () => {
|
||||||
let [magnet, setMagnet] = useState(null);
|
let [magnet, setMagnet] = useState(null);
|
||||||
|
|
||||||
|
let [showModal, setShowModal] = useState(null);
|
||||||
|
|
||||||
const onClick = () => {
|
const onClick = () => {
|
||||||
const m = prompt('Enter magnet link or HTTP(s) URL');
|
const m = prompt('Enter magnet link or HTTP(s) URL');
|
||||||
setMagnet(m === '' ? null : m);
|
setMagnet(m === '' ? null : m);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<UploadButton
|
<>
|
||||||
variant='primary'
|
<UploadButton
|
||||||
buttonText="Add Torrent from Magnet / URL"
|
variant='primary'
|
||||||
onClick={onClick}
|
buttonText="Add Torrent from Magnet / URL"
|
||||||
data={magnet}
|
onClick={() => {
|
||||||
resetData={() => setMagnet(null)}
|
setShowModal(true);
|
||||||
/>
|
}}
|
||||||
|
data={magnet}
|
||||||
|
resetData={() => setMagnet(null)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<UrlPromptModal
|
||||||
|
show={showModal}
|
||||||
|
setUrl={(url) => {
|
||||||
|
setShowModal(null);
|
||||||
|
setMagnet(url);
|
||||||
|
}}
|
||||||
|
cancel={() => {
|
||||||
|
setShowModal(null);
|
||||||
|
setMagnet(null);
|
||||||
|
}} />
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -598,7 +646,7 @@ const Buttons = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const RootContent = (props: { closeableError: ErrorDetails, otherError: ErrorDetails, torrents: Array<TorrentId>, torrentsLoading: boolean }) => {
|
const RootContent = (props: { closeableError: ApiErrorDetails, otherError: ApiErrorDetails, torrents: Array<TorrentId>, torrentsLoading: boolean }) => {
|
||||||
let ctx = useContext(AppContext);
|
let ctx = useContext(AppContext);
|
||||||
return <Container>
|
return <Container>
|
||||||
<ErrorComponent error={props.closeableError} remove={() => ctx.setCloseableError(null)} />
|
<ErrorComponent error={props.closeableError} remove={() => ctx.setCloseableError(null)} />
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue