UI link to video files
This commit is contained in:
parent
f0788f2c4a
commit
94589a21fe
11 changed files with 64 additions and 19 deletions
|
|
@ -176,7 +176,6 @@ impl HttpApi {
|
|||
.and_then(|s| s.parse().ok());
|
||||
if let Some(offset) = offset {
|
||||
status = StatusCode::PARTIAL_CONTENT;
|
||||
info!(offset, "range request offset");
|
||||
stream
|
||||
.seek(SeekFrom::Start(offset))
|
||||
.await
|
||||
|
|
|
|||
2
crates/librqbit/webui/dist/assets/index.css
vendored
2
crates/librqbit/webui/dist/assets/index.css
vendored
File diff suppressed because one or more lines are too long
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
6
crates/librqbit/webui/dist/manifest.json
vendored
6
crates/librqbit/webui/dist/manifest.json
vendored
|
|
@ -4,14 +4,14 @@
|
|||
"src": "assets/logo.svg"
|
||||
},
|
||||
"index.css": {
|
||||
"file": "assets/index-144bafe5.css",
|
||||
"file": "assets/index-74bd798d.css",
|
||||
"src": "index.css"
|
||||
},
|
||||
"index.html": {
|
||||
"css": [
|
||||
"assets/index-144bafe5.css"
|
||||
"assets/index-74bd798d.css"
|
||||
],
|
||||
"file": "assets/index-838acac9.js",
|
||||
"file": "assets/index-80d6e7c6.js",
|
||||
"isEntry": true,
|
||||
"src": "index.html"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -166,6 +166,7 @@ export interface RqbitAPI {
|
|||
listTorrents: () => Promise<ListTorrentsResponse>;
|
||||
getTorrentDetails: (index: number) => Promise<TorrentDetails>;
|
||||
getTorrentStats: (index: number) => Promise<TorrentStats>;
|
||||
getTorrentStreamUrl: (index: number, file_id: number) => string | null;
|
||||
uploadTorrent: (
|
||||
data: string | File,
|
||||
opts?: AddTorrentOptions,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useMemo, useState } from "react";
|
||||
import { useContext, useMemo, useState } from "react";
|
||||
import { TorrentDetails, TorrentStats } from "../api-types";
|
||||
import { FormCheckbox } from "./forms/FormCheckbox";
|
||||
import { CiSquarePlus, CiSquareMinus } from "react-icons/ci";
|
||||
|
|
@ -6,6 +6,7 @@ import { IconButton } from "./buttons/IconButton";
|
|||
import { formatBytes } from "../helper/formatBytes";
|
||||
import { ProgressBar } from "./ProgressBar";
|
||||
import sortBy from "lodash.sortby";
|
||||
import { APIContext } from "../context";
|
||||
|
||||
type TorrentFileForCheckbox = {
|
||||
id: number;
|
||||
|
|
@ -86,6 +87,7 @@ const newFileTree = (
|
|||
};
|
||||
|
||||
const FileTreeComponent: React.FC<{
|
||||
torrentId?: number;
|
||||
tree: FileTree;
|
||||
torrentDetails: TorrentDetails;
|
||||
torrentStats: TorrentStats | null;
|
||||
|
|
@ -94,7 +96,9 @@ const FileTreeComponent: React.FC<{
|
|||
initialExpanded: boolean;
|
||||
showProgressBar?: boolean;
|
||||
disabled?: boolean;
|
||||
allowStream?: boolean;
|
||||
}> = ({
|
||||
torrentId,
|
||||
tree,
|
||||
selectedFiles,
|
||||
setSelectedFiles,
|
||||
|
|
@ -103,7 +107,9 @@ const FileTreeComponent: React.FC<{
|
|||
torrentStats,
|
||||
showProgressBar,
|
||||
disabled,
|
||||
allowStream,
|
||||
}) => {
|
||||
const API = useContext(APIContext);
|
||||
let [expanded, setExpanded] = useState(initialExpanded);
|
||||
let children = useMemo(() => {
|
||||
let getAllChildren = (tree: FileTree): number[] => {
|
||||
|
|
@ -149,6 +155,16 @@ const FileTreeComponent: React.FC<{
|
|||
.reduce((a, b) => a + b, 0);
|
||||
};
|
||||
|
||||
const fileLink = (file: TorrentFileForCheckbox) => {
|
||||
if (
|
||||
allowStream &&
|
||||
torrentId != null &&
|
||||
/\.(mp4|mkv|avi)$/.test(file.filename)
|
||||
) {
|
||||
return API.getTorrentStreamUrl(torrentId, file.id);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center">
|
||||
|
|
@ -170,6 +186,7 @@ const FileTreeComponent: React.FC<{
|
|||
<div className="pl-5" hidden={!expanded}>
|
||||
{tree.dirs.map((dir) => (
|
||||
<FileTreeComponent
|
||||
torrentId={torrentId}
|
||||
torrentDetails={torrentDetails}
|
||||
torrentStats={torrentStats}
|
||||
key={dir.name}
|
||||
|
|
@ -179,6 +196,7 @@ const FileTreeComponent: React.FC<{
|
|||
initialExpanded={false}
|
||||
showProgressBar={showProgressBar}
|
||||
disabled={disabled}
|
||||
allowStream={allowStream}
|
||||
/>
|
||||
))}
|
||||
<div className="pl-1">
|
||||
|
|
@ -197,6 +215,7 @@ const FileTreeComponent: React.FC<{
|
|||
name={`file-${file.id}`}
|
||||
disabled={disabled}
|
||||
onChange={() => handleToggleFile(file.id)}
|
||||
labelLink={fileLink(file)}
|
||||
></FormCheckbox>
|
||||
{showProgressBar && (
|
||||
<ProgressBar
|
||||
|
|
@ -213,19 +232,23 @@ const FileTreeComponent: React.FC<{
|
|||
};
|
||||
|
||||
export const FileListInput: React.FC<{
|
||||
torrentId?: number;
|
||||
torrentDetails: TorrentDetails;
|
||||
torrentStats: TorrentStats | null;
|
||||
selectedFiles: Set<number>;
|
||||
setSelectedFiles: (_: Set<number>) => void;
|
||||
showProgressBar?: boolean;
|
||||
disabled?: boolean;
|
||||
allowStream?: boolean;
|
||||
}> = ({
|
||||
torrentId,
|
||||
torrentDetails,
|
||||
selectedFiles,
|
||||
setSelectedFiles,
|
||||
torrentStats,
|
||||
showProgressBar,
|
||||
disabled,
|
||||
allowStream,
|
||||
}) => {
|
||||
let fileTree = useMemo(
|
||||
() => newFileTree(torrentDetails, torrentStats),
|
||||
|
|
@ -234,6 +257,7 @@ export const FileListInput: React.FC<{
|
|||
|
||||
return (
|
||||
<FileTreeComponent
|
||||
torrentId={torrentId}
|
||||
torrentDetails={torrentDetails}
|
||||
torrentStats={torrentStats}
|
||||
tree={fileTree}
|
||||
|
|
@ -242,6 +266,7 @@ export const FileListInput: React.FC<{
|
|||
initialExpanded={true}
|
||||
showProgressBar={showProgressBar}
|
||||
disabled={disabled}
|
||||
allowStream={allowStream}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -170,11 +170,13 @@ export const TorrentRow: React.FC<{
|
|||
{detailsResponse && extendedView && (
|
||||
<div className="">
|
||||
<FileListInput
|
||||
torrentId={id}
|
||||
torrentDetails={detailsResponse}
|
||||
torrentStats={statsResponse}
|
||||
selectedFiles={selectedFiles}
|
||||
setSelectedFiles={updateSelectedFiles}
|
||||
disabled={savingSelectedFiles}
|
||||
allowStream
|
||||
showProgressBar
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ export const FormCheckbox: React.FC<{
|
|||
onChange?: ChangeEventHandler<HTMLInputElement>;
|
||||
children?: React.ReactNode;
|
||||
classNames?: string;
|
||||
labelLink?: string | null;
|
||||
}> = ({
|
||||
checked,
|
||||
name,
|
||||
|
|
@ -19,6 +20,7 @@ export const FormCheckbox: React.FC<{
|
|||
help,
|
||||
inputType,
|
||||
children,
|
||||
labelLink,
|
||||
}) => {
|
||||
return (
|
||||
<div className={`flex gap-3 items-start`}>
|
||||
|
|
@ -34,7 +36,17 @@ export const FormCheckbox: React.FC<{
|
|||
/>
|
||||
</div>
|
||||
<div className="text-sm flex flex-col gap-1">
|
||||
<label htmlFor={name}>{label}</label>
|
||||
{labelLink ? (
|
||||
<a
|
||||
href={labelLink}
|
||||
className="text-blue-600 dark:text-blue-500 hover:underline"
|
||||
>
|
||||
{label}
|
||||
</a>
|
||||
) : (
|
||||
<label htmlFor={name}>{label}</label>
|
||||
)}
|
||||
|
||||
{help && (
|
||||
<div className="text-xs text-slate-500 dark:text-slate-300 mb-3">
|
||||
{help}
|
||||
|
|
|
|||
|
|
@ -29,8 +29,8 @@ export const APIContext = createContext<RqbitAPI>({
|
|||
delete: () => {
|
||||
throw new Error("Function not implemented.");
|
||||
},
|
||||
getStreamLogsUrl: () => {
|
||||
return null;
|
||||
getTorrentStreamUrl: () => {
|
||||
throw new Error("Function not implemented.");
|
||||
},
|
||||
});
|
||||
export const RefreshTorrentStatsContext = createContext({ refresh: () => {} });
|
||||
|
|
|
|||
|
|
@ -140,4 +140,7 @@ export const API: RqbitAPI & { getVersion: () => Promise<string> } = {
|
|||
const r = await makeRequest("GET", "/");
|
||||
return r.version;
|
||||
},
|
||||
getTorrentStreamUrl: (index: number, file_id: number) => {
|
||||
return apiUrl + `/torrents/${index}/stream/${file_id}`;
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -122,5 +122,8 @@ export const makeAPI = (configuration: RqbitDesktopConfig): RqbitAPI => {
|
|||
delete: function (id: number): Promise<void> {
|
||||
return invokeAPI<void>("torrent_action_delete", { id });
|
||||
},
|
||||
getTorrentStreamUrl: () => {
|
||||
return "";
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue