mirror of
https://github.com/d47081/qBittorrent.git
synced 2025-01-11 07:18:08 +00:00
Implement tag management for WebUI
This commit is contained in:
parent
cf2e7d77d6
commit
b530e19a44
@ -445,6 +445,12 @@ void SyncController::maindataAction()
|
||||
|
||||
data["categories"] = categories;
|
||||
|
||||
QVariantList tags;
|
||||
for (const QString &tag : asConst(session->tags()))
|
||||
tags << tag;
|
||||
|
||||
data["tags"] = tags;
|
||||
|
||||
QVariantMap serverState = getTranserInfo();
|
||||
serverState[KEY_TRANSFER_FREESPACEONDISK] = getFreeDiskSpace();
|
||||
serverState[KEY_SYNC_MAINDATA_QUEUEING] = session->isQueueingSystemEnabled();
|
||||
|
@ -1041,3 +1041,67 @@ void TorrentsController::categoriesAction()
|
||||
|
||||
setResult(categories);
|
||||
}
|
||||
|
||||
void TorrentsController::addTagsAction()
|
||||
{
|
||||
checkParams({"hashes", "tags"});
|
||||
|
||||
const QStringList hashes {params()["hashes"].split('|')};
|
||||
const QStringList tags {params()["tags"].split(',', QString::SkipEmptyParts)};
|
||||
|
||||
for (const QString &tag : tags) {
|
||||
const QString tagTrimmed {tag.trimmed()};
|
||||
applyToTorrents(hashes, [&tagTrimmed](BitTorrent::TorrentHandle *const torrent)
|
||||
{
|
||||
torrent->addTag(tagTrimmed);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void TorrentsController::removeTagsAction()
|
||||
{
|
||||
checkParams({"hashes"});
|
||||
|
||||
const QStringList hashes {params()["hashes"].split('|')};
|
||||
const QStringList tags {params()["tags"].split(',', QString::SkipEmptyParts)};
|
||||
|
||||
for (const QString &tag : tags) {
|
||||
const QString tagTrimmed {tag.trimmed()};
|
||||
applyToTorrents(hashes, [&tagTrimmed](BitTorrent::TorrentHandle *const torrent)
|
||||
{
|
||||
torrent->removeTag(tagTrimmed);
|
||||
});
|
||||
}
|
||||
|
||||
if (tags.isEmpty()) {
|
||||
applyToTorrents(hashes, [](BitTorrent::TorrentHandle *const torrent)
|
||||
{
|
||||
torrent->removeAllTags();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void TorrentsController::createTagsAction()
|
||||
{
|
||||
checkParams({"tags"});
|
||||
|
||||
const QStringList tags {params()["tags"].split(',', QString::SkipEmptyParts)};
|
||||
|
||||
for (const QString &tag : tags)
|
||||
BitTorrent::Session::instance()->addTag(tag.trimmed());
|
||||
}
|
||||
|
||||
void TorrentsController::deleteTagsAction()
|
||||
{
|
||||
checkParams({"tags"});
|
||||
|
||||
const QStringList tags {params()["tags"].split(',', QString::SkipEmptyParts)};
|
||||
for (const QString &tag : tags)
|
||||
BitTorrent::Session::instance()->removeTag(tag.trimmed());
|
||||
}
|
||||
|
||||
void TorrentsController::tagsAction()
|
||||
{
|
||||
const QStringList tags = BitTorrent::Session::instance()->tags().toList();
|
||||
setResult(QJsonArray::fromStringList(tags));
|
||||
}
|
||||
|
@ -56,6 +56,11 @@ private slots:
|
||||
void editCategoryAction();
|
||||
void removeCategoriesAction();
|
||||
void categoriesAction();
|
||||
void addTagsAction();
|
||||
void removeTagsAction();
|
||||
void createTagsAction();
|
||||
void deleteTagsAction();
|
||||
void tagsAction();
|
||||
void addAction();
|
||||
void deleteAction();
|
||||
void addTrackersAction();
|
||||
|
@ -258,6 +258,11 @@ a.propButton img {
|
||||
-ms-interpolation-mode: bicubic;
|
||||
}
|
||||
|
||||
.contextMenu li input[type=checkbox] {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
}
|
||||
|
||||
/* Sliders */
|
||||
|
||||
.slider {
|
||||
|
@ -21,6 +21,13 @@
|
||||
<ul class="filterList" id="categoryFilterList">
|
||||
</ul>
|
||||
</div>
|
||||
<div class="filterWrapper">
|
||||
<span class="filterTitle" onclick="toggleFilterDisplay('tag');">
|
||||
<img src="images/qbt-theme/go-down.svg">QBT_TR(Tags)QBT_TR[CONTEXT=TransferListFiltersWidget]
|
||||
</span>
|
||||
<ul class="filterList" id="tagFilterList">
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
'use strict';
|
||||
@ -60,9 +67,44 @@
|
||||
}
|
||||
});
|
||||
|
||||
const tagsFilterContextMenu = new TagsFilterContextMenu({
|
||||
targets: '.tagsFilterContextMenuTarget',
|
||||
menu: 'tagsFilterMenu',
|
||||
actions: {
|
||||
createTag: function(element, ref) {
|
||||
createTagFN();
|
||||
},
|
||||
deleteTag: function(element, ref) {
|
||||
removeTagFN(element.id);
|
||||
},
|
||||
deleteUnusedTags: function(element, ref) {
|
||||
deleteUnusedTagsFN();
|
||||
},
|
||||
startTorrentsByTag: function(element, ref) {
|
||||
startTorrentsByTagFN(element.id);
|
||||
},
|
||||
pauseTorrentsByTag: function(element, ref) {
|
||||
pauseTorrentsByTagFN(element.id);
|
||||
},
|
||||
deleteTorrentsByTag: function(element, ref) {
|
||||
deleteTorrentsByTagFN(element.id);
|
||||
}
|
||||
},
|
||||
offsets: {
|
||||
x: -15,
|
||||
y: 2
|
||||
},
|
||||
onShow: function() {
|
||||
this.options.element.firstChild.click();
|
||||
}
|
||||
});
|
||||
|
||||
if (localStorage.getItem('filter_status_collapsed') === "true")
|
||||
toggleFilterDisplay('status');
|
||||
|
||||
if (localStorage.getItem('filter_category_collapsed') === "true")
|
||||
toggleFilterDisplay('category');
|
||||
|
||||
if (localStorage.getItem('filter_tag_collapsed') === "true")
|
||||
toggleFilterDisplay('tag');
|
||||
</script>
|
||||
|
@ -132,6 +132,10 @@
|
||||
<a href="#Category" class="arrow-right"><img src="images/qbt-theme/view-categories.svg" alt="QBT_TR(Category)QBT_TR[CONTEXT=TransferListWidget]"/> QBT_TR(Category)QBT_TR[CONTEXT=TransferListWidget]</a>
|
||||
<ul id="contextCategoryList" class="scrollableMenu"></ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#Tags" class="arrow-right"><img src="images/qbt-theme/view-categories.svg" alt="QBT_TR(Tags)QBT_TR[CONTEXT=TransferListWidget]"/> QBT_TR(Tags)QBT_TR[CONTEXT=TransferListWidget]</a>
|
||||
<ul id="contextTagList" class="scrollableMenu"></ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#autoTorrentManagement"><img src="images/qbt-theme/checked.svg" alt="QBT_TR(Automatic Torrent Management)QBT_TR[CONTEXT=TransferListWidget]"/> QBT_TR(Automatic Torrent Management)QBT_TR[CONTEXT=TransferListWidget]</a>
|
||||
</li>
|
||||
@ -170,6 +174,14 @@
|
||||
<li><a href="#pauseTorrentsByCategory"><img src="images/qbt-theme/media-playback-pause.svg" alt="QBT_TR(Pause torrents)QBT_TR[CONTEXT=CategoryFilterWidget]"/> QBT_TR(Pause torrents)QBT_TR[CONTEXT=CategoryFilterWidget]</a></li>
|
||||
<li><a href="#deleteTorrentsByCategory"><img src="images/qbt-theme/edit-delete.svg" alt="QBT_TR(Delete torrents)QBT_TR[CONTEXT=CategoryFilterWidget]"/> QBT_TR(Delete torrents)QBT_TR[CONTEXT=CategoryFilterWidget]</a></li>
|
||||
</ul>
|
||||
<ul id="tagsFilterMenu" class="contextMenu">
|
||||
<li><a href="#createTag"><img src="images/qbt-theme/list-add.svg" alt="QBT_TR(Add tag...)QBT_TR[CONTEXT=TagFilterWidget]"/> QBT_TR(Add tag...)QBT_TR[CONTEXT=TagFilterWidget]</a></li>
|
||||
<li><a href="#deleteTag"><img src="images/qbt-theme/list-remove.svg" alt="QBT_TR(Remove tag)QBT_TR[CONTEXT=TagFilterWidget]"/> QBT_TR(Remove tag)QBT_TR[CONTEXT=TagFilterWidget]</a></li>
|
||||
<li><a href="#deleteUnusedTags"><img src="images/qbt-theme/list-remove.svg" alt="QBT_TR(Remove unused tags)QBT_TR[CONTEXT=TagFilterWidget]"/> QBT_TR(Remove unused tags)QBT_TR[CONTEXT=TagFilterWidget]</a></li>
|
||||
<li class="separator"><a href="#startTorrentsByTag"><img src="images/qbt-theme/media-playback-start.svg" alt="QBT_TR(Resume torrents)QBT_TR[CONTEXT=TagFilterWidget]"/> QBT_TR(Resume torrents)QBT_TR[CONTEXT=TagFilterWidget]</a></li>
|
||||
<li><a href="#pauseTorrentsByTag"><img src="images/qbt-theme/media-playback-pause.svg" alt="QBT_TR(Pause torrents)QBT_TR[CONTEXT=TagFilterWidget]"/> QBT_TR(Pause torrents)QBT_TR[CONTEXT=TagFilterWidget]</a></li>
|
||||
<li><a href="#deleteTorrentsByTag"><img src="images/qbt-theme/edit-delete.svg" alt="QBT_TR(Delete torrents)QBT_TR[CONTEXT=TagFilterWidget]"/> QBT_TR(Delete torrents)QBT_TR[CONTEXT=TagFilterWidget]</a></li>
|
||||
</ul>
|
||||
<ul id="torrentTrackersMenu" class="contextMenu">
|
||||
<li><a href="#AddTracker"><img src="images/qbt-theme/list-add.svg" alt="QBT_TR(Add a new tracker...)QBT_TR[CONTEXT=TrackerListWidget]"/> QBT_TR(Add a new tracker...)QBT_TR[CONTEXT=TrackerListWidget]</a></li>
|
||||
<li class="separator"><a href="#EditTracker"><img src="images/qbt-theme/document-edit.svg" alt="QBT_TR(Edit tracker URL...)QBT_TR[CONTEXT=TrackerListWidget]"/> QBT_TR(Edit tracker URL...)QBT_TR[CONTEXT=TrackerListWidget]</a></li>
|
||||
|
105
src/webui/www/private/newtag.html
Normal file
105
src/webui/www/private/newtag.html
Normal file
@ -0,0 +1,105 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="${LANG}">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>QBT_TR(Add Tags)QBT_TR[CONTEXT=TransferListWidget]</title>
|
||||
<link rel="stylesheet" href="css/style.css?v=${CACHEID}" type="text/css" />
|
||||
<script src="scripts/lib/mootools-1.2-core-yc.js"></script>
|
||||
<script src="scripts/lib/mootools-1.2-more.js"></script>
|
||||
<script src="scripts/misc.js?locale=${LANG}&v=${CACHEID}"></script>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
new Keyboard({
|
||||
defaultEventType: 'keydown',
|
||||
events: {
|
||||
'Enter': function(event) {
|
||||
$('tagNameButton').click();
|
||||
event.preventDefault();
|
||||
},
|
||||
'Escape': function(event) {
|
||||
window.parent.closeWindows();
|
||||
event.preventDefault();
|
||||
},
|
||||
'Esc': function(event) {
|
||||
window.parent.closeWindows();
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
}).activate();
|
||||
|
||||
window.addEvent('domready', function() {
|
||||
const uriAction = safeTrim(new URI().getData('action'));
|
||||
const uriHashes = safeTrim(new URI().getData('hashes'));
|
||||
|
||||
if (uriAction === 'create')
|
||||
$('legendText').innerText = 'QBT_TR(Tag:)QBT_TR[CONTEXT=TagFilterWidget]';
|
||||
|
||||
$('tagName').focus();
|
||||
|
||||
$('tagNameButton').addEvent('click', function(e) {
|
||||
new Event(e).stop();
|
||||
|
||||
const tagName = $('tagName').value.trim();
|
||||
|
||||
const verifyTagName = function(name) {
|
||||
if ((name === null) || (name === ""))
|
||||
return false;
|
||||
if (name.indexOf(",") >= 0) {
|
||||
alert("QBT_TR(Invalid tag name)QBT_TR[CONTEXT=TagFilterWidget]");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
switch (uriAction) {
|
||||
case "set":
|
||||
if (uriHashes === "")
|
||||
return;
|
||||
|
||||
new Request({
|
||||
url: 'api/v2/torrents/addTags',
|
||||
method: 'post',
|
||||
data: {
|
||||
hashes: uriHashes,
|
||||
tags: tagName,
|
||||
},
|
||||
onComplete: function() {
|
||||
window.parent.closeWindows();
|
||||
}
|
||||
}).send();
|
||||
break;
|
||||
|
||||
case "create":
|
||||
if (!verifyTagName(tagName))
|
||||
return;
|
||||
|
||||
new Request({
|
||||
url: 'api/v2/torrents/createTags',
|
||||
method: 'post',
|
||||
data: {
|
||||
tags: tagName,
|
||||
},
|
||||
onComplete: function() {
|
||||
window.parent.closeWindows();
|
||||
}
|
||||
}).send();
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="padding: 10px 10px 0px 10px;">
|
||||
<p id="legendText" style="font-weight: bold;">QBT_TR(Comma-separated tags:)QBT_TR[CONTEXT=TransferListWidget]</p>
|
||||
<input type="text" id="tagName" value="" maxlength="100" style="width: 220px;" />
|
||||
<div style="text-align: center; padding-top: 10px;">
|
||||
<input type="button" value="QBT_TR(Add)QBT_TR[CONTEXT=HttpServer]" id="tagNameButton" />
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -55,6 +55,14 @@ var category_list = {};
|
||||
var selected_category = CATEGORIES_ALL;
|
||||
var setCategoryFilter = function() {};
|
||||
|
||||
const TAGS_ALL = 1;
|
||||
const TAGS_UNTAGGED = 2;
|
||||
|
||||
let tagList = {};
|
||||
|
||||
let selectedTag = TAGS_ALL;
|
||||
let setTagFilter = function() {};
|
||||
|
||||
var selected_filter = getLocalStorageItem('selected_filter', 'all');
|
||||
var setFilter = function() {};
|
||||
var toggleFilterDisplay = function() {};
|
||||
@ -64,6 +72,11 @@ var loadSelectedCategory = function() {
|
||||
};
|
||||
loadSelectedCategory();
|
||||
|
||||
const loadSelectedTag = function() {
|
||||
selectedTag = getLocalStorageItem('selected_tag', TAGS_ALL);
|
||||
};
|
||||
loadSelectedTag();
|
||||
|
||||
function genHash(string) {
|
||||
var hash = 0;
|
||||
for (var i = 0; i < string.length; ++i) {
|
||||
@ -161,6 +174,14 @@ window.addEvent('load', function() {
|
||||
updateMainData();
|
||||
};
|
||||
|
||||
setTagFilter = function(hash) {
|
||||
selectedTag = hash.toString();
|
||||
localStorage.setItem('selected_tag', selectedTag);
|
||||
highlightSelectedTag();
|
||||
if (torrentsTable.tableBody !== undefined)
|
||||
updateMainData();
|
||||
};
|
||||
|
||||
setFilter = function(f) {
|
||||
// Visually Select the right filter
|
||||
$("all_filter").removeClass("selectedFilter");
|
||||
@ -282,8 +303,44 @@ window.addEvent('load', function() {
|
||||
return false;
|
||||
};
|
||||
|
||||
const removeTorrentFromTagList = function(hash) {
|
||||
if ((hash === null) || (hash === ""))
|
||||
return false;
|
||||
|
||||
let removed = false;
|
||||
for (const key in tagList) {
|
||||
const tag = tagList[key];
|
||||
if (Object.contains(tag.torrents, hash)) {
|
||||
removed = true;
|
||||
tag.torrents.splice(tag.torrents.indexOf(hash), 1);
|
||||
}
|
||||
}
|
||||
return removed;
|
||||
};
|
||||
|
||||
const addTorrentToTagList = function(torrent) {
|
||||
if (torrent['tags'] === undefined) // Tags haven't changed
|
||||
return false;
|
||||
|
||||
removeTorrentFromTagList(torrent['hash']);
|
||||
|
||||
if (torrent['tags'].length === 0) // No tags
|
||||
return true;
|
||||
|
||||
const tags = torrent['tags'].split(',');
|
||||
let added = false;
|
||||
for (let i = 0; i < tags.length; ++i) {
|
||||
const tagHash = genHash(tags[i].trim());
|
||||
if (!Object.contains(tagList[tagHash].torrents, torrent['hash'])) {
|
||||
added = true;
|
||||
tagList[tagHash].torrents.push(torrent['hash']);
|
||||
}
|
||||
}
|
||||
return added;
|
||||
};
|
||||
|
||||
var updateFilter = function(filter, filterTitle) {
|
||||
$(filter + '_filter').firstChild.childNodes[1].nodeValue = filterTitle.replace('%1', torrentsTable.getFilteredTorrentsNumber(filter, CATEGORIES_ALL));
|
||||
$(filter + '_filter').firstChild.childNodes[1].nodeValue = filterTitle.replace('%1', torrentsTable.getFilteredTorrentsNumber(filter, CATEGORIES_ALL, TAGS_ALL));
|
||||
};
|
||||
|
||||
var updateFiltersList = function() {
|
||||
@ -353,6 +410,60 @@ window.addEvent('load', function() {
|
||||
}
|
||||
};
|
||||
|
||||
const updateTagList = function() {
|
||||
const tagFilterList = $('tagFilterList');
|
||||
if (tagFilterList === null)
|
||||
return;
|
||||
|
||||
while (tagFilterList.firstChild !== null)
|
||||
tagFilterList.removeChild(tagFilterList.firstChild);
|
||||
|
||||
const createLink = function(hash, text, count) {
|
||||
const html = '<a href="#" onclick="setTagFilter(' + hash + ');return false;">'
|
||||
+ '<img src="images/qbt-theme/inode-directory.svg"/>'
|
||||
+ escapeHtml(text) + ' (' + count + ')' + '</a>';
|
||||
const el = new Element('li', {
|
||||
id: hash,
|
||||
html: html
|
||||
});
|
||||
tagsFilterContextMenu.addTarget(el);
|
||||
return el;
|
||||
};
|
||||
|
||||
const torrentsCount = torrentsTable.getRowIds().length;
|
||||
let untagged = 0;
|
||||
for (const key in torrentsTable.rows) {
|
||||
if (torrentsTable.rows.hasOwnProperty(key) && torrentsTable.rows[key]['full_data'].tags.length === 0)
|
||||
untagged += 1;
|
||||
}
|
||||
tagFilterList.appendChild(createLink(TAGS_ALL, 'QBT_TR(All)QBT_TR[CONTEXT=TagFilterModel]', torrentsCount));
|
||||
tagFilterList.appendChild(createLink(TAGS_UNTAGGED, 'QBT_TR(Untagged)QBT_TR[CONTEXT=TagFilterModel]', untagged));
|
||||
|
||||
const sortedTags = [];
|
||||
for (const key in tagList)
|
||||
sortedTags.push(tagList[key].name);
|
||||
sortedTags.sort();
|
||||
|
||||
for (let i = 0; i < sortedTags.length; ++i) {
|
||||
const tagName = sortedTags[i];
|
||||
const tagHash = genHash(tagName);
|
||||
const tagCount = tagList[tagHash].torrents.length;
|
||||
tagFilterList.appendChild(createLink(tagHash, tagName, tagCount));
|
||||
}
|
||||
|
||||
highlightSelectedTag();
|
||||
};
|
||||
|
||||
const highlightSelectedTag = function() {
|
||||
const tagFilterList = $('tagFilterList');
|
||||
if (!tagFilterList)
|
||||
return;
|
||||
|
||||
const children = tagFilterList.childNodes;
|
||||
for (let i = 0; i < children.length; ++i)
|
||||
children[i].className = (children[i].id === selectedTag) ? "selectedFilter" : "";
|
||||
};
|
||||
|
||||
var syncMainDataTimer;
|
||||
var syncMainData = function() {
|
||||
var url = new URI('api/v2/sync/maindata');
|
||||
@ -374,11 +485,13 @@ window.addEvent('load', function() {
|
||||
clearTimeout(torrentsFilterInputTimer);
|
||||
var torrentsTableSelectedRows;
|
||||
var update_categories = false;
|
||||
let updateTags = false;
|
||||
var full_update = (response['full_update'] === true);
|
||||
if (full_update) {
|
||||
torrentsTableSelectedRows = torrentsTable.selectedRowsIds();
|
||||
torrentsTable.clear();
|
||||
category_list = {};
|
||||
tagList = {};
|
||||
}
|
||||
if (response['rid']) {
|
||||
syncMainDataLastResponseId = response['rid'];
|
||||
@ -408,6 +521,25 @@ window.addEvent('load', function() {
|
||||
});
|
||||
update_categories = true;
|
||||
}
|
||||
if (response['tags']) {
|
||||
for (const tag of response['tags']) {
|
||||
const tagHash = genHash(tag);
|
||||
if (!tagList[tagHash]) {
|
||||
tagList[tagHash] = {
|
||||
name: tag,
|
||||
torrents: []
|
||||
};
|
||||
}
|
||||
}
|
||||
updateTags = true;
|
||||
}
|
||||
if (response['tags_removed']) {
|
||||
for (let i = 0; i < response['tags_removed'].length; ++i) {
|
||||
const tagHash = genHash(response['tags_removed'][i]);
|
||||
delete tagList[tagHash];
|
||||
}
|
||||
updateTags = true;
|
||||
}
|
||||
if (response['torrents']) {
|
||||
var updateTorrentList = false;
|
||||
for (var key in response['torrents']) {
|
||||
@ -418,6 +550,8 @@ window.addEvent('load', function() {
|
||||
torrentsTable.updateRowData(response['torrents'][key]);
|
||||
if (addTorrentToCategoryList(response['torrents'][key]))
|
||||
update_categories = true;
|
||||
if (addTorrentToTagList(response['torrents'][key]))
|
||||
updateTags = true;
|
||||
if (response['torrents'][key]['name'])
|
||||
updateTorrentList = true;
|
||||
}
|
||||
@ -430,6 +564,8 @@ window.addEvent('load', function() {
|
||||
torrentsTable.removeRow(hash);
|
||||
removeTorrentFromCategoryList(hash);
|
||||
update_categories = true; // Always to update All category
|
||||
removeTorrentFromTagList(hash);
|
||||
updateTags = true; // Always to update All tag
|
||||
});
|
||||
torrentsTable.updateTable(full_update);
|
||||
torrentsTable.altRow();
|
||||
@ -444,6 +580,10 @@ window.addEvent('load', function() {
|
||||
updateCategoryList();
|
||||
torrentsTableContextMenu.updateCategoriesSubMenu(category_list);
|
||||
}
|
||||
if (updateTags) {
|
||||
updateTagList();
|
||||
torrentsTableContextMenu.updateTagsSubMenu(tagList);
|
||||
}
|
||||
|
||||
if (full_update)
|
||||
// re-select previously selected rows
|
||||
|
@ -293,6 +293,7 @@ var TorrentsTableContextMenu = new Class({
|
||||
var all_are_super_seeding = true;
|
||||
var all_are_auto_tmm = true;
|
||||
var there_are_auto_tmm = false;
|
||||
const tagsSelectionState = Object.clone(tagList);
|
||||
|
||||
var h = torrentsTable.selectedRowsIds();
|
||||
h.each(function(item, index) {
|
||||
@ -327,6 +328,18 @@ var TorrentsTableContextMenu = new Class({
|
||||
there_are_auto_tmm = true;
|
||||
else
|
||||
all_are_auto_tmm = false;
|
||||
|
||||
const torrentTags = data['tags'].split(', ');
|
||||
for (const key in tagsSelectionState) {
|
||||
const tag = tagsSelectionState[key];
|
||||
const tagExists = torrentTags.contains(tag.name);
|
||||
if ((tag.checked !== undefined) && (tag.checked != tagExists))
|
||||
tag.indeterminate = true;
|
||||
if (tag.checked === undefined)
|
||||
tag.checked = tagExists;
|
||||
else
|
||||
tag.checked = tag.checked && tagExists;
|
||||
}
|
||||
});
|
||||
|
||||
var show_seq_dl = true;
|
||||
@ -389,6 +402,13 @@ var TorrentsTableContextMenu = new Class({
|
||||
this.setItemChecked('autoTorrentManagement', all_are_auto_tmm);
|
||||
}
|
||||
|
||||
const contextTagList = $('contextTagList');
|
||||
for (const tagHash in tagList) {
|
||||
const checkbox = contextTagList.getElement('a[href=#Tag/' + tagHash + '] input[type=checkbox]');
|
||||
const checkboxState = tagsSelectionState[tagHash];
|
||||
checkbox.indeterminate = checkboxState.indeterminate;
|
||||
checkbox.checked = checkboxState.checked;
|
||||
}
|
||||
},
|
||||
|
||||
updateCategoriesSubMenu: function(category_list) {
|
||||
@ -419,6 +439,43 @@ var TorrentsTableContextMenu = new Class({
|
||||
}
|
||||
categoryList.appendChild(el);
|
||||
});
|
||||
},
|
||||
|
||||
updateTagsSubMenu: function(tagList) {
|
||||
const contextTagList = $('contextTagList');
|
||||
while (contextTagList.firstChild !== null)
|
||||
contextTagList.removeChild(contextTagList.firstChild);
|
||||
|
||||
contextTagList.appendChild(new Element('li', {
|
||||
html: '<a href="javascript:torrentAddTagsFN();">'
|
||||
+ '<img src="images/qbt-theme/list-add.svg" alt="QBT_TR(Add...)QBT_TR[CONTEXT=TransferListWidget]"/>'
|
||||
+ ' QBT_TR(Add...)QBT_TR[CONTEXT=TransferListWidget]'
|
||||
+ '</a>'
|
||||
}));
|
||||
contextTagList.appendChild(new Element('li', {
|
||||
html: '<a href="javascript:torrentRemoveAllTagsFN();">'
|
||||
+ '<img src="images/qbt-theme/edit-clear.svg" alt="QBT_TR(Remove All)QBT_TR[CONTEXT=TransferListWidget]"/>'
|
||||
+ ' QBT_TR(Remove All)QBT_TR[CONTEXT=TransferListWidget]'
|
||||
+ '</a>'
|
||||
}));
|
||||
|
||||
const sortedTags = [];
|
||||
for (const key in tagList)
|
||||
sortedTags.push(tagList[key].name);
|
||||
sortedTags.sort();
|
||||
|
||||
for (let i = 0; i < sortedTags.length; ++i) {
|
||||
const tagName = sortedTags[i];
|
||||
const tagHash = genHash(tagName);
|
||||
const el = new Element('li', {
|
||||
html: '<a href="#Tag/' + tagHash + '" onclick="event.preventDefault(); torrentSetTagsFN(\'' + tagHash + '\', !event.currentTarget.getElement(\'input[type=checkbox]\').checked);">'
|
||||
+ '<input type="checkbox" onclick="this.checked = !this.checked;"> ' + escapeHtml(tagName)
|
||||
+ '</a>'
|
||||
});
|
||||
if (i === 0)
|
||||
el.addClass('separator');
|
||||
contextTagList.appendChild(el);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -437,6 +494,17 @@ var CategoriesFilterContextMenu = new Class({
|
||||
}
|
||||
});
|
||||
|
||||
const TagsFilterContextMenu = new Class({
|
||||
Extends: ContextMenu,
|
||||
updateMenuItems: function() {
|
||||
const id = this.options.element.id;
|
||||
if ((id !== TAGS_ALL.toString()) && (id !== TAGS_UNTAGGED.toString()))
|
||||
this.showItem('deleteTag');
|
||||
else
|
||||
this.hideItem('deleteTag');
|
||||
}
|
||||
});
|
||||
|
||||
var SearchPluginsTableContextMenu = new Class({
|
||||
Extends: ContextMenu,
|
||||
|
||||
|
@ -1183,7 +1183,7 @@ var TorrentsTable = new Class({
|
||||
};
|
||||
},
|
||||
|
||||
applyFilter: function(row, filterName, categoryHash, filterTerms) {
|
||||
applyFilter: function(row, filterName, categoryHash, tagHash, filterTerms) {
|
||||
var state = row['full_data'].state;
|
||||
var name = row['full_data'].name.toLowerCase();
|
||||
var inactive = false;
|
||||
@ -1242,6 +1242,28 @@ var TorrentsTable = new Class({
|
||||
}
|
||||
}
|
||||
|
||||
const tagHashInt = parseInt(tagHash);
|
||||
const isNumber = !isNaN(tagHashInt);
|
||||
if (isNumber) {
|
||||
switch (tagHashInt) {
|
||||
case TAGS_ALL:
|
||||
break; // do nothing
|
||||
|
||||
case TAGS_UNTAGGED:
|
||||
if (row['full_data'].tags.length !== 0)
|
||||
return false;
|
||||
break; // do nothing
|
||||
|
||||
default:
|
||||
let rowTags = row['full_data'].tags.split(', ');
|
||||
rowTags = rowTags.map(function(tag) {
|
||||
return genHash(tag);
|
||||
});
|
||||
if (!rowTags.contains(tagHashInt))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (filterTerms) {
|
||||
for (var i = 0; i < filterTerms.length; ++i) {
|
||||
if (name.indexOf(filterTerms[i]) === -1)
|
||||
@ -1252,21 +1274,21 @@ var TorrentsTable = new Class({
|
||||
return true;
|
||||
},
|
||||
|
||||
getFilteredTorrentsNumber: function(filterName, categoryHash) {
|
||||
getFilteredTorrentsNumber: function(filterName, categoryHash, tagHash) {
|
||||
var cnt = 0;
|
||||
var rows = this.rows.getValues();
|
||||
|
||||
for (var i = 0; i < rows.length; ++i)
|
||||
if (this.applyFilter(rows[i], filterName, categoryHash, null)) ++cnt;
|
||||
if (this.applyFilter(rows[i], filterName, categoryHash, tagHash, null)) ++cnt;
|
||||
return cnt;
|
||||
},
|
||||
|
||||
getFilteredTorrentsHashes: function(filterName, categoryHash) {
|
||||
getFilteredTorrentsHashes: function(filterName, categoryHash, tagHash) {
|
||||
var rowsHashes = [];
|
||||
var rows = this.rows.getValues();
|
||||
|
||||
for (var i = 0; i < rows.length; ++i)
|
||||
if (this.applyFilter(rows[i], filterName, categoryHash, null))
|
||||
if (this.applyFilter(rows[i], filterName, categoryHash, tagHash, null))
|
||||
rowsHashes.push(rows[i]['rowId']);
|
||||
|
||||
return rowsHashes;
|
||||
@ -1280,7 +1302,7 @@ var TorrentsTable = new Class({
|
||||
var filterTerms = (filterText.length > 0) ? filterText.split(" ") : null;
|
||||
|
||||
for (var i = 0; i < rows.length; ++i) {
|
||||
if (this.applyFilter(rows[i], selected_filter, selected_category, filterTerms)) {
|
||||
if (this.applyFilter(rows[i], selected_filter, selected_category, selectedTag, filterTerms)) {
|
||||
filteredRows.push(rows[i]);
|
||||
filteredRows[rows[i].rowId] = rows[i];
|
||||
}
|
||||
|
@ -90,6 +90,15 @@ var deleteUnusedCategoriesFN = function() {};
|
||||
var startTorrentsByCategoryFN = function() {};
|
||||
var pauseTorrentsByCategoryFN = function() {};
|
||||
var deleteTorrentsByCategoryFN = function() {};
|
||||
let torrentAddTagsFN = function() {};
|
||||
let torrentSetTagsFN = function() {};
|
||||
let torrentRemoveAllTagsFN = function() {};
|
||||
let createTagFN = function() {};
|
||||
let removeTagFN = function() {};
|
||||
let deleteUnusedTagsFN = function() {};
|
||||
let startTorrentsByTagFN = function() {};
|
||||
let pauseTorrentsByTagFN = function() {};
|
||||
let deleteTorrentsByTagFN = function() {};
|
||||
var copyNameFN = function() {};
|
||||
var copyMagnetLinkFN = function() {};
|
||||
var copyHashFN = function() {};
|
||||
@ -616,7 +625,7 @@ var initializeWindows = function() {
|
||||
deleteUnusedCategoriesFN = function() {
|
||||
var categories = [];
|
||||
for (var hash in category_list) {
|
||||
if (torrentsTable.getFilteredTorrentsNumber('all', hash) === 0)
|
||||
if (torrentsTable.getFilteredTorrentsNumber('all', hash, TAGS_ALL) === 0)
|
||||
categories.push(category_list[hash].name);
|
||||
}
|
||||
new Request({
|
||||
@ -630,7 +639,7 @@ var initializeWindows = function() {
|
||||
};
|
||||
|
||||
startTorrentsByCategoryFN = function(categoryHash) {
|
||||
var hashes = torrentsTable.getFilteredTorrentsHashes('all', categoryHash);
|
||||
var hashes = torrentsTable.getFilteredTorrentsHashes('all', categoryHash, TAGS_ALL);
|
||||
if (hashes.length) {
|
||||
new Request({
|
||||
url: 'api/v2/torrents/resume',
|
||||
@ -644,7 +653,7 @@ var initializeWindows = function() {
|
||||
};
|
||||
|
||||
pauseTorrentsByCategoryFN = function(categoryHash) {
|
||||
var hashes = torrentsTable.getFilteredTorrentsHashes('all', categoryHash);
|
||||
var hashes = torrentsTable.getFilteredTorrentsHashes('all', categoryHash, TAGS_ALL);
|
||||
if (hashes.length) {
|
||||
new Request({
|
||||
url: 'api/v2/torrents/pause',
|
||||
@ -658,7 +667,148 @@ var initializeWindows = function() {
|
||||
};
|
||||
|
||||
deleteTorrentsByCategoryFN = function(categoryHash) {
|
||||
var hashes = torrentsTable.getFilteredTorrentsHashes('all', categoryHash);
|
||||
var hashes = torrentsTable.getFilteredTorrentsHashes('all', categoryHash, TAGS_ALL);
|
||||
if (hashes.length) {
|
||||
new MochaUI.Window({
|
||||
id: 'confirmDeletionPage',
|
||||
title: "QBT_TR(Deletion confirmation)QBT_TR[CONTEXT=confirmDeletionDlg]",
|
||||
loadMethod: 'iframe',
|
||||
contentURL: 'confirmdeletion.html?hashes=' + hashes.join("|"),
|
||||
scrollbars: false,
|
||||
resizable: false,
|
||||
maximizable: false,
|
||||
padding: 10,
|
||||
width: 424,
|
||||
height: 140
|
||||
});
|
||||
updateMainData();
|
||||
}
|
||||
};
|
||||
|
||||
torrentAddTagsFN = function() {
|
||||
const action = "set";
|
||||
const hashes = torrentsTable.selectedRowsIds();
|
||||
if (hashes.length) {
|
||||
new MochaUI.Window({
|
||||
id: 'newTagPage',
|
||||
title: "QBT_TR(Add Tags)QBT_TR[CONTEXT=TransferListWidget]",
|
||||
loadMethod: 'iframe',
|
||||
contentURL: 'newtag.html?action=' + action + '&hashes=' + hashes.join('|'),
|
||||
scrollbars: false,
|
||||
resizable: false,
|
||||
maximizable: false,
|
||||
paddingVertical: 0,
|
||||
paddingHorizontal: 0,
|
||||
width: 250,
|
||||
height: 100
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
torrentSetTagsFN = function(tagHash, isSet) {
|
||||
const tagName = ((tagHash === '0') ? '' : tagList[tagHash].name);
|
||||
const hashes = torrentsTable.selectedRowsIds();
|
||||
if (hashes.length) {
|
||||
new Request({
|
||||
url: (isSet ? 'api/v2/torrents/addTags' : 'api/v2/torrents/removeTags'),
|
||||
method: 'post',
|
||||
data: {
|
||||
hashes: hashes.join("|"),
|
||||
tags: tagName,
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
};
|
||||
|
||||
torrentRemoveAllTagsFN = function() {
|
||||
const hashes = torrentsTable.selectedRowsIds();
|
||||
if (hashes.length) {
|
||||
new Request({
|
||||
url: ('api/v2/torrents/removeTags'),
|
||||
method: 'post',
|
||||
data: {
|
||||
hashes: hashes.join("|"),
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
};
|
||||
|
||||
createTagFN = function() {
|
||||
const action = "create";
|
||||
new MochaUI.Window({
|
||||
id: 'newTagPage',
|
||||
title: "QBT_TR(New Tag)QBT_TR[CONTEXT=TagFilterWidget]",
|
||||
loadMethod: 'iframe',
|
||||
contentURL: 'newtag.html?action=' + action,
|
||||
scrollbars: false,
|
||||
resizable: false,
|
||||
maximizable: false,
|
||||
paddingVertical: 0,
|
||||
paddingHorizontal: 0,
|
||||
width: 250,
|
||||
height: 100
|
||||
});
|
||||
updateMainData();
|
||||
};
|
||||
|
||||
removeTagFN = function(tagHash) {
|
||||
const tagName = tagList[tagHash].name;
|
||||
new Request({
|
||||
url: 'api/v2/torrents/deleteTags',
|
||||
method: 'post',
|
||||
data: {
|
||||
tags: tagName
|
||||
}
|
||||
}).send();
|
||||
setTagFilter(TAGS_ALL);
|
||||
};
|
||||
|
||||
deleteUnusedTagsFN = function() {
|
||||
const tags = [];
|
||||
for (const hash in tagList) {
|
||||
if (torrentsTable.getFilteredTorrentsNumber('all', CATEGORIES_ALL, hash) === 0)
|
||||
tags.push(tagList[hash].name);
|
||||
}
|
||||
new Request({
|
||||
url: 'api/v2/torrents/deleteTags',
|
||||
method: 'post',
|
||||
data: {
|
||||
tags: tags.join(',')
|
||||
}
|
||||
}).send();
|
||||
setTagFilter(TAGS_ALL);
|
||||
};
|
||||
|
||||
startTorrentsByTagFN = function(tagHash) {
|
||||
const hashes = torrentsTable.getFilteredTorrentsHashes('all', CATEGORIES_ALL, tagHash);
|
||||
if (hashes.length) {
|
||||
new Request({
|
||||
url: 'api/v2/torrents/resume',
|
||||
method: 'post',
|
||||
data: {
|
||||
hashes: hashes.join("|")
|
||||
}
|
||||
}).send();
|
||||
updateMainData();
|
||||
}
|
||||
};
|
||||
|
||||
pauseTorrentsByTagFN = function(tagHash) {
|
||||
const hashes = torrentsTable.getFilteredTorrentsHashes('all', CATEGORIES_ALL, tagHash);
|
||||
if (hashes.length) {
|
||||
new Request({
|
||||
url: 'api/v2/torrents/pause',
|
||||
method: 'post',
|
||||
data: {
|
||||
hashes: hashes.join("|")
|
||||
}
|
||||
}).send();
|
||||
updateMainData();
|
||||
}
|
||||
};
|
||||
|
||||
deleteTorrentsByTagFN = function(tagHash) {
|
||||
const hashes = torrentsTable.getFilteredTorrentsHashes('all', CATEGORIES_ALL, tagHash);
|
||||
if (hashes.length) {
|
||||
new MochaUI.Window({
|
||||
id: 'confirmDeletionPage',
|
||||
|
@ -18,6 +18,7 @@
|
||||
<file>private/index.html</file>
|
||||
<file>private/installsearchplugin.html</file>
|
||||
<file>private/newcategory.html</file>
|
||||
<file>private/newtag.html</file>
|
||||
<file>private/preferences.html</file>
|
||||
<file>private/preferences_content.html</file>
|
||||
<file>private/properties.html</file>
|
||||
|
Loading…
Reference in New Issue
Block a user