This commit is contained in:
Carve 2024-01-07 17:04:03 -05:00
parent 4a190baf04
commit 7c5db5975d
22 changed files with 1976 additions and 897 deletions

View File

@ -29,6 +29,7 @@ Required by:
left: 0; left: 0;
position: absolute; /* This is also set in theme.js in order to make theme transitions smoother */ position: absolute; /* This is also set in theme.js in order to make theme transitions smoother */
top: 0; top: 0;
height: auto !important; /* fixes issues with modal height */
} }
/* /*

49
private/css/palette.css Normal file
View File

@ -0,0 +1,49 @@
/* Adaptive color palette */
/* Default rules */
* {
--color-accent-blue: hsl(210deg 65% 55%);
--color-text-blue: hsl(210deg 100% 55%);
--color-text-orange: hsl(26deg 100% 45%);
--color-text-red: hsl(0deg 100% 65%);
--color-text-green: hsl(110deg 94% 27%);
--color-text-white: hsl(0deg 0% 100%);
--color-text-disabled: hsl(0deg 0% 60%);
--color-text-default: hsl(0deg 0% 33%);
--color-background-blue: hsl(210deg 65% 55%);
--color-background-popup: hsl(0deg 0% 100%);
--color-background-default: hsl(0deg 0% 94%);
--color-background-hover: hsl(26deg 80% 60%);
--color-border-blue: hsl(210deg 42% 48%);
--color-border-default: hsl(0deg 0% 85%);
}
:root {
color-scheme: light dark;
}
/* Light corrections */
@media (prefers-color-scheme: light) {
:root {
color-scheme: light;
}
}
/* Dark corrections */
@media (prefers-color-scheme: dark) {
:root {
color-scheme: dark;
}
* {
--color-accent-blue: hsl(210deg 42% 48%);
--color-text-blue: hsl(210deg 88.1% 73.5%);
--color-text-orange: hsl(26deg 65% 70%);
--color-text-default: hsl(0deg 0% 90%);
--color-background-blue: hsl(210deg 42% 48%);
--color-background-popup: hsl(0deg 0% 20%);
--color-background-default: hsl(0deg 0% 25%);
--color-background-hover: hsl(26deg 50% 55%);
--color-border-default: hsl(0deg 0% 33%);
}
}

View File

@ -0,0 +1,231 @@
@import url("palette.css");
.hidden-search {
display: none !important;
}
li[data-parent].closed {
display: none !important;
}
li[data-parent].open:not(.hidden-search) {
display: block !important;
}
.vsb-menu {
background-clip: padding-box;
background-color: var(--color-background-default);
border: 1px solid var(--color-border-default);
cursor: pointer;
display: block;
font-size: 11px;
position: absolute;
visibility: hidden;
z-index: 1000; /*Don't change*/
}
.vsb-js-search-zone {
min-height: 1.8em;
padding: 2px;
position: absolute;
width: 80%;
z-index: 1001; /*Don't change*/
}
.vsb-js-search-zone input {
border-radius: 4px;
height: 25px !important;
margin-left: 2px;
width: 96%;
}
.vsb-main {
display: inline-block;
position: relative;
text-align: left;
vertical-align: top; /*Don't change*/
}
.vsb-menu ul {
cursor: pointer;
list-style: none;
margin: 0;
overflow-y: auto;
padding: 0;
user-select: none;
white-space: nowrap;
}
li.disabled {
background-color: #999;
cursor: not-allowed;
opacity: 0.3;
}
li.overflow {
background-color: #999;
cursor: not-allowed;
opacity: 0.3;
}
li.short {
overflow: hidden;
text-overflow: ellipsis;
}
.vsb-main button {
border: 1px solid var(--color-border-default);
border-radius: 4px;
min-width: 120px;
padding: 6px 12px;
text-align: left;
width: 100%;
z-index: 1;
}
.vsb-main button.disabled {
cursor: not-allowed;
opacity: 0.65;
}
.vsb-main .title {
margin-right: 6px;
user-select: none;
}
.vsb-main ul {
white-space: nowrap;
}
.vsb-menu li {
font-size: 12px;
padding: 4px 26px;
}
.vsb-menu li:hover {
background-color: var(--color-background-hover);
color: var(--color-text-white);
}
.vsb-menu li.grouped-option b {
display: inline-block;
margin-left: 10px;
transform: translate(-18px);
}
.vsb-menu li.grouped-option.open span {
border-radius: 2px;
display: inline-block;
font-size: inherit;
height: 8px;
margin-top: -2px;
transform: translate(-38px) rotate(45deg);
width: 8px;
}
.vsb-menu li.grouped-option.closed span {
border-radius: 2px;
display: inline-block;
font-size: inherit;
height: 8px;
transform: translate(-38px) rotate(-45deg);
width: 8px;
}
.vsb-menu li.grouped-option i {
border: 1px solid;
border-radius: 3px;
display: inline-block;
float: left;
font-size: inherit;
font-weight: bold;
height: 11px;
margin-left: 22px;
margin-right: 2px;
margin-top: 0px;
padding: 1px 3px 2px;
width: 8px;
}
.vsb-menu li.grouped-option.checked i::after {
content: "";
display: inline-block;
float: left;
font-size: inherit;
height: 8px;
margin-left: 0px;
transform: rotate(45deg);
width: 5px;
}
.vsb-menu :not(.multi) li.active {
margin-left: 7px;
}
.vsb-menu :not(.multi) li.active::before {
border-bottom: 3px solid var(--color-border-blue);
border-radius: 2px;
border-right: 3px solid var(--color-border-blue);
content: "";
display: inline-block;
font-size: inherit;
height: 10px;
margin-left: -18px;
transform: rotate(45deg);
width: 5px;
}
.vsb-menu .multi li.grouped-option {
padding-left: 5px;
}
.vsb-menu .multi li.grouped-option:hover {
color: rgb(52 31 112);
font-weight: bold;
text-decoration: underline;
}
.vsb-menu .multi li:not(.grouped-option)::before {
background: var(--color-background-popup);
border: 1px solid;
border-radius: 3px;
content: "";
display: inline-block;
float: left;
font-size: inherit;
font-weight: bold;
margin-left: -22px;
margin-right: 2px;
margin-top: 0px;
padding: 7px;
}
.vsb-menu .multi li:not(.grouped-option).active::after {
border-bottom: 3px solid var(--color-border-blue);
border-right: 3px solid var(--color-border-blue);
content: "";
display: inline-block;
float: left;
font-size: inherit;
height: 8px;
margin-left: -18px;
margin-top: 1px;
transform: rotate(45deg);
width: 5px;
}
.caret {
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 4px dashed;
border-top: 4px solid;
display: inline-block;
height: 0;
margin-left: 2px;
vertical-align: middle;
width: 0;
}
li[data-parent] {
padding-left: 50px !important;
}

View File

@ -80,6 +80,14 @@
<input type="checkbox" id="startTorrent" /> <input type="checkbox" id="startTorrent" />
</td> </td>
</tr> </tr>
<tr>
<td>
<label for="addToTopOfQueue">Add to top of queue</label>
</td>
<td>
<input type="checkbox" id="addToTopOfQueue" name="addToTopOfQueue" value="true" />
</td>
</tr>
<tr> <tr>
<td> <td>
<label for="stopCondition">Stop condition:</label> <label for="stopCondition">Stop condition:</label>

View File

