Fix file selection window on Windows that didnt split file with proper separator
This commit is contained in:
parent
b808382169
commit
b289f1eeaa
6 changed files with 47 additions and 40 deletions
|
|
@ -243,6 +243,7 @@ pub struct TorrentListResponse {
|
|||
#[derive(Serialize, Deserialize)]
|
||||
pub struct TorrentDetailsResponseFile {
|
||||
pub name: String,
|
||||
pub components: Vec<String>,
|
||||
pub length: u64,
|
||||
pub included: bool,
|
||||
}
|
||||
|
|
@ -253,6 +254,7 @@ pub struct EmptyJsonResponse {}
|
|||
#[derive(Serialize, Deserialize)]
|
||||
pub struct TorrentDetailsResponse {
|
||||
pub info_hash: String,
|
||||
pub name: Option<String>,
|
||||
pub files: Vec<TorrentDetailsResponseFile>,
|
||||
}
|
||||
|
||||
|
|
@ -281,9 +283,11 @@ fn make_torrent_details(
|
|||
"<INVALID NAME>".to_string()
|
||||
}
|
||||
};
|
||||
let components = filename_it.to_vec().unwrap_or_default();
|
||||
let included = only_files.map(|o| o.contains(&idx)).unwrap_or(true);
|
||||
TorrentDetailsResponseFile {
|
||||
name,
|
||||
components,
|
||||
length,
|
||||
included,
|
||||
}
|
||||
|
|
@ -291,6 +295,7 @@ fn make_torrent_details(
|
|||
.collect();
|
||||
Ok(TorrentDetailsResponse {
|
||||
info_hash: info_hash.as_string(),
|
||||
name: info.name.as_ref().map(|b| b.to_string()),
|
||||
files,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,12 +6,14 @@ export interface TorrentId {
|
|||
|
||||
export interface TorrentFile {
|
||||
name: string;
|
||||
components: string[];
|
||||
length: number;
|
||||
included: boolean;
|
||||
}
|
||||
|
||||
// Interface for the Torrent Details API response
|
||||
export interface TorrentDetails {
|
||||
name: string | null;
|
||||
info_hash: string;
|
||||
files: Array<TorrentFile>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useMemo, useState } from "react";
|
||||
import { AddTorrentResponse } from "../api-types";
|
||||
import { AddTorrentResponse, TorrentFile } from "../api-types";
|
||||
import { FormCheckbox } from "./forms/FormCheckbox";
|
||||
import { CiSquarePlus, CiSquareMinus } from "react-icons/ci";
|
||||
import { IconButton } from "./buttons/IconButton";
|
||||
|
|
@ -7,7 +7,8 @@ import { formatBytes } from "../helper/formatBytes";
|
|||
|
||||
type TorrentFileForCheckbox = {
|
||||
id: number;
|
||||
name: string;
|
||||
filename: string;
|
||||
pathComponents: string[];
|
||||
length: number;
|
||||
};
|
||||
|
||||
|
|
@ -18,19 +19,12 @@ type FileTree = {
|
|||
files: TorrentFileForCheckbox[];
|
||||
};
|
||||
|
||||
const splitOnce = (s: string, sep: string): [string, string | undefined] => {
|
||||
if (s.indexOf(sep) === -1) {
|
||||
return [s, undefined];
|
||||
}
|
||||
return [s.slice(0, s.indexOf(sep)), s.slice(s.indexOf(sep) + 1)];
|
||||
};
|
||||
|
||||
const newFileTree = (listTorrentResponse: AddTorrentResponse): FileTree => {
|
||||
const separator = "/";
|
||||
const newFileTreeInner = (
|
||||
name: string,
|
||||
id: string,
|
||||
files: TorrentFileForCheckbox[]
|
||||
files: TorrentFileForCheckbox[],
|
||||
depth: number
|
||||
): FileTree => {
|
||||
let directFiles: TorrentFileForCheckbox[] = [];
|
||||
let groups: FileTree[] = [];
|
||||
|
|
@ -41,22 +35,17 @@ const newFileTree = (listTorrentResponse: AddTorrentResponse): FileTree => {
|
|||
return groupsByName[prefix];
|
||||
};
|
||||
|
||||
files.forEach((file) => {
|
||||
let [prefix, name] = splitOnce(file.name, separator);
|
||||
if (name === undefined) {
|
||||
files.forEach((file: TorrentFileForCheckbox) => {
|
||||
if (depth == file.pathComponents.length - 1) {
|
||||
directFiles.push(file);
|
||||
return;
|
||||
}
|
||||
getGroup(prefix).push({
|
||||
id: file.id,
|
||||
name: name,
|
||||
length: file.length,
|
||||
});
|
||||
getGroup(file.pathComponents[0]).push(file);
|
||||
});
|
||||
|
||||
let childId = 0;
|
||||
for (const [key, value] of Object.entries(groupsByName)) {
|
||||
groups.push(newFileTreeInner(key, id + "." + childId, value));
|
||||
groups.push(newFileTreeInner(key, id + "." + childId, value, depth + 1));
|
||||
childId += 1;
|
||||
}
|
||||
return {
|
||||
|
|
@ -70,9 +59,15 @@ const newFileTree = (listTorrentResponse: AddTorrentResponse): FileTree => {
|
|||
return newFileTreeInner(
|
||||
"",
|
||||
"filetree-root",
|
||||
listTorrentResponse.details.files.map((data, id) => {
|
||||
return { id, name: data.name, length: data.length };
|
||||
})
|
||||
listTorrentResponse.details.files.map((file, id) => {
|
||||
return {
|
||||
id,
|
||||
filename: file.components[file.components.length - 1],
|
||||
pathComponents: file.components,
|
||||
length: file.length,
|
||||
};
|
||||
}),
|
||||
0
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -168,7 +163,7 @@ const FileTreeComponent: React.FC<{
|
|||
<FormCheckbox
|
||||
checked={selectedFiles.has(file.id)}
|
||||
key={file.id}
|
||||
label={`${file.name} (${formatBytes(file.length)})`}
|
||||
label={`${file.filename} (${formatBytes(file.length)})`}
|
||||
name={`file-${file.id}`}
|
||||
onChange={() => handleToggleFile(file.id)}
|
||||
></FormCheckbox>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { TorrentActions } from "./buttons/TorrentActions";
|
|||
import { ProgressBar } from "./ProgressBar";
|
||||
import { Speed } from "./Speed";
|
||||
import { formatBytes } from "../helper/formatBytes";
|
||||
import { getLargestFileName } from "../helper/getLargestFileName";
|
||||
import { torrentDisplayName } from "../helper/getTorrentDisplayName";
|
||||
import { getCompletionETA } from "../helper/getCompletionETA";
|
||||
import { StatusIcon } from "./StatusIcon";
|
||||
|
||||
|
|
@ -54,7 +54,7 @@ export const TorrentRow: React.FC<{
|
|||
<div className="flex items-center gap-2">
|
||||
<div className="md:hidden">{statusIcon("w-5 h-5")}</div>
|
||||
<div className="text-left text-lg text-gray-900 text-ellipsis break-all dark:text-slate-200">
|
||||
{getLargestFileName(detailsResponse)}
|
||||
{torrentDisplayName(detailsResponse)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { TorrentDetails } from "../api-types";
|
||||
|
||||
export function getLargestFileName(torrentDetails: TorrentDetails): string {
|
||||
function getLargestFileName(torrentDetails: TorrentDetails): string {
|
||||
const largestFile = torrentDetails.files
|
||||
.filter((f) => f.included)
|
||||
.reduce((prev: any, current: any) =>
|
||||
|
|
@ -8,3 +8,7 @@ export function getLargestFileName(torrentDetails: TorrentDetails): string {
|
|||
);
|
||||
return largestFile.name;
|
||||
}
|
||||
|
||||
export function torrentDisplayName(torrentDetails: TorrentDetails): string {
|
||||
return torrentDetails.name ?? getLargestFileName(torrentDetails);
|
||||
}
|
||||
|
|
@ -74,6 +74,7 @@ pub struct TorrentMetaV1Info<BufType> {
|
|||
pub files: Option<Vec<TorrentMetaV1File<BufType>>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum FileIteratorName<'a, ByteBuf> {
|
||||
Single(Option<&'a ByteBuf>),
|
||||
Tree(&'a [ByteBuf]),
|
||||
|
|
@ -91,11 +92,17 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, ByteBuf> FileIteratorName<'a, ByteBuf> {
|
||||
pub fn to_string(&self) -> anyhow::Result<String>
|
||||
where
|
||||
ByteBuf: AsRef<[u8]>,
|
||||
{
|
||||
impl<'a, ByteBuf> FileIteratorName<'a, ByteBuf>
|
||||
where
|
||||
ByteBuf: AsRef<[u8]>,
|
||||
{
|
||||
pub fn to_vec(&self) -> anyhow::Result<Vec<String>> {
|
||||
self.iter_components()
|
||||
.map(|c| c.map(|s| s.to_owned()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn to_string(&self) -> anyhow::Result<String> {
|
||||
let mut buf = String::new();
|
||||
for (idx, bit) in self.iter_components().enumerate() {
|
||||
let bit = bit?;
|
||||
|
|
@ -106,10 +113,7 @@ impl<'a, ByteBuf> FileIteratorName<'a, ByteBuf> {
|
|||
}
|
||||
Ok(buf)
|
||||
}
|
||||
pub fn to_pathbuf(&self) -> anyhow::Result<PathBuf>
|
||||
where
|
||||
ByteBuf: AsRef<[u8]>,
|
||||
{
|
||||
pub fn to_pathbuf(&self) -> anyhow::Result<PathBuf> {
|
||||
let mut buf = PathBuf::new();
|
||||
for bit in self.iter_components() {
|
||||
let bit = bit?;
|
||||
|
|
@ -117,10 +121,7 @@ impl<'a, ByteBuf> FileIteratorName<'a, ByteBuf> {
|
|||
}
|
||||
Ok(buf)
|
||||
}
|
||||
pub fn iter_components(&self) -> impl Iterator<Item = anyhow::Result<&'a str>>
|
||||
where
|
||||
ByteBuf: AsRef<[u8]>,
|
||||
{
|
||||
pub fn iter_components(&self) -> impl Iterator<Item = anyhow::Result<&'a str>> {
|
||||
let it = match self {
|
||||
FileIteratorName::Single(None) => return Either::Left(once(Ok("torrent-content"))),
|
||||
FileIteratorName::Single(Some(name)) => Either::Left(once((*name).as_ref())),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue