mirror of
https://github.com/Carve/qbittorrent-webui-cjratliff.com.git
synced 2025-02-28 15:40:28 +01:00
832 lines
34 KiB
HTML
832 lines
34 KiB
HTML
<style>
|
|
#rssdownloaderpage_content {
|
|
height: calc(100% - 30px);
|
|
}
|
|
|
|
#RssDownloader {
|
|
height: 100%;
|
|
}
|
|
|
|
#leftRssDownloaderColumn {
|
|
width: 25%;
|
|
float: left;
|
|
}
|
|
|
|
#rulesTable,
|
|
#rssDownloaderArticlesTable {
|
|
overflow: auto;
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
|
|
#centerRssDownloaderColumn {
|
|
width: 50%;
|
|
float: left;
|
|
}
|
|
|
|
#rightRssDownloaderColumn {
|
|
width: 25%;
|
|
float: left;
|
|
}
|
|
|
|
.fullWidth {
|
|
width: 100%;
|
|
max-width: none;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.noWrap {
|
|
white-space: nowrap;
|
|
}
|
|
|
|
#rssDownloaderFeeds {
|
|
height: calc(100% - 355px);
|
|
overflow: hidden;
|
|
}
|
|
|
|
#rssDownloaderFeedsTable {
|
|
height: calc(100% - 21px);
|
|
width: 100%;
|
|
overflow: auto;
|
|
}
|
|
|
|
#saveButton {
|
|
margin-top: 5px;
|
|
width: 100%;
|
|
}
|
|
|
|
.articleTableFeed td {
|
|
font-weight: bold;
|
|
}
|
|
|
|
.articleTableArticle td {
|
|
padding-left: 22px;
|
|
}
|
|
|
|
#rssDownloaderDisabled {
|
|
color: var(--color-text-red);
|
|
font-style: italic;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
#rulesTable table,
|
|
#rssDownloaderFeedsTable table,
|
|
#rssDownloaderArticlesTable table {
|
|
width: 100%;
|
|
}
|
|
|
|
#ignoreDaysValue {
|
|
width: 4em;
|
|
}
|
|
|
|
#lastMatchDiv {
|
|
float: right;
|
|
}
|
|
|
|
#ruleSettings {
|
|
padding: 4px 10px 4px 10px
|
|
}
|
|
|
|
#topMenuBar {
|
|
height: 26px;
|
|
}
|
|
|
|
#rulesTableDesc {
|
|
vertical-align: middle;
|
|
}
|
|
|
|
.imageButton {
|
|
height: 22px;
|
|
width: 24px;
|
|
margin: 1px 2px 1px 2px;
|
|
border: 1px solid;
|
|
border-radius: 2px;
|
|
background-color: #efefef;
|
|
border-color: #767676;
|
|
background-position: center center;
|
|
vertical-align: middle;
|
|
}
|
|
|
|
.imageButton:hover {
|
|
background-color: #e5e5e5;
|
|
border-color: #4f4f4f;
|
|
}
|
|
|
|
.imageButton:active {
|
|
background-color: #f5f5f5;
|
|
border-color: #8d8d8d;
|
|
}
|
|
|
|
#newRuleButton {
|
|
float: right;
|
|
background-image: url('images/list-add.svg');
|
|
}
|
|
|
|
#deleteRuleButton {
|
|
float: right;
|
|
background-image: url('images/edit-clear.svg');
|
|
}
|
|
|
|
</style>
|
|
|
|
<div id="RssDownloader" class="RssDownloader">
|
|
<div id="rssDownloaderDisabled" class="invisible">
|
|
Auto downloading of RSS torrents is disabled now! You can enable it in application settings.
|
|
</div>
|
|
<div id="leftRssDownloaderColumn">
|
|
<div id="topMenuBar">
|
|
<b id="rulesTableDesc">Download Rules</b>
|
|
<button type="button" id="newRuleButton" aria-label="Add rule" class="imageButton" onclick="qBittorrent.RssDownloader.addRule()"></button>
|
|
<button type="button" id="deleteRuleButton" aria-label="Remove rule" class="imageButton" onclick="qBittorrent.RssDownloader.removeSelectedRule()"></button>
|
|
</div>
|
|
<div id="rulesTable">
|
|
<div id="rulesSelectionCheckBoxList">
|
|
<div id="rssDownloaderRuleFixedHeaderDiv" class="dynamicTableFixedHeaderDiv invisible">
|
|
<table class="dynamicTable unselectable">
|
|
<thead>
|
|
<tr class="dynamicTableHeader"></tr>
|
|
</thead>
|
|
</table>
|
|
</div>
|
|
<div id="rssDownloaderRuleTableDiv" class="dynamicTableDiv">
|
|
<table class="dynamicTable unselectable">
|
|
<thead>
|
|
<tr class="dynamicTableHeader"></tr>
|
|
</thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="centerRssDownloaderColumn">
|
|
<fieldset class="settings" id="ruleSettings">
|
|
<legend>Rule Definition</legend>
|
|
<div class="formRow">
|
|
<input disabled type="checkbox" id="useRegEx" onclick="qBittorrent.RssDownloader.setElementTitles()">
|
|
<label for="useRegEx">Use Regular Expressions</label>
|
|
</div>
|
|
<table class="fullWidth">
|
|
<tbody>
|
|
<tr>
|
|
<td>
|
|
<label for="mustContainText" class="noWrap">Must Contain:</label>
|
|
</td>
|
|
<td class="fullWidth">
|
|
<input disabled type="text" id="mustContainText" class="fullWidth" autocorrect="off" autocapitalize="none">
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>
|
|
<label for="mustNotContainText" class="noWrap">Must Not Contain:</label>
|
|
</td>
|
|
<td class="fullWidth">
|
|
<input disabled type="text" id="mustNotContainText" class="fullWidth" autocorrect="off" autocapitalize="none">
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>
|
|
<label for="episodeFilterText" class="noWrap">Episode Filter:</label>
|
|
</td>
|
|
<td class="fullWidth">
|
|
<input disabled type="text" id="episodeFilterText" class="fullWidth" autocorrect="off" autocapitalize="none">
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<div class="formRow" title="Smart Episode Filter will check the episode number to prevent downloading of duplicates.
|
|
Supports the formats: S01E01, 1x1, 2017.12.31 and 31.12.2017 (Date formats also support - as a separator)">
|
|
<input disabled type="checkbox" id="useSmartFilter">
|
|
<label for="useSmartFilter">Use Smart Episode Filter</label>
|
|
</div>
|
|
|
|
<hr>
|
|
|
|
<table class="fullWidth">
|
|
<tbody>
|
|
<tr>
|
|
<td>
|
|
<label class="noWrap" for="assignCategoryCombobox">Assign Category:</label>
|
|
</td>
|
|
<td class="fullWidth">
|
|
<select disabled id="assignCategoryCombobox" class="fullWidth">
|
|
<option value=""></option>
|
|
</select>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>
|
|
<label class="noWrap" for="ruleAddTags">Add Tags:</label>
|
|
</td>
|
|
<td class="fullWidth">
|
|
<input type="text" id="ruleAddTags" class="fullWidth" autocapitalize="none" autocorrect="off">
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<div class="formRow">
|
|
<input disabled type="checkbox" id="savetoDifferentDir">
|
|
<label for="savetoDifferentDir">Save to a Different Directory</label>
|
|
</div>
|
|
<table class="fullWidth">
|
|
<tbody>
|
|
<tr>
|
|
<td>
|
|
<label class="noWrap" for="saveToText">Save to:</label>
|
|
</td>
|
|
<td class="fullWidth">
|
|
<input disabled type="text" class="fullWidth" id="saveToText" autocorrect="off" autocapitalize="none">
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<table>
|
|
<tbody>
|
|
<tr>
|
|
<td><label for="ignoreDaysValue">Ignore Subsequent Matches for (0 to Disable)</label></td>
|
|
<td><input type="number" id="ignoreDaysValue" min="0"> days</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<div id="lastMatchDiv">
|
|
<span id="lastMatchText">Last Match: Unknown</span>
|
|
</div>
|
|
<table class="fullWidth">
|
|
<tbody>
|
|
<tr>
|
|
<td>
|
|
<label class="noWrap" for="addStoppedCombobox">Add Stopped:</label>
|
|
</td>
|
|
<td class="fullWidth">
|
|
<select disabled id="addStoppedCombobox" class="fullWidth">
|
|
<option value="default">Use global settings</option>
|
|
<option value="always">Always</option>
|
|
<option value="never">Never</option>
|
|
</select>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<table class="fullWidth">
|
|
<tbody>
|
|
<tr>
|
|
<td>
|
|
<label class="noWrap" for="contentLayoutCombobox">Torrent content layout:</label>
|
|
</td>
|
|
<td class="fullWidth">
|
|
<select disabled id="contentLayoutCombobox" class="fullWidth">
|
|
<option value="Default">Use global settings</option>
|
|
<option value="Original">Original</option>
|
|
<option value="Subfolder">Create subfolder</option>
|
|
<option value="NoSubfolder">Don't create subfolder</option>
|
|
</select>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</fieldset>
|
|
<fieldset class="settings" id="rssDownloaderFeeds">
|
|
<legend>Apply Rule to Feeds:</legend>
|
|
<div id="rssDownloaderFeedsTable">
|
|
<div id="rssDownloaderFeedSelectionFixedHeaderDiv" class="dynamicTableFixedHeaderDiv invisible">
|
|
<table class="dynamicTable unselectable">
|
|
<thead>
|
|
<tr class="dynamicTableHeader"></tr>
|
|
</thead>
|
|
</table>
|
|
</div>
|
|
<div id="rssDownloaderFeedSelectionTableDiv" class="dynamicTableDiv">
|
|
<table class="dynamicTable unselectable">
|
|
<thead>
|
|
<tr class="dynamicTableHeader"></tr>
|
|
</thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</fieldset>
|
|
<button disabled type="button" id="saveButton" onclick="qBittorrent.RssDownloader.saveSettings()">
|
|
Save
|
|
</button>
|
|
</div>
|
|
<div id="rightRssDownloaderColumn">
|
|
<b id="articleTableDesc">Matching RSS Articles</b>
|
|
<div id="rssDownloaderArticlesTable">
|
|
<div id="rssDownloaderArticlesFixedHeaderDiv" class="dynamicTableFixedHeaderDiv invisible">
|
|
<table class="dynamicTable unselectable">
|
|
<thead>
|
|
<tr class="dynamicTableHeader"></tr>
|
|
</thead>
|
|
</table>
|
|
</div>
|
|
<div id="rssDownloaderArticlesTableDiv" class="dynamicTableDiv">
|
|
<table class="dynamicTable unselectable">
|
|
<thead>
|
|
<tr class="dynamicTableHeader"></tr>
|
|
</thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<ul id="rssDownloaderRuleMenu" class="contextMenu">
|
|
<li><a href="#addRule"><img src="images/list-add.svg" alt="Add new rule..."> Add new rule...</a></li>
|
|
<li><a href="#deleteRule"><img src="images/edit-clear.svg" alt="Delete rule"> Delete rule</a></li>
|
|
<li class="separator"><a href="#renameRule"><img src="images/edit-rename.svg" alt="Rename rule..."> Rename rule...</a></li>
|
|
<li class="separator"><a href="#clearDownloadedEpisodes"><img src="images/edit-clear.svg" alt="Clear downloaded episodes..."> Clear downloaded episodes...</a></li>
|
|
</ul>
|
|
|
|
<script>
|
|
"use strict";
|
|
|
|
window.qBittorrent ??= {};
|
|
window.qBittorrent.RssDownloader ??= (() => {
|
|
const exports = () => {
|
|
return {
|
|
setup: setup,
|
|
updateRulesList: updateRulesList,
|
|
showRule: showRule,
|
|
renameRule: renameRule,
|
|
modifyRuleState: modifyRuleState,
|
|
saveSettings: saveSettings,
|
|
rssDownloaderRulesTable: rssDownloaderRulesTable,
|
|
rssDownloaderFeedSelectionTable: rssDownloaderFeedSelectionTable,
|
|
setElementTitles: setElementTitles,
|
|
addRule: addRule,
|
|
removeSelectedRule: removeSelectedRule
|
|
};
|
|
};
|
|
|
|
const rssDownloaderRulesTable = new window.qBittorrent.DynamicTable.RssDownloaderRulesTable();
|
|
const rssDownloaderFeedSelectionTable = new window.qBittorrent.DynamicTable.RssDownloaderFeedSelectionTable();
|
|
const rssDownloaderArticlesTable = new window.qBittorrent.DynamicTable.RssDownloaderArticlesTable();
|
|
|
|
let rulesList = {};
|
|
let feedList = [];
|
|
|
|
const setup = () => {
|
|
const pref = window.parent.qBittorrent.Cache.preferences.get();
|
|
|
|
if (!pref.rss_auto_downloading_enabled)
|
|
$("rssDownloaderDisabled").removeClass("invisible");
|
|
|
|
// recalculate height
|
|
const warningHeight = $("rssDownloaderDisabled").getBoundingClientRect().height;
|
|
|
|
$("leftRssDownloaderColumn").style.height = "calc(100% - " + warningHeight + "px)";
|
|
$("centerRssDownloaderColumn").style.height = "calc(100% - " + warningHeight + "px)";
|
|
$("rightRssDownloaderColumn").style.height = "calc(100% - " + warningHeight + "px)";
|
|
|
|
$("rulesTable").style.height = "calc(100% - " + $("rulesTableDesc").getBoundingClientRect().height + "px)";
|
|
$("rssDownloaderArticlesTable").style.height = "calc(100% - " + $("articleTableDesc").getBoundingClientRect().height + "px)";
|
|
|
|
const centerRowNotTableHeight = $("saveButton").getBoundingClientRect().height
|
|
+ $("ruleSettings").getBoundingClientRect().height + 15;
|
|
|
|
$("rssDownloaderFeeds").style.height = "calc(100% - " + centerRowNotTableHeight + "px)";
|
|
|
|
// firefox calculates the height of the table inside fieldset differently and thus doesn't need the offset
|
|
if (navigator.userAgent.toLowerCase().indexOf("firefox") > -1) {
|
|
$("rssDownloaderFeedsTable").style.height = "100%";
|
|
}
|
|
else {
|
|
const outsideTableHeight = ($("rssDownloaderFeedsTable").getBoundingClientRect().top - $("rssDownloaderFeeds").getBoundingClientRect().top) - 10;
|
|
$("rssDownloaderFeedsTable").style.height = "calc(100% - " + outsideTableHeight + "px)";
|
|
}
|
|
|
|
const rssDownloaderRuleContextMenu = new window.qBittorrent.ContextMenu.RssDownloaderRuleContextMenu({
|
|
targets: "",
|
|
menu: "rssDownloaderRuleMenu",
|
|
actions: {
|
|
addRule: addRule,
|
|
deleteRule: removeSelectedRule,
|
|
renameRule: (el) => {
|
|
renameRule(rssDownloaderRulesTable.getRow(rssDownloaderRulesTable.selectedRows[0]).full_data.name);
|
|
},
|
|
clearDownloadedEpisodes: (el) => {
|
|
clearDownloadedEpisodes(rssDownloaderRulesTable.selectedRows
|
|
.map((sRow) => rssDownloaderRulesTable.getRow(sRow).full_data.name));
|
|
}
|
|
},
|
|
offsets: {
|
|
x: -22,
|
|
y: -4
|
|
}
|
|
});
|
|
rssDownloaderRulesTable.setup("rssDownloaderRuleTableDiv", "rssDownloaderRuleFixedHeaderDiv", rssDownloaderRuleContextMenu);
|
|
rssDownloaderFeedSelectionTable.setup("rssDownloaderFeedSelectionTableDiv", "rssDownloaderFeedSelectionFixedHeaderDiv");
|
|
rssDownloaderArticlesTable.setup("rssDownloaderArticlesTableDiv", "rssDownloaderArticlesFixedHeaderDiv");
|
|
|
|
rssDownloaderRuleContextMenu.addTarget($("rulesTable"));
|
|
// deselect feed when clicking on empty part of table
|
|
$("rulesTable").addEventListener("click", (e) => {
|
|
rssDownloaderRulesTable.deselectAll();
|
|
rssDownloaderRulesTable.deselectRow();
|
|
showRule("");
|
|
});
|
|
$("rulesTable").addEventListener("contextmenu", (e) => {
|
|
if (e.toElement.nodeName === "DIV") {
|
|
rssDownloaderRulesTable.deselectAll();
|
|
rssDownloaderRulesTable.deselectRow();
|
|
rssDownloaderRuleContextMenu.updateMenuItems();
|
|
showRule("");
|
|
}
|
|
});
|
|
// get all categories and add to combobox
|
|
new Request.JSON({
|
|
url: "api/v2/torrents/categories",
|
|
method: "get",
|
|
noCache: true,
|
|
onSuccess: (response) => {
|
|
const combobox = $("assignCategoryCombobox");
|
|
for (const cat in response) {
|
|
if (!Object.hasOwn(response, cat))
|
|
continue;
|
|
|
|
const option = document.createElement("option");
|
|
option.text = option.value = cat;
|
|
combobox.add(option);
|
|
}
|
|
}
|
|
}).send();
|
|
// get all rss feed
|
|
new Request.JSON({
|
|
url: "api/v2/rss/items",
|
|
method: "get",
|
|
noCache: true,
|
|
data: {
|
|
withData: false
|
|
},
|
|
onSuccess: (response) => {
|
|
feedList = [];
|
|
const flatten = (root) => {
|
|
for (const child in root) {
|
|
if (root[child].uid !== undefined)
|
|
feedList.push({ name: child, url: root[child].url });
|
|
else
|
|
flatten(root[child]);
|
|
}
|
|
};
|
|
flatten(response);
|
|
}
|
|
}).send();
|
|
$("savetoDifferentDir").addEventListener("click", () => {
|
|
$("saveToText").disabled = !$("savetoDifferentDir").checked;
|
|
});
|
|
updateRulesList();
|
|
};
|
|
|
|
const updateRulesList = () => {
|
|
// get all rules
|
|
new Request.JSON({
|
|
url: "api/v2/rss/rules",
|
|
method: "get",
|
|
noCache: true,
|
|
onSuccess: (response) => {
|
|
rssDownloaderRulesTable.clear();
|
|
let rowCount = 0;
|
|
for (const rule in response) {
|
|
if (!Object.hasOwn(response, rule))
|
|
continue;
|
|
rssDownloaderRulesTable.updateRowData({
|
|
rowId: rowCount++,
|
|
checked: response[rule].enabled,
|
|
name: rule
|
|
});
|
|
}
|
|
rssDownloaderRulesTable.updateTable(false);
|
|
rulesList = response;
|
|
}
|
|
}).send();
|
|
};
|
|
|
|
const modifyRuleState = (rule, setting, newState, callback = () => {}) => {
|
|
rulesList[rule][setting] = newState;
|
|
new Request({
|
|
url: "api/v2/rss/setRule",
|
|
method: "post",
|
|
data: {
|
|
ruleName: rule,
|
|
ruleDef: JSON.stringify(rulesList[rule])
|
|
},
|
|
onSuccess: () => {
|
|
callback();
|
|
}
|
|
}).send();
|
|
};
|
|
|
|
const addRule = () => {
|
|
new MochaUI.Window({
|
|
id: "newRulePage",
|
|
icon: "images/qbittorrent-tray.svg",
|
|
title: "New rule name",
|
|
loadMethod: "iframe",
|
|
contentURL: "newrule.html",
|
|
scrollbars: false,
|
|
resizable: false,
|
|
maximizable: false,
|
|
width: 350,
|
|
height: 100
|
|
});
|
|
};
|
|
|
|
const renameRule = (rule) => {
|
|
new MochaUI.Window({
|
|
id: "renameRulePage",
|
|
icon: "images/qbittorrent-tray.svg",
|
|
title: "Rule renaming",
|
|
loadMethod: "iframe",
|
|
contentURL: "rename_rule.html?rule=" + encodeURIComponent(rule),
|
|
scrollbars: false,
|
|
resizable: false,
|
|
maximizable: false,
|
|
width: 350,
|
|
height: 100
|
|
});
|
|
};
|
|
|
|
const removeSelectedRule = () => {
|
|
if (rssDownloaderRulesTable.selectedRows.length === 0)
|
|
return;
|
|
removeRules(rssDownloaderRulesTable.selectedRows.map((sRow) =>
|
|
rssDownloaderRulesTable.getRow(sRow).full_data.name));
|
|
};
|
|
|
|
const removeRules = (rules) => {
|
|
const encodedRules = rules.map((rule) => encodeURIComponent(rule));
|
|
new MochaUI.Window({
|
|
id: "removeRulePage",
|
|
icon: "images/qbittorrent-tray.svg",
|
|
title: "Rule deletion confirmation",
|
|
loadMethod: "iframe",
|
|
contentURL: "confirmruledeletion.html?rules=" + encodeURIComponent(encodedRules.join("|")),
|
|
scrollbars: false,
|
|
resizable: false,
|
|
maximizable: false,
|
|
width: 360,
|
|
height: 80
|
|
});
|
|
};
|
|
|
|
const clearDownloadedEpisodes = (rules) => {
|
|
const encodedRules = rules.map((rule) => encodeURIComponent(rule));
|
|
new MochaUI.Window({
|
|
id: "clearRulesPage",
|
|
icon: "images/qbittorrent-tray.svg",
|
|
title: "New rule name",
|
|
loadMethod: "iframe",
|
|
contentURL: "confirmruleclear.html?rules=" + encodeURIComponent(encodedRules.join("|")),
|
|
scrollbars: false,
|
|
resizable: false,
|
|
maximizable: false,
|
|
width: 350,
|
|
height: 85
|
|
});
|
|
};
|
|
|
|
const saveSettings = () => {
|
|
const lastSelectedRow = rssDownloaderRulesTable.selectedRows.at(-1);
|
|
const rule = rssDownloaderRulesTable.getRow(lastSelectedRow).full_data.name;
|
|
|
|
rulesList[rule].useRegex = $("useRegEx").checked;
|
|
rulesList[rule].mustContain = $("mustContainText").value;
|
|
rulesList[rule].mustNotContain = $("mustNotContainText").value;
|
|
rulesList[rule].episodeFilter = $("episodeFilterText").value;
|
|
rulesList[rule].smartFilter = $("useSmartFilter").checked;
|
|
rulesList[rule].ignoreDays = parseInt($("ignoreDaysValue").value, 10);
|
|
rulesList[rule].affectedFeeds = [...rssDownloaderFeedSelectionTable.getRowValues()]
|
|
.filter((row) => row.full_data.checked)
|
|
.map((row) => row.full_data.url);
|
|
|
|
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;
|
|
}
|
|
else {
|
|
rulesList[rule].torrentParams.save_path = "";
|
|
}
|
|
|
|
switch ($("addStoppedCombobox").value) {
|
|
case "default":
|
|
rulesList[rule].torrentParams.stopped = null;
|
|
break;
|
|
case "always":
|
|
rulesList[rule].torrentParams.stopped = true;
|
|
break;
|
|
case "never":
|
|
rulesList[rule].torrentParams.stopped = false;
|
|
break;
|
|
}
|
|
|
|
switch ($("contentLayoutCombobox").value) {
|
|
case "Default":
|
|
rulesList[rule].torrentParams.content_layout = null;
|
|
break;
|
|
case "Original":
|
|
rulesList[rule].torrentParams.content_layout = "Original";
|
|
break;
|
|
case "Subfolder":
|
|
rulesList[rule].torrentParams.content_layout = "Subfolder";
|
|
break;
|
|
case "NoSubfolder":
|
|
rulesList[rule].torrentParams.content_layout = "NoSubfolder";
|
|
break;
|
|
}
|
|
|
|
new Request({
|
|
url: "api/v2/rss/setRule",
|
|
method: "post",
|
|
data: {
|
|
ruleName: rule,
|
|
ruleDef: JSON.stringify(rulesList[rule])
|
|
},
|
|
onSuccess: () => {
|
|
updateMatchingArticles(rule);
|
|
}
|
|
}).send();
|
|
};
|
|
|
|
const updateMatchingArticles = (ruleName) => {
|
|
new Request.JSON({
|
|
url: "api/v2/rss/matchingArticles",
|
|
method: "get",
|
|
noCache: true,
|
|
data: {
|
|
ruleName: ruleName
|
|
},
|
|
onSuccess: (response) => {
|
|
rssDownloaderArticlesTable.clear();
|
|
let rowCount = 0;
|
|
for (const feed in response) {
|
|
if (!Object.hasOwn(response, feed))
|
|
continue;
|
|
rssDownloaderArticlesTable.updateRowData({
|
|
rowId: rowCount++,
|
|
name: feed,
|
|
isFeed: true
|
|
});
|
|
response[feed].each((article) => {
|
|
rssDownloaderArticlesTable.updateRowData({
|
|
rowId: rowCount++,
|
|
name: article,
|
|
isFeed: false
|
|
});
|
|
});
|
|
}
|
|
rssDownloaderArticlesTable.updateTable(false);
|
|
}
|
|
}).send();
|
|
};
|
|
|
|
const showRule = (ruleName) => {
|
|
if (ruleName === "") {
|
|
// disable all
|
|
$("saveButton").disabled = true;
|
|
$("useRegEx").disabled = true;
|
|
$("mustContainText").disabled = true;
|
|
$("mustNotContainText").disabled = true;
|
|
$("episodeFilterText").disabled = true;
|
|
$("useSmartFilter").disabled = true;
|
|
$("assignCategoryCombobox").disabled = true;
|
|
$("ruleAddTags").disabled = true;
|
|
$("savetoDifferentDir").disabled = true;
|
|
$("saveToText").disabled = true;
|
|
$("ignoreDaysValue").disabled = true;
|
|
$("addStoppedCombobox").disabled = true;
|
|
$("contentLayoutCombobox").disabled = true;
|
|
|
|
// reset all boxes
|
|
$("useRegEx").checked = false;
|
|
$("mustContainText").value = "";
|
|
$("mustNotContainText").value = "";
|
|
$("episodeFilterText").value = "";
|
|
$("useSmartFilter").checked = false;
|
|
$("assignCategoryCombobox").value = "default";
|
|
$("ruleAddTags").value = "";
|
|
$("savetoDifferentDir").checked = false;
|
|
$("saveToText").value = "";
|
|
$("ignoreDaysValue").value = 0;
|
|
$("lastMatchText").textContent = "Last Match: Unknown";
|
|
$("addStoppedCombobox").value = "default";
|
|
$("contentLayoutCombobox").value = "Default";
|
|
rssDownloaderFeedSelectionTable.clear();
|
|
rssDownloaderArticlesTable.clear();
|
|
|
|
$("mustContainText").title = "";
|
|
$("mustNotContainText").title = "";
|
|
$("episodeFilterText").title = "";
|
|
}
|
|
else {
|
|
// enable all
|
|
$("saveButton").disabled = false;
|
|
$("useRegEx").disabled = false;
|
|
$("mustContainText").disabled = false;
|
|
$("mustNotContainText").disabled = false;
|
|
$("episodeFilterText").disabled = false;
|
|
$("useSmartFilter").disabled = false;
|
|
$("assignCategoryCombobox").disabled = false;
|
|
$("ruleAddTags").disabled = false;
|
|
$("savetoDifferentDir").disabled = false;
|
|
$("ignoreDaysValue").disabled = false;
|
|
$("addStoppedCombobox").disabled = false;
|
|
$("contentLayoutCombobox").disabled = false;
|
|
|
|
// load rule settings
|
|
$("useRegEx").checked = rulesList[ruleName].useRegex;
|
|
$("mustContainText").value = rulesList[ruleName].mustContain;
|
|
$("mustNotContainText").value = rulesList[ruleName].mustNotContain;
|
|
$("episodeFilterText").value = rulesList[ruleName].episodeFilter;
|
|
$("useSmartFilter").checked = rulesList[ruleName].smartFilter;
|
|
|
|
$("assignCategoryCombobox").value = rulesList[ruleName].torrentParams.category ? rulesList[ruleName].torrentParams.category : "default";
|
|
$("ruleAddTags").value = rulesList[ruleName].torrentParams.tags.join(",");
|
|
$("savetoDifferentDir").checked = rulesList[ruleName].torrentParams.save_path !== "";
|
|
$("saveToText").disabled = !$("savetoDifferentDir").checked;
|
|
$("saveToText").value = rulesList[ruleName].torrentParams.save_path;
|
|
$("ignoreDaysValue").value = rulesList[ruleName].ignoreDays;
|
|
|
|
// calculate days since last match
|
|
if (rulesList[ruleName].lastMatch !== "") {
|
|
const timeDiffInMs = new Date().getTime() - new Date(rulesList[ruleName].lastMatch).getTime();
|
|
const daysAgo = Math.floor(timeDiffInMs / (1000 * 60 * 60 * 24)).toString();
|
|
$("lastMatchText").textContent = " Last Match: %1 days ago".replace("%1", daysAgo);
|
|
}
|
|
else {
|
|
$("lastMatchText").textContent = "Last Match: Unknown";
|
|
}
|
|
|
|
if ((rulesList[ruleName].torrentParams.stopped === undefined) || (rulesList[ruleName].torrentParams.stopped === null))
|
|
$("addStoppedCombobox").value = "default";
|
|
else
|
|
$("addStoppedCombobox").value = rulesList[ruleName].torrentParams.stopped ? "always" : "never";
|
|
|
|
if ((rulesList[ruleName].torrentParams.content_layout === undefined) || (rulesList[ruleName].torrentParams.content_layout === null))
|
|
$("contentLayoutCombobox").value = "Default";
|
|
else
|
|
$("contentLayoutCombobox").value = rulesList[ruleName].torrentParams.content_layout;
|
|
|
|
setElementTitles();
|
|
|
|
rssDownloaderFeedSelectionTable.clear();
|
|
let rowCount = 0;
|
|
feedList.forEach((feed) => {
|
|
rssDownloaderFeedSelectionTable.updateRowData({
|
|
rowId: rowCount++,
|
|
checked: rulesList[ruleName].affectedFeeds.contains(feed.url),
|
|
name: feed.name,
|
|
url: feed.url
|
|
});
|
|
});
|
|
rssDownloaderFeedSelectionTable.updateTable(false);
|
|
updateMatchingArticles(ruleName);
|
|
}
|
|
};
|
|
|
|
const setElementTitles = () => {
|
|
let mainPart;
|
|
if ($("useRegEx").checked) {
|
|
mainPart = "Regex mode: use Perl-compatible regular expressions\n\n";
|
|
}
|
|
else {
|
|
mainPart = "Wildcard mode: you can use\n\n"
|
|
+ " ● ? to match any single character\n"
|
|
+ " ● * to match zero or more of any characters\n"
|
|
+ " ● Whitespaces count as AND operators (all words, any order)\n"
|
|
+ " ● | is used as OR operator\n\n"
|
|
+ "If word order is important use * instead of whitespace.\n\n";
|
|
}
|
|
const secondPart = "An expression with an empty %1 clause (e.g. %2)"
|
|
.replace("%1", "|").replace("%2", "expr|");
|
|
|
|
$("mustContainText").title = mainPart + secondPart + " will match all articles.";
|
|
$("mustNotContainText").title = mainPart + secondPart + " will exclude all articles.";
|
|
|
|
let episodeFilterTitle = "Matches articles based on episode filter.\n\n"
|
|
+ "Example: "
|
|
+ "1x2;8-15;5;30-;"
|
|
+ " will match 2, 5, 8 through 15, 30 and onward episodes of season one\n\n"
|
|
+ "Episode filter rules: \n\n"
|
|
+ " ● Season number is a mandatory non-zero value\n"
|
|
+ " ● Episode number is a mandatory positive value\n"
|
|
+ " ● Filter must end with semicolon\n"
|
|
+ " ● Three range types for episodes are supported: \n"
|
|
+ " ● Single number: <b>1x25;</b> matches episode 25 of season one\n"
|
|
+ " ● Normal range: <b>1x25-40;</b> matches episodes 25 through 40 of season one\n"
|
|
+ " ● Infinite range: <b>1x25-;</b> matches episodes 25 and upward of season one, and all episodes of later seasons";
|
|
|
|
episodeFilterTitle = episodeFilterTitle.replace(/<b>/g, "").replace(/<\/b>/g, "");
|
|
$("episodeFilterText").title = episodeFilterTitle;
|
|
};
|
|
|
|
return exports();
|
|
})();
|
|
Object.freeze(window.qBittorrent.RssDownloader);
|
|
|
|
window.qBittorrent.RssDownloader.setup();
|
|
</script>
|