@ -30,6 +30,7 @@
<script src="scripts/piecesbar.js?v=${CACHEID}"></script> <script src="scripts/piecesbar.js?v=${CACHEID}"></script>
<script src="scripts/file-tree.js?v=${CACHEID}"></script> <script src="scripts/file-tree.js?v=${CACHEID}"></script>
<script src="scripts/dynamicTable.js?locale=${LANG}&v=${CACHEID}"></script> <script src="scripts/dynamicTable.js?locale=${LANG}&v=${CACHEID}"></script>
<script src="scripts/rename-files.js?v=${CACHEID}"></script>
<script src="scripts/client.js?locale=${LANG}&v=${CACHEID}"></script> <script src="scripts/client.js?locale=${LANG}&v=${CACHEID}"></script>
<script src="scripts/contextmenu.js?locale=${LANG}&v=${CACHEID}"></script> <script src="scripts/contextmenu.js?locale=${LANG}&v=${CACHEID}"></script>
</head> </head>
@ -136,8 +137,13 @@
<li class="separator"><a href="#delete"><img src="images/list-remove.svg" alt="Remove" /> Remove</a></li> <li class="separator"><a href="#delete"><img src="images/list-remove.svg" alt="Remove" /> Remove</a></li>
<li class="separator"> <li class="separator">
<a href="#setLocation"><img src="images/set-location.svg" alt="Set location..." /> Set location...</a> <a href="#setLocation"><img src="images/set-location.svg" alt="Set location..." /> Set location...</a>
</li>
<li>
<a href="#rename"><img src="images/edit-rename.svg" alt="Rename..." /> Rename...</a> <a href="#rename"><img src="images/edit-rename.svg" alt="Rename..." /> Rename...</a>
</li> </li>
<li>
<a href="#renameFiles"><img src="images/edit-rename.svg" alt="Rename Files..." /> Rename Files...</a>
</li>
<li> <li>
<a href="#Category" class="arrow-right"><img src="images/view-categories.svg" alt="Category" /> Category</a> <a href="#Category" class="arrow-right"><img src="images/view-categories.svg" alt="Category" /> Category</a>
<ul id="contextCategoryList" class="scrollableMenu"></ul> <ul id="contextCategoryList" class="scrollableMenu"></ul>
@ -174,6 +180,7 @@
<li><a href="#" id="copyInfohash2" class="copyToClipboard"><img src="images/hash.svg" alt="Info hash v2" /> Info hash v2</a></li> <li><a href="#" id="copyInfohash2" class="copyToClipboard"><img src="images/hash.svg" alt="Info hash v2" /> Info hash v2</a></li>
<li><a href="#" id="copyMagnetLink" class="copyToClipboard"><img src="images/torrent-magnet.svg" alt="Magnet link" /> Magnet link</a></li> <li><a href="#" id="copyMagnetLink" class="copyToClipboard"><img src="images/torrent-magnet.svg" alt="Magnet link" /> Magnet link</a></li>
<li><a href="#" id="copyID" class="copyToClipboard"><img src="images/help-about.svg" alt="Torrent ID" /> Torrent ID</a></li> <li><a href="#" id="copyID" class="copyToClipboard"><img src="images/help-about.svg" alt="Torrent ID" /> Torrent ID</a></li>
<li><a href="#" id="copyComment" class="copyToClipboard"><img src="images/edit-copy.svg" alt="Comment" /> Comment</a></li>
</ul> </ul>
</li> </li>
<li> <li>
@ -182,6 +189,7 @@
</ul> </ul>
<ul id="categoriesFilterMenu" class="contextMenu"> <ul id="categoriesFilterMenu" class="contextMenu">
<li><a href="#createCategory"><img src="images/list-add.svg" alt="Add category..." /> Add category...</a></li> <li><a href="#createCategory"><img src="images/list-add.svg" alt="Add category..." /> Add category...</a></li>
<li><a href="#createSubcategory"><img src="images/list-add.svg" alt="Add subcategory..." /> Add subcategory...</a></li>
<li><a href="#editCategory"><img src="images/edit-rename.svg" alt="Edit category..." /> Edit category...</a></li> <li><a href="#editCategory"><img src="images/edit-rename.svg" alt="Edit category..." /> Edit category...</a></li>
<li><a href="#deleteCategory"><img src="images/list-remove.svg" alt="Remove category" /> Remove category</a></li> <li><a href="#deleteCategory"><img src="images/list-remove.svg" alt="Remove category" /> Remove category</a></li>
<li><a href="#deleteUnusedCategories"><img src="images/list-remove.svg" alt="Remove unused categories" /> Remove unused categories</a></li> <li><a href="#deleteUnusedCategories"><img src="images/list-remove.svg" alt="Remove unused categories" /> Remove unused categories</a></li>
@ -225,6 +233,9 @@
</ul> </ul>
</li> </li>
</ul> </ul>
<ul id="multiRenameFilesMenu" class="contextMenu">
<li><a href="#ToggleSelection"><img src="images/edit-rename.svg" alt="Toggle Selection" /> Toggle Selection</a></li>
</ul>
<div id="desktopFooterWrapper"> <div id="desktopFooterWrapper">
<div id="desktopFooter"> <div id="desktopFooter">
<span id="error_div"></span> <span id="error_div"></span>

View File

@ -45,6 +45,10 @@
$('savePath').set('value', window.qBittorrent.Misc.escapeHtml(uriSavePath)); $('savePath').set('value', window.qBittorrent.Misc.escapeHtml(uriSavePath));
$('savePath').focus(); $('savePath').focus();
} }
else if (uriAction === "createSubcategory") {
$('categoryName').set('value', window.qBittorrent.Misc.escapeHtml(uriCategoryName));
$('categoryName').focus();
}
else { else {
$('categoryName').focus(); $('categoryName').focus();
} }
@ -96,6 +100,7 @@
}).send(); }).send();
break; break;
case "create": case "create":
case "createSubcategory":
if (!verifyCategoryName(categoryName)) if (!verifyCategoryName(categoryName))
return; return;

491
private/rename_files.html Normal file
View File

@ -0,0 +1,491 @@
<!DOCTYPE html>
<html lang="${LANG}">
<head>
<meta charset="UTF-8" />
<title>Renaming</title>
<script src="scripts/lib/MooTools-Core-1.6.0-compat-compressed.js"></script>
<script src="scripts/lib/MooTools-More-1.6.0-compat-compressed.js"></script>
<script src="scripts/filesystem.js?v=${CACHEID}"></script>
<script src="scripts/misc.js?locale=${LANG}&v=${CACHEID}"></script>
<script src="scripts/file-tree.js?v=${CACHEID}"></script>
<script src="scripts/dynamicTable.js?locale=${LANG}&v=${CACHEID}"></script>
<script src="scripts/rename-files.js?v=${CACHEID}"></script>
<script>
'use strict';
if (window.parent.qBittorrent !== undefined) {
window.qBittorrent = window.parent.qBittorrent;
}
window.qBittorrent = window.parent.qBittorrent;
var TriState = window.qBittorrent.FileTree.TriState;
var data = window.MUI.Windows.instances['multiRenamePage'].options.data;
var bulkRenameFilesContextMenu;
if (!bulkRenameFilesContextMenu) {
bulkRenameFilesContextMenu = new window.qBittorrent.ContextMenu.ContextMenu({
targets: '#bulkRenameFilesTableDiv tr',
menu: 'multiRenameFilesMenu',
actions: {
ToggleSelection: function(element, ref) {
const rowId = parseInt(element.get('data-row-id'));
const row = bulkRenameFilesTable.getNode(rowId);
const checkState = row.checked == 1 ? 0 : 1;
bulkRenameFilesTable.toggleNodeTreeCheckbox(rowId, checkState);
bulkRenameFilesTable.updateGlobalCheckbox();
bulkRenameFilesTable.onRowSelectionChange(bulkRenameFilesTable.getSelectedRows());
}
},
offsets: {
x: -15,
y: 2
}
});
}
// Setup the dynamic table for bulk renaming
var bulkRenameFilesTable = new window.qBittorrent.DynamicTable.BulkRenameTorrentFilesTable();
bulkRenameFilesTable.setup('bulkRenameFilesTableDiv', 'bulkRenameFilesTableFixedHeaderDiv', bulkRenameFilesContextMenu);
// Inject checkbox into the first column of the table header
var tableHeaders = $$('#bulkRenameFilesTableFixedHeaderDiv .dynamicTableHeader th');
var checkboxHeader;
if (tableHeaders.length > 0) {
if (checkboxHeader) {
checkboxHeader.remove();
}
checkboxHeader = new Element('input');
checkboxHeader.set('type', 'checkbox');
checkboxHeader.set('id', 'rootMultiRename_cb');
checkboxHeader.addEvent('click', function(e) {
bulkRenameFilesTable.toggleGlobalCheckbox();
fileRenamer.selectedFiles = bulkRenameFilesTable.getSelectedRows();
fileRenamer.update();
});
const checkboxTH = tableHeaders[0];
checkboxHeader.injectInside(checkboxTH);
}
// Register keyboard events to modal window
// https://github.com/qbittorrent/qBittorrent/pull/18687#discussion_r1135045726
var keyboard;
if (!keyboard) {
keyboard = new Keyboard({
defaultEventType: 'keydown',
events: {
'Escape': function(event) {
window.parent.closeWindows();
event.preventDefault();
},
'Esc': function(event) {
window.parent.closeWindows();
event.preventDefault();
}
}
});
keyboard.activate();
}
var fileRenamer = new window.qBittorrent.MultiRename.RenameFiles();
fileRenamer.hash = data.hash;
// Load Multi Rename Preferences
var multiRenamePrefChecked = LocalPreferences.get('multirename_rememberPreferences', "true") === "true";
$('multirename_rememberprefs_checkbox').setProperty('checked', multiRenamePrefChecked);
if (multiRenamePrefChecked) {
var multirename_search = LocalPreferences.get('multirename_search', '');
fileRenamer.setSearch(multirename_search);
$('multiRenameSearch').set('value', multirename_search);
var multirename_useRegex = LocalPreferences.get('multirename_useRegex', false);
fileRenamer.useRegex = multirename_useRegex === 'true';
$('use_regex_search').checked = fileRenamer.useRegex;
var multirename_matchAllOccurrences = LocalPreferences.get('multirename_matchAllOccurrences', false);
fileRenamer.matchAllOccurrences = multirename_matchAllOccurrences === 'true';
$('match_all_occurrences').checked = fileRenamer.matchAllOccurrences;
var multirename_caseSensitive = LocalPreferences.get('multirename_caseSensitive', false);
fileRenamer.caseSensitive = multirename_caseSensitive === 'true';
$('case_sensitive').checked = fileRenamer.caseSensitive;
var multirename_replace = LocalPreferences.get('multirename_replace', '');
fileRenamer.setReplacement(multirename_replace);
$('multiRenameReplace').set('value', multirename_replace);
var multirename_appliesTo = LocalPreferences.get('multirename_appliesTo', window.qBittorrent.MultiRename.AppliesTo.FilenameExtension);
fileRenamer.appliesTo = window.qBittorrent.MultiRename.AppliesTo[multirename_appliesTo];
$('applies_to_option').set('value', fileRenamer.appliesTo);
var multirename_includeFiles = LocalPreferences.get('multirename_includeFiles', true);
fileRenamer.includeFiles = multirename_includeFiles === 'true';
$('include_files').checked = fileRenamer.includeFiles;
var multirename_includeFolders = LocalPreferences.get('multirename_includeFolders', false);
fileRenamer.includeFolders = multirename_includeFolders === 'true';
$('include_folders').checked = fileRenamer.includeFolders;
var multirename_fileEnumerationStart = LocalPreferences.get('multirename_fileEnumerationStart', 0);
fileRenamer.fileEnumerationStart = parseInt(multirename_fileEnumerationStart);
$('file_counter').set('value', fileRenamer.fileEnumerationStart);
var multirename_replaceAll = LocalPreferences.get('multirename_replaceAll', false);
fileRenamer.replaceAll = multirename_replaceAll === 'true';
var renameButtonValue = fileRenamer.replaceAll ? 'Replace All' : 'Replace';
$('renameOptions').set('value', renameButtonValue);
$('renameButton').set('value', renameButtonValue);
}
// Fires every time a row's selection changes
bulkRenameFilesTable.onRowSelectionChange = function(row) {
fileRenamer.selectedFiles = bulkRenameFilesTable.getSelectedRows();
fileRenamer.update();
};
// Setup Search Events that control renaming
$('multiRenameSearch').addEvent('input', function(e) {
let sanitized = e.target.value.replace(/\n/g, '');
$('multiRenameSearch').set('value', sanitized);
// Search input has changed
$('multiRenameSearch').style['border-color'] = '';
LocalPreferences.set('multirename_search', sanitized);
fileRenamer.setSearch(sanitized);
});
$('use_regex_search').addEvent('change', function(e) {
fileRenamer.useRegex = e.target.checked;
LocalPreferences.set('multirename_useRegex', e.target.checked);
fileRenamer.update();
});
$('match_all_occurrences').addEvent('change', function(e) {
fileRenamer.matchAllOccurrences = e.target.checked;
LocalPreferences.set('multirename_matchAllOccurrences', e.target.checked);
fileRenamer.update();
});
$('case_sensitive').addEvent('change', function(e) {
fileRenamer.caseSensitive = e.target.checked;
LocalPreferences.set('multirename_caseSensitive', e.target.checked);
fileRenamer.update();
});
/**
* Fires every time the filerenamer gets changed, it will update all the rows in the table
*/
fileRenamer.onChanged = function(matchedRows) {
// Clear renamed column
document
.querySelectorAll("span[id^='filesTablefileRenamed']")
.forEach(function(span) {
span.set('text', "");
});
// Update renamed column for matched rows
for (let i = 0; i < matchedRows.length; ++i) {
const row = matchedRows[i];
$('filesTablefileRenamed' + row.rowId).set('text', row.renamed);
}
};
fileRenamer.onInvalidRegex = function(err) {
$('multiRenameSearch').style['border-color'] = '#CC0033';
};
// Setup Replace Events that control renaming
$('multiRenameReplace').addEvent('input', function(e) {
let sanitized = e.target.value.replace(/\n/g, '');
$('multiRenameReplace').set('value', sanitized);
// Replace input has changed
$('multiRenameReplace').style['border-color'] = '';
LocalPreferences.set('multirename_replace', sanitized);
fileRenamer.setReplacement(sanitized);
});
$('applies_to_option').addEvent('change', function(e) {
fileRenamer.appliesTo = e.target.value;
LocalPreferences.set('multirename_appliesTo', e.target.value);
fileRenamer.update();
});
$('include_files').addEvent('change', function(e) {
fileRenamer.includeFiles = e.target.checked;
LocalPreferences.set('multirename_includeFiles', e.target.checked);
fileRenamer.update();
});
$('include_folders').addEvent('change', function(e) {
fileRenamer.includeFolders = e.target.checked;
LocalPreferences.set('multirename_includeFolders', e.target.checked);
fileRenamer.update();
});
$('file_counter').addEvent('input', function(e) {
let value = e.target.valueAsNumber;
if (!value) { value = 0; }
if (value < 0) { value = 0; }
if (value > 99999999) { value = 99999999; }
fileRenamer.fileEnumerationStart = value;
$('file_counter').set('value', value);
LocalPreferences.set('multirename_fileEnumerationStart', value);
fileRenamer.update();
});
// Setup Rename Operation Events
$('renameButton').addEvent('click', function(e) {
// Disable Search Options
$('multiRenameSearch').disabled = true;
$('use_regex_search').disabled = true;
$('match_all_occurrences').disabled = true;
$('case_sensitive').disabled = true;
// Disable Replace Options
$('multiRenameReplace').disabled = true;
$('applies_to_option').disabled = true;
$('include_files').disabled = true;
$('include_folders').disabled = true;
$('file_counter').disabled = true;
// Disable Rename Buttons
$('renameButton').disabled = true;
$('renameOptions').disabled = true;
// Clear error text
$('rename_error').set('text', '');
fileRenamer.rename();
});
fileRenamer.onRenamed = function(rows) {
// Disable Search Options
$('multiRenameSearch').disabled = false;
$('use_regex_search').disabled = false;
$('match_all_occurrences').disabled = false;
$('case_sensitive').disabled = false;
// Disable Replace Options
$('multiRenameReplace').disabled = false;
$('applies_to_option').disabled = false;
$('include_files').disabled = false;
$('include_folders').disabled = false;
$('file_counter').disabled = false;
// Disable Rename Buttons
$('renameButton').disabled = false;
$('renameOptions').disabled = false;
// Recreate table
let selectedRows = bulkRenameFilesTable.getSelectedRows().map(row => row.rowId.toString());
for (let renamedRow of rows) {
selectedRows = selectedRows.filter(selectedRow => selectedRow !== renamedRow.rowId.toString());
}
bulkRenameFilesTable.clear();
// Adjust file enumeration count by 1 when replacing single files to prevent naming conflicts
if (!fileRenamer.replaceAll) {
fileRenamer.fileEnumerationStart++;
$('file_counter').set('value', fileRenamer.fileEnumerationStart);
}
setupTable(selectedRows);
};
fileRenamer.onRenameError = function(err, row) {
if (err.xhr.status === 409) {
$('rename_error').set('text', `Rename failed: file or folder already exists \`${row.renamed}\``);
}
};
$('renameOptions').addEvent('change', function(e) {
const combobox = e.target;
const replaceOperation = combobox.value;
if (replaceOperation == "Replace") {
fileRenamer.replaceAll = false;
}
else if (replaceOperation == "Replace All") {
fileRenamer.replaceAll = true;
}
else {
fileRenamer.replaceAll = false;
}
LocalPreferences.set('multirename_replaceAll', fileRenamer.replaceAll);
$('renameButton').set('value', replaceOperation);
});
$('closeButton').addEvent('click', function() {
window.parent.closeWindows();
event.preventDefault();
});
// synchronize header scrolling to table body
$('bulkRenameFilesTableDiv').onscroll = function() {
const length = $(this).scrollLeft;
$('bulkRenameFilesTableFixedHeaderDiv').scrollLeft = length;
};
var handleTorrentFiles = function(files, selectedRows) {
const rows = files.map(function(file, index) {
const row = {
fileId: index,
checked: 1, // unchecked
path: file.name,
original: window.qBittorrent.Filesystem.fileName(file.name),
renamed: "",
size: file.size
};
return row;
});
addRowsToTable(rows, selectedRows);
};
var addRowsToTable = function(rows, selectedRows) {
let rowId = 0;
const rootNode = new window.qBittorrent.FileTree.FolderNode();
rootNode.autoCheckFolders = false;
rows.forEach(function(row) {
const pathItems = row.path.split(window.qBittorrent.Filesystem.PathSeparator);
pathItems.pop(); // remove last item (i.e. file name)
let parent = rootNode;
pathItems.forEach(function(folderName) {
if (folderName === '.unwanted') {
return;
}
let folderNode = null;
if (parent.children !== null) {
for (let i = 0; i < parent.children.length; ++i) {
const childFolder = parent.children[i];
if (childFolder.original === folderName) {
folderNode = childFolder;
break;
}
}
}
if (folderNode === null) {
folderNode = new window.qBittorrent.FileTree.FolderNode();
folderNode.autoCheckFolders = false;
folderNode.rowId = rowId;
folderNode.path = (parent.path === "")
? folderName
: [parent.path, folderName].join(window.qBittorrent.Filesystem.PathSeparator);
folderNode.checked = selectedRows.includes(rowId.toString()) ? 0 : 1;
folderNode.original = folderName;
folderNode.renamed = "";
folderNode.root = parent;
parent.addChild(folderNode);
++rowId;
}
parent = folderNode;
});
const childNode = new window.qBittorrent.FileTree.FileNode();
childNode.rowId = rowId;
childNode.path = row.path;
childNode.checked = selectedRows.includes(rowId.toString()) ? 0 : 1;
childNode.original = row.original;
childNode.renamed = "";
childNode.root = parent;
childNode.data = row;
parent.addChild(childNode);
++rowId;
});
bulkRenameFilesTable.populateTable(rootNode);
bulkRenameFilesTable.updateTable(false);
bulkRenameFilesTable.altRow();
if (selectedRows !== undefined) {
bulkRenameFilesTable.reselectRows(selectedRows);
}
fileRenamer.selectedFiles = bulkRenameFilesTable.getSelectedRows();
fileRenamer.update();
};
var setupTable = function(selectedRows) {
new Request.JSON({
url: new URI('api/v2/torrents/files?hash=' + data.hash),
noCache: true,
method: 'get',
onSuccess: function(files) {
if (files.length === 0) {
bulkRenameFilesTable.clear();
}
else {
handleTorrentFiles(files, selectedRows);
}
}
}).send();
};
setupTable(data.selectedRows);
</script>
</head>
<body style="min-width: 400px; min-height: 300px;">
<div style="padding: 0px 10px 0px 0px;">
<div style="float: left; height: 100%; width: 228px;">
<div class="formRow">
<input type="checkbox" id="multirename_rememberprefs_checkbox" onchange="LocalPreferences.set('multirename_rememberPreferences', this.checked);" />
<label for="multirename_rememberprefs_checkbox">Remember Multi-Rename settings</label>
</div>
<hr>
<textarea id="multiRenameSearch" placeholder="Search Files" style="width: calc(100% - 8px); resize: vertical; min-height: 30px;"></textarea>
<div class="formRow">
<input type="checkbox" id="use_regex_search" />
<label for="use_regex_search">Use regular expressions</label>
</div>
<div class="formRow">
<input type="checkbox" id="match_all_occurrences" />
<label for="match_all_occurrences">Match all occurrences</label>
</div>
<div class="formRow">
<input type="checkbox" id="case_sensitive" />
<label for="case_sensitive">Case sensitive</label>
</div>
<hr>
<textarea id="multiRenameReplace" placeholder="Replacement Input" style="width: calc(100% - 8px); resize: vertical; min-height: 30px;"></textarea>
<select id="applies_to_option" name="applies_to_option" style="width: 100%; margin-bottom: 5px;">
<option selected value="FilenameExtension">Filename + Extension</option>
<option value="Filename">Filename</option>
<option value="Extension">Extension</option>
</select>
<div class="formRow">
<input type="checkbox" id="include_files" checked />
<label for="include_files">Include files</label>
</div>
<div class="formRow">
<input type="checkbox" id="include_folders" />
<label for="include_folders">Include folders</label>
</div>
<div class="formRow">
<input type="number" min="0" max="99999999" value="0" id="file_counter" style="width: 80px;" />
<label for="file_counter">Enumerate Files</label>
</div>
</div>
<div id="operation_btns" style="position: absolute; left: 0; bottom: 0; margin: 0px 12px 36px 12px; width: 228px;background: #ffffff;padding: 0px 5px 10px 0px;">
<div style="overflow: auto;">
<span id="rename_error" style="float: unset; font-size: unset;"></span>
</div>
<hr>
<div style="width: 60%; float: left;">
<input id="renameButton" type="button" value="Replace" style="float: left; width: 86px;">
<select id="renameOptions" name="renameOptions" style="width: 22px;">
<option selected value="Replace">Replace</option>
<option value="Replace All">Replace All</option>
</select>
</div>
<input id="closeButton" type="button" value="Close" style="float: right; width: 30%;">
</div>
<div id="torrentFiles" class="panel" style="position: absolute; top: 0; right: 0; bottom: 0; left: 228px; margin: 35px 10px 45px 20px; border-bottom: 0">
<div id="bulkRenameFilesTableFixedHeaderDiv" class="dynamicTableFixedHeaderDiv">
<table class="dynamicTable">
<thead>
<tr class="dynamicTableHeader"></tr>
</thead>
</table>
</div>
<div id="bulkRenameFilesTableDiv" class="dynamicTableDiv">
<table class="dynamicTable">
<thead>
<tr class="dynamicTableHeader"></tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</body>
</html>

View File

@ -273,6 +273,7 @@ window.addEvent('load', function() {
$("stalled_uploading_filter").removeClass("selectedFilter"); $("stalled_uploading_filter").removeClass("selectedFilter");
$("stalled_downloading_filter").removeClass("selectedFilter"); $("stalled_downloading_filter").removeClass("selectedFilter");
$("checking_filter").removeClass("selectedFilter"); $("checking_filter").removeClass("selectedFilter");
$("moving_filter").removeClass("selectedFilter");
$("errored_filter").removeClass("selectedFilter"); $("errored_filter").removeClass("selectedFilter");
$(f + "_filter").addClass("selectedFilter"); $(f + "_filter").addClass("selectedFilter");
selected_filter = f; selected_filter = f;
@ -446,6 +447,7 @@ window.addEvent('load', function() {
updateFilter('stalled_uploading', 'Stalled Uploading (%1)'); updateFilter('stalled_uploading', 'Stalled Uploading (%1)');
updateFilter('stalled_downloading', 'Stalled Downloading (%1)'); updateFilter('stalled_downloading', 'Stalled Downloading (%1)');
updateFilter('checking', 'Checking (%1)'); updateFilter('checking', 'Checking (%1)');
updateFilter('moving', 'Moving (%1)');
updateFilter('errored', 'Errored (%1)'); updateFilter('errored', 'Errored (%1)');
}; };
@ -453,7 +455,7 @@ window.addEvent('load', function() {
const categoryList = $('categoryFilterList'); const categoryList = $('categoryFilterList');
if (!categoryList) if (!categoryList)
return; return;
categoryList.empty(); categoryList.getChildren().each(c => c.destroy());
const create_link = function(hash, text, count) { const create_link = function(hash, text, count) {
let display_name = text; let display_name = text;
@ -488,7 +490,19 @@ window.addEvent('load', function() {
Object.each(category_list, function(category) { Object.each(category_list, function(category) {
sortedCategories.push(category.name); sortedCategories.push(category.name);
}); });
sortedCategories.sort(); sortedCategories.sort((leftCategory, rightCategory) => {
const leftSegments = leftCategory.split('/');
const rightSegments = rightCategory.split('/');
for (let i = 0, iMax = Math.min(leftSegments.length, rightSegments.length); i < iMax; ++i) {
const compareResult = window.qBittorrent.Misc.naturalSortCollator.compare(
leftSegments[i], rightSegments[i]);
if (compareResult !== 0)
return compareResult;
}
return leftSegments.length - rightSegments.length;
});
for (let i = 0; i < sortedCategories.length; ++i) { for (let i = 0; i < sortedCategories.length; ++i) {
const categoryName = sortedCategories[i]; const categoryName = sortedCategories[i];
@ -526,8 +540,7 @@ window.addEvent('load', function() {
if (tagFilterList === null) if (tagFilterList === null)
return; return;
while (tagFilterList.firstChild !== null) tagFilterList.getChildren().each(c => c.destroy());
tagFilterList.removeChild(tagFilterList.firstChild);
const createLink = function(hash, text, count) { const createLink = function(hash, text, count) {
const html = '<a href="#" onclick="setTagFilter(' + hash + ');return false;">' const html = '<a href="#" onclick="setTagFilter(' + hash + ');return false;">'
@ -580,8 +593,7 @@ window.addEvent('load', function() {
if (trackerFilterList === null) if (trackerFilterList === null)
return; return;
while (trackerFilterList.firstChild !== null) trackerFilterList.getChildren().each(c => c.destroy());
trackerFilterList.removeChild(trackerFilterList.firstChild);
const createLink = function(hash, text, count) { const createLink = function(hash, text, count) {
const html = '<a href="#" onclick="setTrackerFilter(' + hash + ');return false;">' const html = '<a href="#" onclick="setTrackerFilter(' + hash + ');return false;">'
@ -1200,7 +1212,7 @@ window.addEvent('load', function() {
loadMethod: 'xhr', loadMethod: 'xhr',
contentURL: 'views/log.html', contentURL: 'views/log.html',
require: { require: {
css: ['css/lib/vanillaSelectBox.css'], css: ['css/vanillaSelectBox.css'],
js: ['scripts/lib/vanillaSelectBox.js'], js: ['scripts/lib/vanillaSelectBox.js'],
}, },
tabsURL: 'views/logTabs.html', tabsURL: 'views/logTabs.html',
@ -1512,6 +1524,8 @@ function setupCopyEventHandler() {
return copyMagnetLinkFN(); return copyMagnetLinkFN();
case "copyID": case "copyID":
return copyIdFN(); return copyIdFN();
case "copyComment":
return copyCommentFN();
default: default:
return ""; return "";
} }

File diff suppressed because it is too large Load Diff

View File

@ -816,8 +816,7 @@ window.qBittorrent.DynamicTable = (function() {
let rowPos = rows.length; let rowPos = rows.length;
while ((rowPos < trs.length) && (trs.length > 0)) { while ((rowPos < trs.length) && (trs.length > 0)) {
trs[trs.length - 1].dispose(); trs.pop().destroy();
trs.pop();
} }
}, },
@ -839,7 +838,7 @@ window.qBittorrent.DynamicTable = (function() {
this.selectedRows.erase(rowId); this.selectedRows.erase(rowId);
const tr = this.getTrByRowId(rowId); const tr = this.getTrByRowId(rowId);
if (tr !== null) { if (tr !== null) {
tr.dispose(); tr.destroy();
this.rows.erase(rowId); this.rows.erase(rowId);
return true; return true;
} }
@ -851,8 +850,7 @@ window.qBittorrent.DynamicTable = (function() {
this.rows.empty(); this.rows.empty();
const trs = this.tableBody.getElements('tr'); const trs = this.tableBody.getElements('tr');
while (trs.length > 0) { while (trs.length > 0) {
trs[trs.length - 1].dispose(); trs.pop().destroy();
trs.pop();
} }
}, },
@ -945,6 +943,7 @@ window.qBittorrent.DynamicTable = (function() {
this.newColumn('seen_complete', '', 'Last Seen Complete', 100, false); this.newColumn('seen_complete', '', 'Last Seen Complete', 100, false);
this.newColumn('last_activity', '', 'Last Activity', 100, false); this.newColumn('last_activity', '', 'Last Activity', 100, false);
this.newColumn('availability', '', 'Availability', 100, false); this.newColumn('availability', '', 'Availability', 100, false);
this.newColumn('reannounce', '', 'Reannounce In', 100, false);
this.columns['state_icon'].onclick = ''; this.columns['state_icon'].onclick = '';
this.columns['state_icon'].dataProperties[0] = 'state'; this.columns['state_icon'].dataProperties[0] = 'state';
@ -1001,10 +1000,14 @@ window.qBittorrent.DynamicTable = (function() {
case "checkingUP": case "checkingUP":
case "queuedForChecking": case "queuedForChecking":
case "checkingResumeData": case "checkingResumeData":
case "moving":
state = "force-recheck"; state = "force-recheck";
img_path = "images/force-recheck.svg"; img_path = "images/force-recheck.svg";
break; break;
case "moving":
state = "moving";
img_path = "images/set-location.svg";
break;
case "error":
case "unknown": case "unknown":
case "missingFiles": case "missingFiles":
state = "error"; state = "error";
@ -1331,6 +1334,13 @@ window.qBittorrent.DynamicTable = (function() {
td.set('text', value); td.set('text', value);
td.set('title', value); td.set('title', value);
}; };
// reannounce
this.columns['reannounce'].updateTd = function(td, row) {
const time = window.qBittorrent.Misc.friendlyDuration(this.getRowValue(row));
td.set('text', time);
td.set('title', time);
};
}, },
applyFilter: function(row, filterName, categoryHash, tagHash, trackerHash, filterTerms) { applyFilter: function(row, filterName, categoryHash, tagHash, trackerHash, filterTerms) {
@ -1387,6 +1397,10 @@ window.qBittorrent.DynamicTable = (function() {
if (state !== 'checkingUP' && state !== 'checkingDL' && state !== 'checkingResumeData') if (state !== 'checkingUP' && state !== 'checkingDL' && state !== 'checkingResumeData')
return false; return false;
break; break;
case 'moving':
if (state !== 'moving')
return false;
break;
case 'errored': case 'errored':
if (state != 'error' && state != "unknown" && state != "missingFiles") if (state != 'error' && state != "unknown" && state != "missingFiles")
return false; return false;
@ -1567,7 +1581,7 @@ window.qBittorrent.DynamicTable = (function() {
if (!country_code) { if (!country_code) {
if (td.getChildren('img').length > 0) if (td.getChildren('img').length > 0)
td.getChildren('img')[0].dispose(); td.getChildren('img')[0].destroy();
return; return;
} }

View File

@ -2262,7 +2262,8 @@ MUI.Window = new Class({
'styles': { 'styles': {
'position': 'absolute', // This is set here to make theme transitions smoother 'position': 'absolute', // This is set here to make theme transitions smoother
'top': 0, 'top': 0,
'left': 0 'left': 0,
'height': 'auto'
} }
}).inject(this.windowEl); }).inject(this.windowEl);

View File

@ -89,6 +89,7 @@ let copyNameFN = function() {};
let copyInfohashFN = function(policy) {}; let copyInfohashFN = function(policy) {};
let copyMagnetLinkFN = function() {}; let copyMagnetLinkFN = function() {};
let copyIdFN = function() {}; let copyIdFN = function() {};
let copyCommentFN = function() {};
let setQueuePositionFN = function() {}; let setQueuePositionFN = function() {};
let exportTorrentFN = function() {}; let exportTorrentFN = function() {};
@ -348,7 +349,7 @@ const initializeWindows = function() {
const id = 'statisticspage'; const id = 'statisticspage';
new MochaUI.Window({ new MochaUI.Window({
id: id, id: id,
title: 'Statistics', title: 'Statistics]',
loadMethod: 'xhr', loadMethod: 'xhr',
contentURL: new URI("views/statistics.html").toString(), contentURL: new URI("views/statistics.html").toString(),
maximizable: false, maximizable: false,
@ -1005,6 +1006,21 @@ const initializeWindows = function() {
return torrentsTable.selectedRowsIds().join("\n"); return torrentsTable.selectedRowsIds().join("\n");
}; };
copyCommentFN = function() {
const selectedRows = torrentsTable.selectedRowsIds();
const comments = [];
if (selectedRows.length > 0) {
const rows = torrentsTable.getFilteredAndSortedRows();
for (let i = 0; i < selectedRows.length; ++i) {
const hash = selectedRows[i];
const comment = rows[hash].full_data.comment;
if (comment && (comment !== ""))
comments.push(comment);
}
}
return comments.join("\n---------\n");
};
exportTorrentFN = async function() { exportTorrentFN = async function() {
const hashes = torrentsTable.selectedRowsIds(); const hashes = torrentsTable.selectedRowsIds();
for (const hash of hashes) { for (const hash of hashes) {

View File

@ -173,7 +173,7 @@ window.qBittorrent.PropFiles = (function() {
elem.set('value', priority.toString()); elem.set('value', priority.toString());
elem.set('html', html); elem.set('html', html);
if (selected) if (selected)
elem.setAttribute('selected', ''); elem.selected = true;
return elem; return elem;
}; };

View File

@ -50,8 +50,7 @@ window.qBittorrent.PropWebseeds = (function() {
removeRow: function(url) { removeRow: function(url) {
if (this.rows.has(url)) { if (this.rows.has(url)) {
const tr = this.rows.get(url); this.rows.get(url).destroy();
tr.dispose();
this.rows.erase(url); this.rows.erase(url);
return true; return true;
} }

View File

@ -40,16 +40,19 @@
const values = { const values = {
ratioLimit: window.qBittorrent.Misc.friendlyFloat(origValues[0], 2), ratioLimit: window.qBittorrent.Misc.friendlyFloat(origValues[0], 2),
seedingTimeLimit: parseInt(origValues[1]), seedingTimeLimit: parseInt(origValues[1]),
maxRatio: window.qBittorrent.Misc.friendlyFloat(origValues[2], 2), inactiveSeedingTimeLimit: parseInt(origValues[2]),
maxSeedingTime: parseInt(origValues[3]) maxRatio: window.qBittorrent.Misc.friendlyFloat(origValues[3], 2),
maxSeedingTime: parseInt(origValues[4]),
maxInactiveSeedingTime: parseInt(origValues[5])
}; };
// select default when orig values not passed. using double equals to compare string and int // select default when orig values not passed. using double equals to compare string and int
if ((origValues[0] === "") || ((values.ratioLimit == UseGlobalLimit) && (values.seedingTimeLimit == UseGlobalLimit))) { if ((origValues[0] === "") || ((values.ratioLimit == UseGlobalLimit) && (values.seedingTimeLimit == UseGlobalLimit))
&& (values.inactiveSeedingTimeLimit == UseGlobalLimit)) {
// use default option // use default option
setSelectedRadioValue('shareLimit', 'default'); setSelectedRadioValue('shareLimit', 'default');
} }
else if ((values.maxRatio == NoLimit) && (values.maxSeedingTime == NoLimit)) { else if ((values.maxRatio == NoLimit) && (values.maxSeedingTime == NoLimit) && (values.maxInactiveSeedingTime == NoLimit)) {
setSelectedRadioValue('shareLimit', 'none'); setSelectedRadioValue('shareLimit', 'none');
// TODO set input boxes to *global* max ratio and seeding time // TODO set input boxes to *global* max ratio and seeding time
} }
@ -60,8 +63,12 @@
$('ratio').set('value', values.ratioLimit); $('ratio').set('value', values.ratioLimit);
} }
if (values.seedingTimeLimit >= 0) { if (values.seedingTimeLimit >= 0) {
$('setMinutes').set('checked', true); $('setTotalMinutes').set('checked', true);
$('minutes').set('value', values.seedingTimeLimit); $('totalMinutes').set('value', values.seedingTimeLimit);
}
if (values.inactiveSeedingTimeLimit >= 0) {
$('setInactiveMinutes').set('checked', true);
$('inactiveMinutes').set('value', values.inactiveSeedingTimeLimit);
} }
} }
@ -78,16 +85,18 @@
const shareLimit = getSelectedRadioValue('shareLimit'); const shareLimit = getSelectedRadioValue('shareLimit');
let ratioLimitValue = 0.00; let ratioLimitValue = 0.00;
let seedingTimeLimitValue = 0; let seedingTimeLimitValue = 0;
let inactiveSeedingTimeLimitValue = 0;
if (shareLimit === 'default') { if (shareLimit === 'default') {
ratioLimitValue = seedingTimeLimitValue = UseGlobalLimit; ratioLimitValue = seedingTimeLimitValue = inactiveSeedingTimeLimitValue = UseGlobalLimit;
} }
else if (shareLimit === 'none') { else if (shareLimit === 'none') {
ratioLimitValue = seedingTimeLimitValue = NoLimit; ratioLimitValue = seedingTimeLimitValue = inactiveSeedingTimeLimitValue = NoLimit;
} }
else if (shareLimit === 'custom') { else if (shareLimit === 'custom') {
ratioLimitValue = $('setRatio').get('checked') ? $('ratio').get('value') : -1; ratioLimitValue = $('setRatio').get('checked') ? $('ratio').get('value') : -1;
seedingTimeLimitValue = $('setMinutes').get('checked') ? $('minutes').get('value') : -1; seedingTimeLimitValue = $('setTotalMinutes').get('checked') ? $('totalMinutes').get('value') : -1;
inactiveSeedingTimeLimitValue = $('setInactiveMinutes').get('checked') ? $('inactiveMinutes').get('value') : -1;
} }
else { else {
return false; return false;
@ -99,7 +108,8 @@
data: { data: {
hashes: hashesList.join('|'), hashes: hashesList.join('|'),
ratioLimit: ratioLimitValue, ratioLimit: ratioLimitValue,
seedingTimeLimit: seedingTimeLimitValue seedingTimeLimit: seedingTimeLimitValue,
inactiveSeedingTimeLimit: inactiveSeedingTimeLimitValue
}, },
onComplete: function() { onComplete: function() {
window.parent.closeWindows(); window.parent.closeWindows();
@ -132,7 +142,8 @@
function shareLimitChanged() { function shareLimitChanged() {
const customShareLimit = getSelectedRadioValue('shareLimit') === 'custom'; const customShareLimit = getSelectedRadioValue('shareLimit') === 'custom';
$('setRatio').set('disabled', !customShareLimit); $('setRatio').set('disabled', !customShareLimit);
$('setMinutes').set('disabled', !customShareLimit); $('setTotalMinutes').set('disabled', !customShareLimit);
$('setInactiveMinutes').set('disabled', !customShareLimit);
enableInputBoxes(); enableInputBoxes();
@ -141,22 +152,24 @@
function enableInputBoxes() { function enableInputBoxes() {
$('ratio').set('disabled', ($('setRatio').get('disabled') || !$('setRatio').get('checked'))); $('ratio').set('disabled', ($('setRatio').get('disabled') || !$('setRatio').get('checked')));
$('minutes').set('disabled', ($('setMinutes').get('disabled') || !$('setMinutes').get('checked'))); $('totalMinutes').set('disabled', ($('setTotalMinutes').get('disabled') || !$('setTotalMinutes').get('checked')));
$('inactiveMinutes').set('disabled', ($('setInactiveMinutes').get('disabled') || !$('setInactiveMinutes').get('checked')));
$('save').set('disabled', !isFormValid()); $('save').set('disabled', !isFormValid());
} }
function isFormValid() { function isFormValid() {
return !((getSelectedRadioValue('shareLimit') === 'custom') && !$('setRatio').get('checked') && !$('setMinutes').get('checked')); return !((getSelectedRadioValue('shareLimit') === 'custom') && !$('setRatio').get('checked')
&& !$('setTotalMinutes').get('checked') && !$('setInactiveMinutes').get('checked'));
} }
</script> </script>
</head> </head>
<body> <body>
<div style="padding: 10px 10px 0px 10px;"> <div style="padding: 10px 10px 0px 10px;">
<input type="radio" name="shareLimit" id="default" value="default" onchange="shareLimitChanged()" checked style="margin-bottom: 5px;" />Use global share limit</br> <input type="radio" name="shareLimit" id="default" value="default" onchange="shareLimitChanged()" checked style="margin-bottom: 5px;" />Use global share limit<br />
<input type="radio" name="shareLimit" value="none" onchange="shareLimitChanged()" style="margin-bottom: 5px;" />Set no share limit</br> <input type="radio" name="shareLimit" value="none" onchange="shareLimitChanged()" style="margin-bottom: 5px;" />Set no share limit<br />
<input type="radio" name="shareLimit" value="custom" onchange="shareLimitChanged()" style="margin-bottom: 5px;" />Set share limit to</br> <input type="radio" name="shareLimit" value="custom" onchange="shareLimitChanged()" style="margin-bottom: 5px;" />Set share limit to<br />
<div style="margin-left: 40px; margin-bottom: 5px;"> <div style="margin-left: 40px; margin-bottom: 5px;">
<input type="checkbox" id="setRatio" class="shareLimitInput" onclick="enableInputBoxes()" /> <input type="checkbox" id="setRatio" class="shareLimitInput" onclick="enableInputBoxes()" />
@ -164,12 +177,17 @@
<input type="number" id="ratio" value="0.00" step=".01" min="0" max="9999" class="shareLimitInput" /> <input type="number" id="ratio" value="0.00" step=".01" min="0" max="9999" class="shareLimitInput" />
</div> </div>
<div style="margin-left: 40px; margin-bottom: 5px;"> <div style="margin-left: 40px; margin-bottom: 5px;">
<input type="checkbox" id="setMinutes" class="shareLimitInput" onclick="enableInputBoxes()" /> <input type="checkbox" id="setTotalMinutes" class="shareLimitInput" onclick="enableInputBoxes()" />
<label for="setMinutes">minutes</label> <label for="setTotalMinutes">total minutes</label>
<input type="number" id="minutes" value="0" step="1" min="0" max="525600" class="shareLimitInput" /> <input type="number" id="totalMinutes" value="0" step="1" min="0" max="525600" class="shareLimitInput" />
</div>
<div style="margin-left: 40px; margin-bottom: 5px;">
<input type="checkbox" id="setInactiveMinutes" class="shareLimitInput" onclick="enableInputBoxes()" />
<label for="setInactiveMinutes">inactive minutes</label>
<input type="number" id="inactiveMinutes" value="0" step="1" min="0" max="525600" class="shareLimitInput" />
</div> </div>
<div style="text-align: center; padding-top: 10px;"> <div style="text-align: center; padding-top: 10px;">
<input class="button" type="button" value="Save" id="save" /> <input type="button" value="Save" id="save" />
</div> </div>
</div> </div>
</body> </body>

View File

@ -12,24 +12,21 @@
</head> </head>
<body> <body>
<iframe id="upload_frame" name="upload_frame" class="invisible" src="about:blank"></iframe> <iframe id="upload_frame" name="upload_frame" class="invisible" title="" src="about:blank"></iframe>
<form action="api/v2/torrents/add" enctype="multipart/form-data" method="post" id="uploadForm" style="text-align: center;" target="upload_frame" autocorrect="off" autocapitalize="none"> <form action="api/v2/torrents/add" enctype="multipart/form-data" method="post" id="uploadForm" style="text-align: center;" target="upload_frame" autocorrect="off" autocapitalize="none">
<div style="margin-top: 25px; display: inline-block; border: 1px solid lightgrey; border-radius: 4px;"> <div style="margin-top: 25px; display: inline-block; border: 1px solid lightgrey; border-radius: 4px;">
<label class="custom-file-upload"> <input type="file" id="fileselect" accept=".torrent, application/x-bittorrent" name="fileselect[]" multiple />
<input type="file" class="fileselect" id="fileselect" accept="application/x-bittorrent, .torrent" name="fileselect[]" multiple />
Upload Files
</label>
</div> </div>
<fieldset class="settings" style="border: 0; text-align: left; margin-top: 12px;"> <fieldset class="settings" style="border: 0; text-align: left; margin-top: 12px;">
<table style="margin: auto;"> <table style="margin: auto;">
<tr> <tr>
<td> <td>
<label for="autoTMM">Torrent Management Mode:</label> <label for="autoTMM">(Torrent Management Mode:</label>
</td> </td>
<td> <td>
<select id="autoTMM" name="autoTMM" onchange="qBittorrent.Download.changeTMM(this)"> <select id="autoTMM" name="autoTMM" onchange="qBittorrent.Download.changeTMM(this)">
<option selected value="false">Manual</option> <option selected value="false">(Manual</option>
<option value="true">Automatic</option> <option value="true">(Automatic</option>
</select> </select>
</td> </td>
</tr> </tr>
@ -73,7 +70,15 @@
</tr> </tr>
<tr> <tr>
<td> <td>
<label for="stopCondition">Stop condition:</label> <label for="addToTopOfQueue">(Add to top of queue</label>
</td>
<td>
<input type="checkbox" id="addToTopOfQueue" name="addToTopOfQueue" value="true" />
</td>
</tr>
<tr>
<td>
<label for="stopCondition">(Stop condition:</label>
</td> </td>
<td> <td>
<select id="stopCondition" name="stopCondition"> <select id="stopCondition" name="stopCondition">
@ -162,6 +167,10 @@
if (submitted) if (submitted)
window.parent.closeWindows(); window.parent.closeWindows();
}); });
if ((Browser.platform === 'ios') || ((Browser.platform === 'mac') && (navigator.maxTouchPoints > 1))) {
$('fileselect').accept = ".torrent";
}
</script> </script>
<div id="upload_spinner" class="mochaSpinner"></div> <div id="upload_spinner" class="mochaSpinner"></div>
</body> </body>

View File

@ -15,6 +15,7 @@
<li id="stalled_uploading_filter"><a href="#" onclick="setFilter('stalled_uploading');return false;"><img src="images/stalledUP.svg" alt="Stalled Uploading" />Stalled Uploading (0)</a></li> <li id="stalled_uploading_filter"><a href="#" onclick="setFilter('stalled_uploading');return false;"><img src="images/stalledUP.svg" alt="Stalled Uploading" />Stalled Uploading (0)</a></li>
<li id="stalled_downloading_filter"><a href="#" onclick="setFilter('stalled_downloading');return false;"><img src="images/stalledDL.svg" alt="Stalled Downloading" />Stalled Downloading (0)</a></li> <li id="stalled_downloading_filter"><a href="#" onclick="setFilter('stalled_downloading');return false;"><img src="images/stalledDL.svg" alt="Stalled Downloading" />Stalled Downloading (0)</a></li>
<li id="checking_filter"><a href="#" onclick="setFilter('checking'); return false;"><img src="images/force-recheck.svg" alt="Checking" />Checking (0)</a></li> <li id="checking_filter"><a href="#" onclick="setFilter('checking'); return false;"><img src="images/force-recheck.svg" alt="Checking" />Checking (0)</a></li>
<li id="moving_filter"><a href="#" onclick="setFilter('moving'); return false;"><img src="images/set-location.svg" alt="Moving" />Moving (0)</a></li>
<li id="errored_filter"><a href="#" onclick="setFilter('errored');return false;"><img src="images/error.svg" alt="Errored" />Errored (0)</a></li> <li id="errored_filter"><a href="#" onclick="setFilter('errored');return false;"><img src="images/error.svg" alt="Errored" />Errored (0)</a></li>
</ul> </ul>
</div> </div>
@ -63,6 +64,9 @@
createCategory: function(element, ref) { createCategory: function(element, ref) {
createCategoryFN(); createCategoryFN();
}, },
createSubcategory: function(element, ref) {
createSubcategoryFN(element.id);
},
editCategory: function(element, ref) { editCategory: function(element, ref) {
editCategoryFN(element.id); editCategoryFN(element.id);
}, },

View File

@ -191,10 +191,10 @@
const init = () => { const init = () => {
$('logLevelSelect').getElements('option').each((x) => { $('logLevelSelect').getElements('option').each((x) => {
if (selectedLogLevels.indexOf(x.value.toString()) !== -1) { if (selectedLogLevels.indexOf(x.value.toString()) !== -1) {
x.setAttribute('selected', ''); x.selected = true;
} }
else { else {
x.removeAttribute('selected'); x.selected = false;
} }
}); });
@ -424,4 +424,6 @@
return exports(); return exports();
})(); })();
Object.freeze(window.qBittorrent.Log);
</script> </script>

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,8 @@
<!-- preferences --> <!-- preferences -->
<div class="toolbarTabs"> <div class="toolbarTabs">
<ul id="preferencesTabs" class="tab-menu"> <ul id="preferencesTabs" class="tab-menu">
<li id="PrefDownloadsLink" class="selected"><a>Downloads</a></li> <li id="PrefBehaviorLink" class="selected"><a>Behavior</a></li>
<li id="PrefDownloadsLink"><a>Downloads</a></li>
<li id="PrefConnectionLink"><a>Connection</a></li> <li id="PrefConnectionLink"><a>Connection</a></li>
<li id="PrefSpeedLink"><a>Speed</a></li> <li id="PrefSpeedLink"><a>Speed</a></li>
<li id="PrefBittorrentLink"><a>BitTorrent</a></li> <li id="PrefBittorrentLink"><a>BitTorrent</a></li>
@ -19,6 +20,10 @@
// Tabs // Tabs
MochaUI.initializeTabs('preferencesTabs'); MochaUI.initializeTabs('preferencesTabs');
$('PrefBehaviorLink').addEvent('click', function(e) {
$$('.PrefTab').addClass('invisible');
$('BehaviorTab').removeClass('invisible');
});
$('PrefDownloadsLink').addEvent('click', function(e) { $('PrefDownloadsLink').addEvent('click', function(e) {
$$('.PrefTab').addClass('invisible'); $$('.PrefTab').addClass('invisible');
$('DownloadsTab').removeClass('invisible'); $('DownloadsTab').removeClass('invisible');

View File

@ -211,7 +211,16 @@ Supports the formats: S01E01, 1x1, 2017.12.31 and 31.12.2017 (Date formats also
</select> </select>
</td> </td>
</tr> </tr>
<tr>
<td>
<label class="noWrap">Add Tags:</label>
</td>
<td class="fullWidth">
<input type="text" id="ruleAddTags" class="fullWidth" autocapitalize="none" autocorrect="off" />
</td>
</tr>
</table> </table>
<div class="formRow"> <div class="formRow">
<input disabled type="checkbox" id="savetoDifferentDir" /> <input disabled type="checkbox" id="savetoDifferentDir" />
<label for="savetoDifferentDir">Save to a Different Directory</label> <label for="savetoDifferentDir">Save to a Different Directory</label>
@ -580,6 +589,16 @@ Supports the formats: S01E01, 1x1, 2017.12.31 and 31.12.2017 (Date formats also
rulesList[rule].assignedCategory = $('assignCategoryCombobox').value; rulesList[rule].assignedCategory = $('assignCategoryCombobox').value;
rulesList[rule].savePath = $('savetoDifferentDir').checked ? $('saveToText').value : ''; rulesList[rule].savePath = $('savetoDifferentDir').checked ? $('saveToText').value : '';
rulesList[rule].ignoreDays = parseInt($('ignoreDaysValue').value); rulesList[rule].ignoreDays = parseInt($('ignoreDaysValue').value);
rulesList[rule].affectedFeeds = rssDownloaderFeedSelectionTable.rows.filter((row) => row.full_data.checked)
.map((row) => row.full_data.url)
.getValues();
rulesList[rule].torrentParams.category = $('assignCategoryCombobox').value;
rulesList[rule].torrentParams.tags = $('ruleAddTags').value.split(',');
if ($('savetoDifferentDir').checked) {
rulesList[rule].torrentParams.save_path = $('saveToText').value;
rulesList[rule].torrentParams.use_auto_tmm = false;
}
switch ($('addPausedCombobox').value) { switch ($('addPausedCombobox').value) {
case 'default': case 'default':
@ -666,6 +685,7 @@ Supports the formats: S01E01, 1x1, 2017.12.31 and 31.12.2017 (Date formats also
$('episodeFilterText').disabled = true; $('episodeFilterText').disabled = true;
$('useSmartFilter').disabled = true; $('useSmartFilter').disabled = true;
$('assignCategoryCombobox').disabled = true; $('assignCategoryCombobox').disabled = true;
$('ruleAddTags').disabled = true;
$('savetoDifferentDir').disabled = true; $('savetoDifferentDir').disabled = true;
$('saveToText').disabled = true; $('saveToText').disabled = true;
$('ignoreDaysValue').disabled = true; $('ignoreDaysValue').disabled = true;
@ -679,6 +699,7 @@ Supports the formats: S01E01, 1x1, 2017.12.31 and 31.12.2017 (Date formats also
$('episodeFilterText').value = ''; $('episodeFilterText').value = '';
$('useSmartFilter').checked = false; $('useSmartFilter').checked = false;
$('assignCategoryCombobox').value = 'default'; $('assignCategoryCombobox').value = 'default';
$('ruleAddTags').value = '';
$('savetoDifferentDir').checked = false; $('savetoDifferentDir').checked = false;
$('saveToText').value = ''; $('saveToText').value = '';
$('ignoreDaysValue').value = 0; $('ignoreDaysValue').value = 0;
@ -701,6 +722,7 @@ Supports the formats: S01E01, 1x1, 2017.12.31 and 31.12.2017 (Date formats also
$('episodeFilterText').disabled = false; $('episodeFilterText').disabled = false;
$('useSmartFilter').disabled = false; $('useSmartFilter').disabled = false;
$('assignCategoryCombobox').disabled = false; $('assignCategoryCombobox').disabled = false;
$('ruleAddTags').disabled = false;
$('savetoDifferentDir').disabled = false; $('savetoDifferentDir').disabled = false;
$('savetoDifferentDir').checked = rulesList[ruleName].savePath ? false : true; $('savetoDifferentDir').checked = rulesList[ruleName].savePath ? false : true;
$('saveToText').disabled = rulesList[ruleName].savePath ? false : true; $('saveToText').disabled = rulesList[ruleName].savePath ? false : true;

View File

@ -48,13 +48,13 @@ function submitLoginForm() {
if ((xhr.status === 200) && (xhr.responseText === "Ok.")) if ((xhr.status === 200) && (xhr.responseText === "Ok."))
location.reload(true); location.reload(true);
else else
errorMsgElement.textContent = 'QBT_TR(Invalid Username or Password.)QBT_TR[CONTEXT=HttpServer]'; errorMsgElement.textContent = 'Invalid Username or Password.';
} }
}); });
xhr.addEventListener('error', function() { xhr.addEventListener('error', function() {
errorMsgElement.textContent = (xhr.responseText !== "") errorMsgElement.textContent = (xhr.responseText !== "")
? xhr.responseText ? xhr.responseText
: 'QBT_TR(Unable to log in, qBittorrent is probably unreachable.)QBT_TR[CONTEXT=HttpServer]'; : 'Unable to log in, qBittorrent is probably unreachable.';
}); });
const usernameElement = document.getElementById('username'); const usernameElement = document.getElementById('username');