Implement Advanced Saving Management subsystem

Closes #4696
This commit is contained in:
Vladimir Golovnev (Glassez)
2016-02-09 11:56:48 +03:00
parent d05d5a85a5
commit dd34663224
59 changed files with 1796 additions and 1280 deletions

View File

@@ -243,7 +243,7 @@ void AbstractWebApplication::translateDocument(QString& data)
"options_imp", "Preferences", "TrackersAdditionDlg", "ScanFoldersModel",
"PropTabBar", "TorrentModel", "downloadFromURL", "MainWindow", "misc",
"StatusBar", "AboutDlg", "about", "PeerListWidget", "StatusFiltersWidget",
"LabelFiltersList"
"CategoryFiltersList"
};
const size_t context_count = sizeof(contexts) / sizeof(contexts[0]);
int i = 0;

View File

@@ -102,7 +102,7 @@ static const char KEY_TORRENT_ETA[] = "eta";
static const char KEY_TORRENT_STATE[] = "state";
static const char KEY_TORRENT_SEQUENTIAL_DOWNLOAD[] = "seq_dl";
static const char KEY_TORRENT_FIRST_LAST_PIECE_PRIO[] = "f_l_piece_prio";
static const char KEY_TORRENT_LABEL[] = "label";
static const char KEY_TORRENT_CATEGORY[] = "category";
static const char KEY_TORRENT_SUPER_SEEDING[] = "super_seeding";
static const char KEY_TORRENT_FORCE_START[] = "force_start";
static const char KEY_TORRENT_SAVE_PATH[] = "save_path";
@@ -279,13 +279,13 @@ private:
* - "seq_dl": Torrent sequential download state
* - "f_l_piece_prio": Torrent first last piece priority state
* - "force_start": Torrent force start state
* - "label": Torrent label
* - "category": Torrent category
*/
QByteArray btjson::getTorrents(QString filter, QString label,
QByteArray btjson::getTorrents(QString filter, QString category,
QString sortedColumn, bool reverse, int limit, int offset)
{
QVariantList torrentList;
TorrentFilter torrentFilter(filter, TorrentFilter::AnyHash, label);
TorrentFilter torrentFilter(filter, TorrentFilter::AnyHash, category);
foreach (BitTorrent::TorrentHandle *const torrent, BitTorrent::Session::instance()->torrents()) {
if (torrentFilter.match(torrent))
torrentList.append(toMap(torrent));
@@ -317,8 +317,8 @@ QByteArray btjson::getTorrents(QString filter, QString label,
* - "full_update": full data update flag
* - "torrents": dictionary contains information about torrents.
* - "torrents_removed": a list of hashes of removed torrents
* - "labels": list of labels
* - "labels_removed": list of removed labels
* - "categories": list of categories
* - "categories_removed": list of removed categories
* - "server_state": map contains information about the state of the server
* The keys of the 'torrents' dictionary are hashes of torrents.
* Each value of the 'torrents' dictionary contains map. The map can contain following keys:
@@ -362,11 +362,11 @@ QByteArray btjson::getSyncMainData(int acceptedResponseId, QVariantMap &lastData
data["torrents"] = torrents;
QVariantList labels;
foreach (QString s, Preferences::instance()->getTorrentLabels())
labels << s;
QVariantList categories;
foreach (const QString &category, BitTorrent::Session::instance()->categories())
categories << category;
data["labels"] = labels;
data["categories"] = categories;
QVariantMap serverState = getTranserInfoMap();
serverState[KEY_SYNC_MAINDATA_QUEUEING] = BitTorrent::Session::instance()->isQueueingEnabled();
@@ -705,7 +705,7 @@ QVariantMap toMap(BitTorrent::TorrentHandle *const torrent)
ret[KEY_TORRENT_SEQUENTIAL_DOWNLOAD] = torrent->isSequentialDownload();
if (torrent->hasMetadata())
ret[KEY_TORRENT_FIRST_LAST_PIECE_PRIO] = torrent->hasFirstLastPiecePriority();
ret[KEY_TORRENT_LABEL] = torrent->label();
ret[KEY_TORRENT_CATEGORY] = torrent->category();
ret[KEY_TORRENT_SUPER_SEEDING] = torrent->superSeeding();
ret[KEY_TORRENT_FORCE_START] = torrent->isForced();
ret[KEY_TORRENT_SAVE_PATH] = Utils::Fs::toNativePath(torrent->savePath());
@@ -760,6 +760,7 @@ void processMap(QVariantMap prevData, QVariantMap data, QVariantMap &syncData)
case QMetaType::Double:
case QMetaType::ULongLong:
case QMetaType::UInt:
case QMetaType::QDateTime:
if (prevData[key] != data[key])
syncData[key] = data[key];
break;

View File

@@ -43,7 +43,7 @@ private:
btjson() {}
public:
static QByteArray getTorrents(QString filter = "all", QString label = QString(),
static QByteArray getTorrents(QString filter = "all", QString category = QString(),
QString sortedColumn = "name", bool reverse = false, int limit = 0, int offset = 0);
static QByteArray getSyncMainData(int acceptedResponseId, QVariantMap &lastData, QVariantMap &lastAcceptedData);
static QByteArray getSyncTorrentPeersData(int acceptedResponseId, QString hash, QVariantMap &lastData, QVariantMap &lastAcceptedData);

View File

@@ -84,7 +84,7 @@ static const char *__TRANSLATIONS__[] = {
QT_TRANSLATE_NOOP("HttpServer", "Active"),
QT_TRANSLATE_NOOP("HttpServer", "Inactive"),
QT_TRANSLATE_NOOP("HttpServer", "Save files to location:"),
QT_TRANSLATE_NOOP("HttpServer", "Label:"),
QT_TRANSLATE_NOOP("HttpServer", "Category:"),
QT_TRANSLATE_NOOP("HttpServer", "Cookie:"),
QT_TRANSLATE_NOOP("HttpServer", "Type folder here"),
QT_TRANSLATE_NOOP("HttpServer", "Run an external program on torrent completion"),

View File

@@ -28,11 +28,6 @@
* Contact : chris@qbittorrent.org
*/
#include "prefjson.h"
#include "base/preferences.h"
#include "base/scanfoldersmodel.h"
#include "base/utils/fs.h"
#ifndef QT_NO_OPENSSL
#include <QSslCertificate>
#include <QSslKey>
@@ -40,7 +35,13 @@
#include <QStringList>
#include <QTranslator>
#include <QCoreApplication>
#include "base/preferences.h"
#include "base/scanfoldersmodel.h"
#include "base/utils/fs.h"
#include "base/bittorrent/session.h"
#include "jsonutils.h"
#include "prefjson.h"
prefjson::prefjson()
{
@@ -49,13 +50,14 @@ prefjson::prefjson()
QByteArray prefjson::getPreferences()
{
const Preferences* const pref = Preferences::instance();
auto session = BitTorrent::Session::instance();
QVariantMap data;
// Downloads
// Hard Disk
data["save_path"] = Utils::Fs::toNativePath(pref->getSavePath());
data["temp_path_enabled"] = pref->isTempPathEnabled();
data["temp_path"] = Utils::Fs::toNativePath(pref->getTempPath());
data["save_path"] = Utils::Fs::toNativePath(session->defaultSavePath());
data["temp_path_enabled"] = session->isTempPathEnabled();
data["temp_path"] = Utils::Fs::toNativePath(session->tempPath());
data["preallocate_all"] = pref->preAllocateAllFiles();
data["incomplete_files_ext"] = pref->useIncompleteFilesExtension();
QVariantHash dirs = pref->getScanDirs();
@@ -140,7 +142,7 @@ QByteArray prefjson::getPreferences()
// Share Ratio Limiting
data["max_ratio_enabled"] = (pref->getGlobalMaxRatio() >= 0.);
data["max_ratio"] = pref->getGlobalMaxRatio();
data["max_ratio_act"] = static_cast<int>(pref->getMaxRatioAction());
data["max_ratio_act"] = BitTorrent::Session::instance()->maxRatioAction();
// Web UI
// Language
@@ -168,16 +170,17 @@ QByteArray prefjson::getPreferences()
void prefjson::setPreferences(const QString& json)
{
Preferences* const pref = Preferences::instance();
auto session = BitTorrent::Session::instance();
const QVariantMap m = json::fromJson(json).toMap();
// Downloads
// Hard Disk
if (m.contains("save_path"))
pref->setSavePath(m["save_path"].toString());
session->setDefaultSavePath(m["save_path"].toString());
if (m.contains("temp_path_enabled"))
pref->setTempPathEnabled(m["temp_path_enabled"].toBool());
session->setTempPathEnabled(m["temp_path_enabled"].toBool());
if (m.contains("temp_path"))
pref->setTempPath(m["temp_path"].toString());
session->setTempPath(m["temp_path"].toString());
if (m.contains("preallocate_all"))
pref->preAllocateAllFiles(m["preallocate_all"].toBool());
if (m.contains("incomplete_files_ext"))
@@ -351,7 +354,8 @@ void prefjson::setPreferences(const QString& json)
else
pref->setGlobalMaxRatio(-1);
if (m.contains("max_ratio_act"))
pref->setMaxRatioAction(static_cast<MaxRatioAction>(m["max_ratio_act"].toInt()));
BitTorrent::Session::instance()->setMaxRatioAction(
static_cast<MaxRatioAction>(m["max_ratio_act"].toInt()));
// Web UI
// Language

View File

@@ -49,8 +49,8 @@
#include "websessiondata.h"
#include "webapplication.h"
static const int API_VERSION = 8;
static const int API_VERSION_MIN = 8;
static const int API_VERSION = 9;
static const int API_VERSION_MIN = 9;
const QString WWW_FOLDER = ":/www/public/";
const QString PRIVATE_FOLDER = ":/www/private/";
@@ -114,7 +114,7 @@ QMap<QString, QMap<QString, WebApplication::Action> > WebApplication::initialize
ADD_ACTION(command, topPrio);
ADD_ACTION(command, bottomPrio);
ADD_ACTION(command, recheck);
ADD_ACTION(command, setLabel);
ADD_ACTION(command, setCategory);
ADD_ACTION(command, getSavePath);
ADD_ACTION(version, api);
ADD_ACTION(version, api_min);
@@ -216,7 +216,7 @@ void WebApplication::action_public_images()
// GET params:
// - filter (string): all, downloading, seeding, completed, paused, resumed, active, inactive
// - label (string): torrent label for filtering by it (empty string means "unlabeled"; no "label" param presented means "any label")
// - category (string): torrent category for filtering by it (empty string means "uncategorized"; no "category" param presented means "any category")
// - sort (string): name of column for sorting by its value
// - reverse (bool): enable reverse sorting
// - limit (int): set limit number of torrents returned (if greater than 0, otherwise - unlimited)
@@ -227,7 +227,7 @@ void WebApplication::action_query_torrents()
const QStringMap& gets = request().gets;
print(btjson::getTorrents(
gets["filter"], gets["label"], gets["sort"], gets["reverse"] == "true",
gets["filter"], gets["category"], gets["sort"], gets["reverse"] == "true",
gets["limit"].toInt(), gets["offset"].toInt()
), Http::CONTENT_TYPE_JSON);
}
@@ -326,7 +326,7 @@ void WebApplication::action_command_download()
QString urls = request().posts["urls"];
QStringList list = urls.split('\n');
QString savepath = request().posts["savepath"];
QString label = request().posts["label"];
QString category = request().posts["category"];
QString cookie = request().posts["cookie"];
QList<QNetworkCookie> cookies;
if (!cookie.isEmpty()) {
@@ -345,11 +345,11 @@ void WebApplication::action_command_download()
}
savepath = savepath.trimmed();
label = label.trimmed();
category = category.trimmed();
BitTorrent::AddTorrentParams params;
params.savePath = savepath;
params.label = label;
params.category = category;
foreach (QString url, list) {
url = url.trimmed();
@@ -365,10 +365,10 @@ void WebApplication::action_command_upload()
qDebug() << Q_FUNC_INFO;
CHECK_URI(0);
QString savepath = request().posts["savepath"];
QString label = request().posts["label"];
QString category = request().posts["category"];
savepath = savepath.trimmed();
label = label.trimmed();
category = category.trimmed();
foreach(const Http::UploadedFile& torrent, request().files) {
QString filePath = saveTmpFile(torrent.data);
@@ -382,7 +382,7 @@ void WebApplication::action_command_upload()
else {
BitTorrent::AddTorrentParams params;
params.savePath = savepath;
params.label = label;
params.category = category;
if (!BitTorrent::Session::instance()->addTorrent(torrentInfo, params)) {
status(500, "Internal Server Error");
print(QObject::tr("Error: Could not add torrent to session."), Http::CONTENT_TYPE_TXT);
@@ -709,29 +709,29 @@ void WebApplication::action_command_recheck()
torrent->forceRecheck();
}
void WebApplication::action_command_setLabel()
void WebApplication::action_command_setCategory()
{
CHECK_URI(0);
CHECK_PARAMETERS("hashes" << "label");
CHECK_PARAMETERS("hashes" << "category");
QStringList hashes = request().posts["hashes"].split("|");
QString label = request().posts["label"].trimmed();
if (!Utils::Fs::isValidFileSystemName(label) && !label.isEmpty()) {
status(400, "Labels must not contain special characters");
return;
}
QString category = request().posts["category"].trimmed();
foreach (const QString &hash, hashes) {
BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash);
if (torrent)
torrent->setLabel(label);
if (torrent) {
if (!torrent->setCategory(category)) {
status(400, "Incorrect category name");
return;
}
}
}
}
void WebApplication::action_command_getSavePath()
{
CHECK_URI(0);
print(Preferences::instance()->getSavePath());
print(BitTorrent::Session::instance()->defaultSavePath());
}
bool WebApplication::isPublicScope()

View File

@@ -87,7 +87,7 @@ private:
void action_command_topPrio();
void action_command_bottomPrio();
void action_command_recheck();
void action_command_setLabel();
void action_command_setCategory();
void action_command_getSavePath();
void action_version_api();
void action_version_api_min();

View File

@@ -1,43 +1,43 @@
<RCC>
<qresource prefix="/">
<file>www/private/index.html</file>
<file>www/private/login.html</file>
<file>www/public/css/Core.css</file>
<file>www/public/css/dynamicTable.css</file>
<file>www/public/css/Layout.css</file>
<file>www/public/css/style.css</file>
<file>www/public/css/Tabs.css</file>
<file>www/public/css/Window.css</file>
<file>www/public/scripts/client.js</file>
<file>www/public/scripts/contextmenu.js</file>
<file>www/public/scripts/download.js</file>
<file>www/public/scripts/dynamicTable.js</file>
<file>www/public/scripts/excanvas-compressed.js</file>
<file>www/public/scripts/misc.js</file>
<file>www/public/scripts/mocha.js</file>
<file>www/public/scripts/mocha-init.js</file>
<file>www/public/scripts/mocha-yc.js</file>
<file>www/public/scripts/mootools-1.2-core-yc.js</file>
<file>www/public/scripts/mootools-1.2-more.js</file>
<file>www/public/scripts/parametrics.js</file>
<file>www/public/scripts/progressbar.js</file>
<file>www/public/about.html</file>
<file>www/public/addtrackers.html</file>
<file>www/public/confirmdeletion.html</file>
<file>www/public/download.html</file>
<file>www/public/downloadlimit.html</file>
<file>www/public/filters.html</file>
<file>www/public/newlabel.html</file>
<file>www/public/preferences.html</file>
<file>www/public/preferences_content.html</file>
<file>www/public/properties.html</file>
<file>www/public/properties_content.html</file>
<file>www/public/scripts/prop-general.js</file>
<file>www/public/scripts/prop-trackers.js</file>
<file>www/public/scripts/prop-webseeds.js</file>
<file>www/public/scripts/prop-files.js</file>
<file>www/public/transferlist.html</file>
<file>www/public/upload.html</file>
<file>www/public/uploadlimit.html</file>
</qresource>
<qresource prefix="/">
<file>www/private/index.html</file>
<file>www/private/login.html</file>
<file>www/public/css/Core.css</file>
<file>www/public/css/dynamicTable.css</file>
<file>www/public/css/Layout.css</file>
<file>www/public/css/style.css</file>
<file>www/public/css/Tabs.css</file>
<file>www/public/css/Window.css</file>
<file>www/public/scripts/client.js</file>
<file>www/public/scripts/contextmenu.js</file>
<file>www/public/scripts/download.js</file>
<file>www/public/scripts/dynamicTable.js</file>
<file>www/public/scripts/excanvas-compressed.js</file>
<file>www/public/scripts/misc.js</file>
<file>www/public/scripts/mocha.js</file>
<file>www/public/scripts/mocha-init.js</file>
<file>www/public/scripts/mocha-yc.js</file>
<file>www/public/scripts/mootools-1.2-core-yc.js</file>
<file>www/public/scripts/mootools-1.2-more.js</file>
<file>www/public/scripts/parametrics.js</file>
<file>www/public/scripts/progressbar.js</file>
<file>www/public/about.html</file>
<file>www/public/addtrackers.html</file>
<file>www/public/confirmdeletion.html</file>
<file>www/public/download.html</file>
<file>www/public/downloadlimit.html</file>
<file>www/public/filters.html</file>
<file>www/public/preferences.html</file>
<file>www/public/preferences_content.html</file>
<file>www/public/properties.html</file>
<file>www/public/properties_content.html</file>
<file>www/public/scripts/prop-general.js</file>
<file>www/public/scripts/prop-trackers.js</file>
<file>www/public/scripts/prop-webseeds.js</file>
<file>www/public/scripts/prop-files.js</file>
<file>www/public/transferlist.html</file>
<file>www/public/upload.html</file>
<file>www/public/uploadlimit.html</file>
<file>www/public/newcategory.html</file>
</qresource>
</RCC>

View File

@@ -106,8 +106,8 @@
<li><a href="#ForceStart"><img src="theme/media-seek-forward" alt="QBT_TR(Force Resume)QBT_TR"/> QBT_TR(Force Resume)QBT_TR</a></li>
<li class="separator"><a href="#Delete"><img src="theme/list-remove" alt="QBT_TR(Delete)QBT_TR"/> QBT_TR(Delete)QBT_TR</a></li>
<li class="separator">
<a href="#Label" class="arrow-right"><img src="theme/view-categories" alt="QBT_TR(Label)QBT_TR"/> QBT_TR(Label)QBT_TR</a>
<ul id="contextLabelList"></ul>
<a href="#Category" class="arrow-right"><img src="theme/view-categories" alt="QBT_TR(Category)QBT_TR"/> QBT_TR(Category)QBT_TR</a>
<ul id="contextCategoryList"></ul>
</li>
<li id="queueingMenuItems" class="separator">
<a href="#priority" class="arrow-right"><span style="display: inline-block; width:16px"></span> QBT_TR(Priority)QBT_TR</a>

View File

@@ -1,33 +1,33 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" dir="ltr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>QBT_TR(Add Torrent Link)QBT_TR</title>
<link rel="stylesheet" href="css/style.css" type="text/css" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>QBT_TR(Add Torrent Link)QBT_TR</title>
<link rel="stylesheet" href="css/style.css" type="text/css" />
<link rel="stylesheet" href="css/Window.css" type="text/css" />
<script type="text/javascript" src="scripts/mootools-1.2-core-yc.js" charset="utf-8"></script>
<script type="text/javascript" src="scripts/download.js" charset="utf-8"></script>
<script type="text/javascript" src="scripts/mootools-1.2-core-yc.js" charset="utf-8"></script>
<script type="text/javascript" src="scripts/download.js" charset="utf-8"></script>
</head>
<body>
<iframe id="download_frame" name="download_frame" class="invisible" src="javascript:false;"></iframe>
<form action="command/download" enctype="multipart/form-data" method="post" id="downloadForm" style="text-align: center;" target="download_frame">
<center>
<br/>
<h2 class="vcenter">QBT_TR(Download Torrents from their URLs or Magnet links)QBT_TR</h2>
<textarea id="urls" rows="10" name="urls"></textarea>
<br/>
<h2 class="vcenter">QBT_TR(Download Torrents from their URLs or Magnet links)QBT_TR</h2>
<textarea id="urls" rows="10" name="urls"></textarea>
<p>QBT_TR(Only one link per line)QBT_TR</p>
<fieldset class="settings" style="border: 0; text-align: left;">
<div class="formRow" style="margin-top: 6px;">
<label for="savepath" class="leftLabelLarge">QBT_TR(Save files to location:)QBT_TR</label>
<input type="text" id="savepath" name="savepath" style="width: 16em;"/>
<label for="savepath" class="leftLabelLarge">QBT_TR(Save files to location:)QBT_TR</label>
<input type="text" id="savepath" name="savepath" style="width: 16em;"/>
</div>
<div class="formRow">
<label for="cookie" class="leftLabelLarge">QBT_TR(Cookie:)QBT_TR</label>
<input type="text" id="cookie" name="cookie" style="width: 16em;"/>
<label for="cookie" class="leftLabelLarge">QBT_TR(Cookie:)QBT_TR</label>
<input type="text" id="cookie" name="cookie" style="width: 16em;"/>
</div>
<div class="formRow">
<label for="label" class="leftLabelLarge">QBT_TR(Label:)QBT_TR</label>
<input type="text" id="label" name="label" style="width: 16em;"/>
<label for="category" class="leftLabelLarge">QBT_TR(Category:)QBT_TR</label>
<input type="text" id="category" name="category" style="width: 16em;"/>
</div>
<div id="submitbutton" style="margin-top: 12px; text-align: center;">
<button type="submit" id="submitButton">QBT_TR(Download)QBT_TR</button>
@@ -39,13 +39,13 @@
var submitted = false;
$('downloadForm').addEventListener("submit", function() {
$('download_spinner').style.display = "block";
$('download_spinner').style.display = "block";
submitted = true;
});
$('download_frame').addEventListener("load", function() {
if (submitted)
window.parent.closeWindows();
if (submitted)
window.parent.closeWindows();
});
</script>
<div id="download_spinner" class="mochaSpinner"></div>

View File

@@ -11,6 +11,6 @@
<li id="errored_filter"><a href="#" onclick="setFilter('errored');return false;"><img src="images/skin/error.png"/>QBT_TR(Errored (0))QBT_TR</a></li>
</ul>
<br/>
<span class="filterTitle">QBT_TR(Labels)QBT_TR</span>
<ul id="filterLabelList" class="filterList">
<span class="filterTitle">QBT_TR(Categories)QBT_TR</span>
<ul id="filterCategoryList" class="filterList">
</ul>

View File

@@ -2,41 +2,41 @@
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" dir="ltr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>QBT_TR(New Label)QBT_TR</title>
<title>QBT_TR(New Category)QBT_TR</title>
<link rel="stylesheet" href="css/style.css" type="text/css" />
<script type="text/javascript" src="scripts/mootools-1.2-core-yc.js" charset="utf-8"></script>
<script type="text/javascript" src="scripts/mootools-1.2-more.js" charset="utf-8"></script>
<script type="text/javascript">
var newLabelKeyboardEvents = new Keyboard({
var newCategoryKeyboardEvents = new Keyboard({
defaultEventType: 'keydown',
events: {
'enter': function (event) {
$('newLabelButton').click();
$('newCategoryButton').click();
event.preventDefault();
}
}
});
newLabelKeyboardEvents.activate();
newCategoryKeyboardEvents.activate();
window.addEvent('domready', function() {
$('newLabel').focus();
$('newLabelButton').addEvent('click', function(e) {
$('newCategory').focus();
$('newCategoryButton').addEvent('click', function(e) {
new Event(e).stop();
// check field
var labelName = $('newLabel').value.trim();
if (labelName == null || labelName == "")
var categoryName = $('newCategory').value.trim();
if (categoryName == null || categoryName == "")
return false;
if (labelName.match("[\\\\/:?\"*<>|]") !== null) {
alert("QBT_TR(Invalid label name:\nPlease do not use any special characters in the label name.)QBT_TR");
if (categoryName.match("^([^\\\\\\/]|[^\\\\\\/]([^\\\\\\/]|\\/(?=[^\\/]))*[^\\\\\\/])$") === null) {
alert("QBT_TR(Invalid category name:\nPlease do not use any special characters in the category name.)QBT_TR");
return false;
}
var hashesList = new URI().getData('hashes');
new Request({
url: 'command/setLabel',
url: 'command/setCategory',
method: 'post',
data: {
hashes: hashesList,
label: labelName
category: categoryName
},
onComplete: function() {
window.parent.closeWindows();
@@ -48,10 +48,10 @@
</head>
<body>
<div style="padding: 10px 10px 0px 10px;">
<p style="font-weight: bold;">QBT_TR(Label)QBT_TR:</p>
<input type="text" id="newLabel" value="" maxlength="100" style="width: 220px;"/>
<p style="font-weight: bold;">QBT_TR(Category)QBT_TR:</p>
<input type="text" id="newCategory" value="" maxlength="100" style="width: 220px;"/>
<div style="text-align: center;">
<input type="button" value="QBT_TR(Add)QBT_TR" id="newLabelButton"/>
<input type="button" value="QBT_TR(Add)QBT_TR" id="newCategoryButton"/>
</div>
</div>
</body>

View File

@@ -73,7 +73,7 @@
<i>QBT_TR(Supported parameters (case sensitive):)QBT_TR
<ul>
<li>QBT_TR(%N: Torrent name)QBT_TR</li>
<li>QBT_TR(%L: Label)QBT_TR</li>
<li>QBT_TR(%L: Category)QBT_TR</li>
<li>QBT_TR(%F: Content path (same as root path for multifile torrent))QBT_TR</li>
<li>QBT_TR(%R: Root path (first torrent subdirectory path))QBT_TR</li>
<li>QBT_TR(%D: Save path)QBT_TR</li>
@@ -1263,19 +1263,19 @@ applyPreferences = function() {
var json_str = JSON.encode(settings);
new Request({url: 'command/setPreferences',
method: 'post',
data: {'json': json_str,
method: 'post',
data: {'json': json_str,
},
onFailure: function() {
alert("QBT_TR(Unable to save program preferences, qBittorrent is probably unreachable.)QBT_TR");
window.parent.closeWindows();
},
onSuccess: function() {
onSuccess: function() {
// Close window
window.parent.location.reload();
window.parent.closeWindows();
}
}).send();
window.parent.closeWindows();
}
}).send();
};
loadPreferences();

View File

@@ -38,21 +38,21 @@ var alternativeSpeedLimits = false;
var queueing_enabled = true;
var syncMainDataTimerPeriod = 1500;
var LABELS_ALL = 1;
var LABELS_UNLABELLED = 2;
var CATEGORIES_ALL = 1;
var CATEGORIES_UNCATEGORIZED = 2;
var label_list = {};
var category_list = {};
var selected_label = LABELS_ALL;
var setLabelFilter = function(){};
var selected_category = CATEGORIES_ALL;
var setCategoryFilter = function(){};
var selected_filter = getLocalStorageItem('selected_filter', 'all');
var setFilter = function(){};
var loadSelectedLabel = function () {
selected_label = getLocalStorageItem('selected_label', LABELS_ALL);
var loadSelectedCategory = function () {
selected_category = getLocalStorageItem('selected_category', CATEGORIES_ALL);
};
loadSelectedLabel();
loadSelectedCategory();
function genHash(string) {
var hash = 0;
@@ -103,10 +103,10 @@ window.addEvent('load', function () {
resizeLimit : [100, 300]
});
setLabelFilter = function(hash) {
selected_label = hash;
localStorage.setItem('selected_label', selected_label);
highlightSelectedLabel();
setCategoryFilter = function(hash) {
selected_category = hash;
localStorage.setItem('selected_category', selected_category);
highlightSelectedCategory();
if (typeof torrentsTable.table != 'undefined')
updateMainData();
};
@@ -170,59 +170,59 @@ window.addEvent('load', function () {
var syncMainDataLastResponseId = 0;
var serverState = {};
var removeTorrentFromLabelList = function(hash) {
var removeTorrentFromCategoryList = function(hash) {
if (hash == null || hash == "")
return false;
var removed = false;
Object.each(label_list, function(label) {
if (Object.contains(label.torrents, hash)) {
Object.each(category_list, function(category) {
if (Object.contains(category.torrents, hash)) {
removed = true;
label.torrents.splice(label.torrents.indexOf(hash), 1);
category.torrents.splice(category.torrents.indexOf(hash), 1);
}
});
return removed;
};
var addTorrentToLabelList = function(torrent) {
var label = torrent['label'];
if (label == null)
var addTorrentToCategoryList = function(torrent) {
var category = torrent['category'];
if (category == null)
return false;
if (label.length === 0) { // Empty label
removeTorrentFromLabelList(torrent['hash']);
if (category.length === 0) { // Empty category
removeTorrentFromCategoryList(torrent['hash']);
return true;
}
var labelHash = genHash(label);
if (label_list[labelHash] == null) // This should not happen
label_list[labelHash] = {name: label, torrents: []};
if (!Object.contains(label_list[labelHash].torrents, torrent['hash'])) {
removeTorrentFromLabelList(torrent['hash']);
label_list[labelHash].torrents = label_list[labelHash].torrents.combine([torrent['hash']]);
var categoryHash = genHash(category);
if (category_list[categoryHash] == null) // This should not happen
category_list[categoryHash] = {name: category, torrents: []};
if (!Object.contains(category_list[categoryHash].torrents, torrent['hash'])) {
removeTorrentFromCategoryList(torrent['hash']);
category_list[categoryHash].torrents = category_list[categoryHash].torrents.combine([torrent['hash']]);
return true;
}
return false;
};
var updateContextMenu = function () {
var labelList = $('contextLabelList');
labelList.empty();
labelList.appendChild(new Element('li', {html: '<a href="javascript:newLabelFN();"><img src="theme/list-add" alt="QBT_TR(New...)QBT_TR"/> QBT_TR(New...)QBT_TR</a>'}));
labelList.appendChild(new Element('li', {html: '<a href="javascript:updateLabelFN(0);"><img src="theme/edit-clear" alt="QBT_TR(Reset)QBT_TR"/> QBT_TR(Reset)QBT_TR</a>'}));
var categoryList = $('contextCategoryList');
categoryList.empty();
categoryList.appendChild(new Element('li', {html: '<a href="javascript:newCategoryFN();"><img src="theme/list-add" alt="QBT_TR(New...)QBT_TR"/> QBT_TR(New...)QBT_TR</a>'}));
categoryList.appendChild(new Element('li', {html: '<a href="javascript:updateCategoryFN(0);"><img src="theme/edit-clear" alt="QBT_TR(Reset)QBT_TR"/> QBT_TR(Reset)QBT_TR</a>'}));
var sortedLabels = []
Object.each(label_list, function(label) {
sortedLabels.push(label.name);
var sortedCategories = []
Object.each(category_list, function(category) {
sortedCategories.push(category.name);
});
sortedLabels.sort();
sortedCategories.sort();
var first = true;
Object.each(sortedLabels, function(labelName) {
var labelHash = genHash(labelName);
var el = new Element('li', {html: '<a href="javascript:updateLabelFN(\'' + labelHash + '\');"><img src="theme/inode-directory"/> ' + labelName + '</a>'});
Object.each(sortedCategories, function(categoryName) {
var categoryHash = genHash(categoryName);
var el = new Element('li', {html: '<a href="javascript:updateCategoryFN(\'' + categoryHash + '\');"><img src="theme/inode-directory"/> ' + categoryName + '</a>'});
if (first) {
el.addClass('separator');
first = false;
}
labelList.appendChild(el);
categoryList.appendChild(el);
});
};
@@ -242,50 +242,50 @@ window.addEvent('load', function () {
updateFilter('errored', 'QBT_TR(Errored (%1))QBT_TR');
};
var updateLabelList = function() {
var labelList = $('filterLabelList');
if (!labelList)
var updateCategoryList = function() {
var categoryList = $('filterCategoryList');
if (!categoryList)
return;
labelList.empty();
categoryList.empty();
var create_link = function(hash, text, count) {
var html = '<a href="#" onclick="setLabelFilter(' + hash + ');return false;">' +
var html = '<a href="#" onclick="setCategoryFilter(' + hash + ');return false;">' +
'<img src="theme/inode-directory"/>' +
text + ' (' + count + ')' + '</a>';
return new Element('li', {id: hash, html: html});
};
var all = torrentsTable.getRowIds().length;
var unlabelled = 0;
var uncategorized = 0;
Object.each(torrentsTable.rows, function(row) {
if (row['full_data'].label.length === 0)
unlabelled += 1;
if (row['full_data'].category.length === 0)
uncategorized += 1;
});
labelList.appendChild(create_link(LABELS_ALL, 'QBT_TR(All (0))QBT_TR'.replace(' (0)', ''), all));
labelList.appendChild(create_link(LABELS_UNLABELLED, 'QBT_TR(Unlabeled (0))QBT_TR'.replace(' (0)', ''), unlabelled));
categoryList.appendChild(create_link(CATEGORIES_ALL, 'QBT_TR(All (0))QBT_TR'.replace(' (0)', ''), all));
categoryList.appendChild(create_link(CATEGORIES_UNCATEGORIZED, 'QBT_TR(Uncategorized (0))QBT_TR'.replace(' (0)', ''), uncategorized));
var sortedLabels = []
Object.each(label_list, function(label) {
sortedLabels.push(label.name);
var sortedCategories = []
Object.each(category_list, function(category) {
sortedCategories.push(category.name);
});
sortedLabels.sort();
sortedCategories.sort();
Object.each(sortedLabels, function(labelName) {
var labelHash = genHash(labelName);
var labelCount = label_list[labelHash].torrents.length;
labelList.appendChild(create_link(labelHash, labelName, labelCount));
Object.each(sortedCategories, function(categoryName) {
var categoryHash = genHash(categoryName);
var categoryCount = category_list[categoryHash].torrents.length;
categoryList.appendChild(create_link(categoryHash, categoryName, categoryCount));
});
highlightSelectedLabel();
highlightSelectedCategory();
};
var highlightSelectedLabel = function() {
var labelList = $('filterLabelList');
if (!labelList)
var highlightSelectedCategory = function() {
var categoryList = $('filterCategoryList');
if (!categoryList)
return;
var childrens = labelList.childNodes;
var childrens = categoryList.childNodes;
for (var i in childrens) {
if (childrens[i].id == selected_label)
if (childrens[i].id == selected_category)
childrens[i].className = "selectedFilter";
else
childrens[i].className = "";
@@ -308,43 +308,43 @@ window.addEvent('load', function () {
onSuccess : function (response) {
$('error_div').set('html', '');
if (response) {
var update_labels = false;
var update_categories = false;
var full_update = (response['full_update'] == true);
if (full_update) {
torrentsTable.clear();
label_list = {};
category_list = {};
}
if (response['rid']) {
syncMainDataLastResponseId = response['rid'];
}
if (response['labels']) {
response['labels'].each(function(label) {
var labelHash = genHash(label);
label_list[labelHash] = {name: label, torrents: []};
if (response['categories']) {
response['categories'].each(function(category) {
var categoryHash = genHash(category);
category_list[categoryHash] = {name: category, torrents: []};
});
update_labels = true;
update_categories = true;
}
if (response['labels_removed']) {
response['labels_removed'].each(function(label) {
var labelHash = genHash(label);
delete label_list[labelHash];
if (response['categories_removed']) {
response['categories_removed'].each(function(category) {
var categoryHash = genHash(category);
delete category_list[categoryHash];
});
update_labels = true;
update_categories = true;
}
if (response['torrents']) {
for (var key in response['torrents']) {
response['torrents'][key]['hash'] = key;
response['torrents'][key]['rowId'] = key;
torrentsTable.updateRowData(response['torrents'][key]);
if (addTorrentToLabelList(response['torrents'][key]))
update_labels = true;
if (addTorrentToCategoryList(response['torrents'][key]))
update_categories = true;
}
}
if (response['torrents_removed'])
response['torrents_removed'].each(function (hash) {
torrentsTable.removeRow(hash);
removeTorrentFromLabelList(hash);
update_labels = true; // Allways to update All label
removeTorrentFromCategoryList(hash);
update_categories = true; // Allways to update All category
});
torrentsTable.updateTable(full_update);
torrentsTable.altRow();
@@ -355,8 +355,8 @@ window.addEvent('load', function () {
processServerState();
}
updateFiltersList();
if (update_labels) {
updateLabelList();
if (update_categories) {
updateCategoryList();
updateContextMenu();
}
}
@@ -683,4 +683,4 @@ var loadTorrentPeersData = function(){
updateTorrentPeersData = function(){
clearTimeout(loadTorrentPeersTimer);
loadTorrentPeersData();
};
};

View File

@@ -452,7 +452,7 @@ var TorrentsTable = new Class({
this.newColumn('upspeed', 'width: 100px', 'QBT_TR(Up Speed)QBT_TR');
this.newColumn('eta', 'width: 100px', 'QBT_TR(ETA)QBT_TR');
this.newColumn('ratio', 'width: 100px', 'QBT_TR(Ratio)QBT_TR');
this.newColumn('label', 'width: 100px', 'QBT_TR(Label)QBT_TR');
this.newColumn('category', 'width: 100px', 'QBT_TR(Category)QBT_TR');
this.columns['state_icon'].onclick = '';
this.columns['state_icon'].dataProperties[0] = 'state';
@@ -620,7 +620,7 @@ var TorrentsTable = new Class({
};
},
applyFilter : function (row, filterName, labelName) {
applyFilter : function (row, filterName, categoryName) {
var state = row['full_data'].state;
var inactive = false;
var r;
@@ -662,13 +662,13 @@ var TorrentsTable = new Class({
break;
}
if (labelName == LABELS_ALL)
if (categoryName == CATEGORIES_ALL)
return true;
if (labelName == LABELS_UNLABELLED && row['full_data'].label.length === 0)
if (categoryName == CATEGORIES_UNCATEGORIZED && row['full_data'].category.length === 0)
return true;
if (labelName != genHash( row['full_data'].label) )
if (categoryName != genHash(row['full_data'].category))
return false;
return true;
@@ -679,7 +679,7 @@ var TorrentsTable = new Class({
var rows = this.rows.getValues();
for (i = 0; i < rows.length; i++)
if (this.applyFilter(rows[i], filterName, LABELS_ALL)) cnt++;
if (this.applyFilter(rows[i], filterName, CATEGORIES_ALL)) cnt++;
return cnt;
},
@@ -690,7 +690,7 @@ var TorrentsTable = new Class({
var rows = this.rows.getValues();
for (i = 0; i < rows.length; i++)
if (this.applyFilter(rows[i], selected_filter, selected_label)) {
if (this.applyFilter(rows[i], selected_filter, selected_category)) {
filteredRows.push(rows[i]);
filteredRows[rows[i].rowId] = rows[i];
}

View File

@@ -304,14 +304,14 @@ initializeWindows = function() {
}
};
newLabelFN = function () {
newCategoryFN = function () {
var h = torrentsTable.selectedRowsIds();
if (h.length) {
new MochaUI.Window({
id: 'newLabelPage',
title: "QBT_TR(New Label)QBT_TR",
id: 'newCategoryPage',
title: "QBT_TR(New Category)QBT_TR",
loadMethod: 'iframe',
contentURL: 'newlabel.html?hashes=' + h.join('|'),
contentURL: 'newcategory.html?hashes=' + h.join('|'),
scrollbars: false,
resizable: false,
maximizable: false,
@@ -323,18 +323,18 @@ initializeWindows = function() {
}
};
updateLabelFN = function (labelHash) {
var labelName = '';
if (labelHash != 0)
var labelName = label_list[labelHash].name;
updateCategoryFN = function (categoryHash) {
var categoryName = '';
if (categoryHash != 0)
var categoryName = category_list[categoryHash].name;
var h = torrentsTable.selectedRowsIds();
if (h.length) {
new Request({
url: 'command/setLabel',
url: 'command/setCategory',
method: 'post',
data: {
hashes: h.join("|"),
label: labelName
category: categoryName
}
}).send();
}

View File

@@ -22,7 +22,7 @@
<input type="text" id="savepath" name="savepath" style="width: 16em;"/>
</div>
<div class="formRow">
<label for="label" class="leftLabelLarge">QBT_TR(Label:)QBT_TR</label>
<label for="label" class="leftLabelLarge">QBT_TR(Category:)QBT_TR</label>
<input type="text" id="label" name="label"/ style="width: 16em;"/>
</div>
<div id="submitbutton" style="margin-top: 30px; text-align: center;">