UI link to video files

This commit is contained in:
Igor Katson 2024-04-24 22:55:56 +01:00
parent f0788f2c4a
commit 94589a21fe
11 changed files with 64 additions and 19 deletions

View file

@ -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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -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"
}

View file

@ -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,

View file

@ -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}
/>
);
};

View file

@ -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>

View file

@ -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}

View file

@ -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: () => {} });

View file

@ -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}`;
},
};

View file

@ -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 "";
},
};
};