mirror of
https://github.com/qbittorrent/qBittorrent
synced 2025-08-20 13:23:34 -07:00
Merge f65b261962
into feacfb0627
This commit is contained in:
commit
a511bf56cc
5 changed files with 410 additions and 572 deletions
|
@ -826,10 +826,6 @@ td.statusBarSeparator {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
#searchResultsTableDiv {
|
|
||||||
height: calc(100% - 26px) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#searchResults .dynamicTable {
|
#searchResults .dynamicTable {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
|
@ -310,7 +310,7 @@
|
||||||
const addRowsToTable = (rows, selectedRows) => {
|
const addRowsToTable = (rows, selectedRows) => {
|
||||||
let rowId = 0;
|
let rowId = 0;
|
||||||
const rootNode = new window.qBittorrent.FileTree.FolderNode();
|
const rootNode = new window.qBittorrent.FileTree.FolderNode();
|
||||||
rootNode.autoCheckFolders = false;
|
rootNode.autoCalculateCheckedState = false;
|
||||||
|
|
||||||
rows.forEach((row) => {
|
rows.forEach((row) => {
|
||||||
const pathItems = row.path.split(window.qBittorrent.Filesystem.PathSeparator);
|
const pathItems = row.path.split(window.qBittorrent.Filesystem.PathSeparator);
|
||||||
|
@ -334,7 +334,7 @@
|
||||||
|
|
||||||
if (folderNode === null) {
|
if (folderNode === null) {
|
||||||
folderNode = new window.qBittorrent.FileTree.FolderNode();
|
folderNode = new window.qBittorrent.FileTree.FolderNode();
|
||||||
folderNode.autoCheckFolders = false;
|
folderNode.autoCalculateCheckedState = false;
|
||||||
folderNode.rowId = rowId;
|
folderNode.rowId = rowId;
|
||||||
folderNode.path = (parent.path === "")
|
folderNode.path = (parent.path === "")
|
||||||
? folderName
|
? folderName
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -114,6 +114,7 @@ window.qBittorrent.FileTree ??= (() => {
|
||||||
name = "";
|
name = "";
|
||||||
path = "";
|
path = "";
|
||||||
rowId = null;
|
rowId = null;
|
||||||
|
fileId = null;
|
||||||
size = 0;
|
size = 0;
|
||||||
checked = TriState.Unchecked;
|
checked = TriState.Unchecked;
|
||||||
remaining = 0;
|
remaining = 0;
|
||||||
|
@ -122,19 +123,42 @@ window.qBittorrent.FileTree ??= (() => {
|
||||||
availability = 0;
|
availability = 0;
|
||||||
depth = 0;
|
depth = 0;
|
||||||
root = null;
|
root = null;
|
||||||
data = null;
|
|
||||||
isFolder = false;
|
isFolder = false;
|
||||||
children = [];
|
children = [];
|
||||||
|
|
||||||
|
isIgnored() {
|
||||||
|
return this.priority === FilePriority.Ignored;
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateRemaining() {
|
||||||
|
this.remaining = this.isIgnored() ? 0 : (this.size * (1.0 - (this.progress / 100)));
|
||||||
|
}
|
||||||
|
|
||||||
|
serialize() {
|
||||||
|
return {
|
||||||
|
name: this.name,
|
||||||
|
path: this.path,
|
||||||
|
fileId: this.fileId,
|
||||||
|
size: this.size,
|
||||||
|
checked: this.checked,
|
||||||
|
remaining: this.remaining,
|
||||||
|
progress: this.progress,
|
||||||
|
priority: this.priority,
|
||||||
|
availability: this.availability
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FolderNode extends FileNode {
|
class FolderNode extends FileNode {
|
||||||
/**
|
/**
|
||||||
* Will automatically tick the checkbox for a folder if all subfolders and files are also ticked
|
* When true, the folder's `checked` state will be calculately automatically based on its children
|
||||||
*/
|
*/
|
||||||
autoCheckFolders = true;
|
autoCalculateCheckedState = true;
|
||||||
isFolder = true;
|
isFolder = true;
|
||||||
|
fileId = -1;
|
||||||
|
|
||||||
addChild(node) {
|
addChild(node) {
|
||||||
|
node.calculateRemaining();
|
||||||
this.children.push(node);
|
this.children.push(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,22 +205,32 @@ window.qBittorrent.FileTree ??= (() => {
|
||||||
root.checked = TriState.Partial;
|
root.checked = TriState.Partial;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isIgnored = (child.priority === FilePriority.Ignored);
|
if (!child.isIgnored()) {
|
||||||
if (!isIgnored) {
|
|
||||||
root.remaining += child.remaining;
|
root.remaining += child.remaining;
|
||||||
root.progress += (child.progress * child.size);
|
root.progress += (child.progress * child.size);
|
||||||
root.availability += (child.availability * child.size);
|
root.availability += (child.availability * child.size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
root.checked = root.autoCheckFolders ? root.checked : TriState.Checked;
|
root.checked = root.autoCalculateCheckedState ? root.checked : TriState.Checked;
|
||||||
root.progress /= root.size;
|
root.progress = (root.size > 0) ? (root.progress / root.size) : 0;
|
||||||
root.availability /= root.size;
|
root.availability = (root.size > 0) ? (root.availability / root.size) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
stack.pop();
|
stack.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively recalculate the amount of data remaining to be downloaded.
|
||||||
|
* This is useful for updating a folder's "remaining" size as files are unchecked/ignored.
|
||||||
|
*/
|
||||||
|
calculateRemaining() {
|
||||||
|
this.remaining = this.children.reduce((sum, node) => {
|
||||||
|
node.calculateRemaining();
|
||||||
|
return sum + node.remaining;
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return exports();
|
return exports();
|
||||||
|
|
|
@ -84,12 +84,12 @@ window.qBittorrent.TorrentContent ??= (() => {
|
||||||
|
|
||||||
const getAllChildren = (id, fileId) => {
|
const getAllChildren = (id, fileId) => {
|
||||||
const node = torrentFilesTable.getNode(id);
|
const node = torrentFilesTable.getNode(id);
|
||||||
const rowIds = [node.data.rowId];
|
const rowIds = [node.rowId];
|
||||||
const fileIds = [node.data.fileId];
|
const fileIds = [node.fileId];
|
||||||
|
|
||||||
const getChildFiles = (node) => {
|
const getChildFiles = (node) => {
|
||||||
rowIds.push(node.data.rowId);
|
rowIds.push(node.rowId);
|
||||||
fileIds.push(node.data.fileId);
|
fileIds.push(node.fileId);
|
||||||
|
|
||||||
if (node.isFolder) {
|
if (node.isFolder) {
|
||||||
node.children.forEach((child) => {
|
node.children.forEach((child) => {
|
||||||
|
@ -214,8 +214,8 @@ window.qBittorrent.TorrentContent ??= (() => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const getComboboxPriority = (id) => {
|
const getComboboxPriority = (id) => {
|
||||||
const row = torrentFilesTable.rows.get(id.toString());
|
const node = torrentFilesTable.getNode(id.toString());
|
||||||
return normalizePriority(row.full_data.priority, 10);
|
return normalizePriority(node.priority, 10);
|
||||||
};
|
};
|
||||||
|
|
||||||
const switchGlobalCheckboxState = (e) => {
|
const switchGlobalCheckboxState = (e) => {
|
||||||
|
@ -230,8 +230,9 @@ window.qBittorrent.TorrentContent ??= (() => {
|
||||||
setCheckboxUnchecked(checkbox);
|
setCheckboxUnchecked(checkbox);
|
||||||
torrentFilesTable.rows.forEach((row) => {
|
torrentFilesTable.rows.forEach((row) => {
|
||||||
const rowId = row.rowId;
|
const rowId = row.rowId;
|
||||||
const fileId = row.full_data.fileId;
|
const node = torrentFilesTable.getNode(rowId);
|
||||||
const isChecked = (getCheckboxState(rowId) === TriState.Checked);
|
const fileId = node.fileId;
|
||||||
|
const isChecked = (node.checked === TriState.Checked);
|
||||||
if (isChecked) {
|
if (isChecked) {
|
||||||
rowIds.push(rowId);
|
rowIds.push(rowId);
|
||||||
fileIds.push(fileId);
|
fileIds.push(fileId);
|
||||||
|
@ -242,8 +243,9 @@ window.qBittorrent.TorrentContent ??= (() => {
|
||||||
setCheckboxChecked(checkbox);
|
setCheckboxChecked(checkbox);
|
||||||
torrentFilesTable.rows.forEach((row) => {
|
torrentFilesTable.rows.forEach((row) => {
|
||||||
const rowId = row.rowId;
|
const rowId = row.rowId;
|
||||||
const fileId = row.full_data.fileId;
|
const node = torrentFilesTable.getNode(rowId);
|
||||||
const isUnchecked = (getCheckboxState(rowId) === TriState.Unchecked);
|
const fileId = node.fileId;
|
||||||
|
const isUnchecked = (node.checked === TriState.Unchecked);
|
||||||
if (isUnchecked) {
|
if (isUnchecked) {
|
||||||
rowIds.push(rowId);
|
rowIds.push(rowId);
|
||||||
fileIds.push(fileId);
|
fileIds.push(fileId);
|
||||||
|
@ -285,26 +287,22 @@ window.qBittorrent.TorrentContent ??= (() => {
|
||||||
checkbox.indeterminate = true;
|
checkbox.indeterminate = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getCheckboxState = (id) => {
|
|
||||||
const row = torrentFilesTable.rows.get(id.toString());
|
|
||||||
return Number(row.full_data.checked);
|
|
||||||
};
|
|
||||||
|
|
||||||
const setFilePriority = (ids, fileIds, priority) => {
|
const setFilePriority = (ids, fileIds, priority) => {
|
||||||
priority = normalizePriority(priority);
|
priority = normalizePriority(priority);
|
||||||
|
|
||||||
if (onFilePriorityChanged)
|
if (onFilePriorityChanged)
|
||||||
onFilePriorityChanged(fileIds, priority);
|
onFilePriorityChanged(fileIds, priority);
|
||||||
|
|
||||||
const ignore = (priority === FilePriority.Ignored);
|
const nodes = ids.map((id) => {
|
||||||
ids.forEach((id) => {
|
const node = torrentFilesTable.getNode(id.toString());
|
||||||
id = id.toString();
|
node.priority = priority;
|
||||||
torrentFilesTable.setIgnored(id, ignore);
|
node.checked = triStateFromPriority(priority);
|
||||||
|
return node;
|
||||||
const row = torrentFilesTable.rows.get(id);
|
|
||||||
row.full_data.priority = priority;
|
|
||||||
row.full_data.checked = triStateFromPriority(priority);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// must update all nodes above before recalculating
|
||||||
|
for (const node of nodes)
|
||||||
|
node.calculateRemaining();
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateData = (files) => {
|
const updateData = (files) => {
|
||||||
|
@ -318,7 +316,6 @@ window.qBittorrent.TorrentContent ??= (() => {
|
||||||
size: file.size,
|
size: file.size,
|
||||||
progress: window.qBittorrent.Misc.toFixedPointString((file.progress * 100), 1),
|
progress: window.qBittorrent.Misc.toFixedPointString((file.progress * 100), 1),
|
||||||
priority: normalizePriority(file.priority),
|
priority: normalizePriority(file.priority),
|
||||||
remaining: (ignore ? 0 : (file.size * (1 - file.progress))),
|
|
||||||
availability: file.availability
|
availability: file.availability
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -372,19 +369,17 @@ window.qBittorrent.TorrentContent ??= (() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const isChecked = row.checked ? TriState.Checked : TriState.Unchecked;
|
const isChecked = row.checked ? TriState.Checked : TriState.Unchecked;
|
||||||
const remaining = (row.priority === FilePriority.Ignored) ? 0 : row.remaining;
|
|
||||||
const childNode = new window.qBittorrent.FileTree.FileNode();
|
const childNode = new window.qBittorrent.FileTree.FileNode();
|
||||||
childNode.name = row.name;
|
childNode.name = row.name;
|
||||||
childNode.path = row.fileName;
|
childNode.path = row.fileName;
|
||||||
childNode.rowId = rowId;
|
childNode.rowId = rowId;
|
||||||
|
childNode.fileId = row.fileId;
|
||||||
childNode.size = row.size;
|
childNode.size = row.size;
|
||||||
childNode.checked = isChecked;
|
childNode.checked = isChecked;
|
||||||
childNode.remaining = remaining;
|
|
||||||
childNode.progress = row.progress;
|
childNode.progress = row.progress;
|
||||||
childNode.priority = row.priority;
|
childNode.priority = row.priority;
|
||||||
childNode.availability = row.availability;
|
childNode.availability = row.availability;
|
||||||
childNode.root = parent;
|
childNode.root = parent;
|
||||||
childNode.data = row;
|
|
||||||
parent.addChild(childNode);
|
parent.addChild(childNode);
|
||||||
|
|
||||||
++rowId;
|
++rowId;
|
||||||
|
@ -430,6 +425,7 @@ window.qBittorrent.TorrentContent ??= (() => {
|
||||||
const updateComplete = () => {
|
const updateComplete = () => {
|
||||||
// we've finished recursing
|
// we've finished recursing
|
||||||
updateGlobalCheckbox();
|
updateGlobalCheckbox();
|
||||||
|
torrentFilesTable.calculateRemaining();
|
||||||
torrentFilesTable.updateTable(true);
|
torrentFilesTable.updateTable(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -447,7 +443,7 @@ window.qBittorrent.TorrentContent ??= (() => {
|
||||||
let indeterminateCount = 0;
|
let indeterminateCount = 0;
|
||||||
let desiredComboboxPriority = null;
|
let desiredComboboxPriority = null;
|
||||||
for (const sibling of siblings) {
|
for (const sibling of siblings) {
|
||||||
switch (getCheckboxState(sibling.rowId)) {
|
switch (sibling.checked) {
|
||||||
case TriState.Checked:
|
case TriState.Checked:
|
||||||
++checkedCount;
|
++checkedCount;
|
||||||
break;
|
break;
|
||||||
|
@ -465,7 +461,7 @@ window.qBittorrent.TorrentContent ??= (() => {
|
||||||
desiredComboboxPriority = FilePriority.Mixed;
|
desiredComboboxPriority = FilePriority.Mixed;
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentCheckboxState = getCheckboxState(parent.rowId);
|
const currentCheckboxState = parent.checked;
|
||||||
let desiredCheckboxState = TriState.Unchecked;
|
let desiredCheckboxState = TriState.Unchecked;
|
||||||
if ((indeterminateCount > 0) || ((checkedCount > 0) && (uncheckedCount > 0)))
|
if ((indeterminateCount > 0) || ((checkedCount > 0) && (uncheckedCount > 0)))
|
||||||
desiredCheckboxState = TriState.Partial;
|
desiredCheckboxState = TriState.Partial;
|
||||||
|
@ -474,9 +470,9 @@ window.qBittorrent.TorrentContent ??= (() => {
|
||||||
|
|
||||||
const currentComboboxPriority = getComboboxPriority(parent.rowId);
|
const currentComboboxPriority = getComboboxPriority(parent.rowId);
|
||||||
if ((currentCheckboxState !== desiredCheckboxState) || (currentComboboxPriority !== desiredComboboxPriority)) {
|
if ((currentCheckboxState !== desiredCheckboxState) || (currentComboboxPriority !== desiredComboboxPriority)) {
|
||||||
const row = torrentFilesTable.rows.get(parent.rowId.toString());
|
const node = torrentFilesTable.getNode(parent.rowId.toString());
|
||||||
row.full_data.priority = desiredComboboxPriority;
|
node.priority = desiredComboboxPriority;
|
||||||
row.full_data.checked = desiredCheckboxState;
|
node.checked = desiredCheckboxState;
|
||||||
|
|
||||||
updateParentFolder(parent.rowId);
|
updateParentFolder(parent.rowId);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue