mirror of
https://github.com/Carve/qbittorrent-webui-cjratliff.com.git
synced 2025-02-28 15:40:28 +01:00
428 lines
14 KiB
HTML
428 lines
14 KiB
HTML
<style type="text/css">
|
|
#logTopBar {
|
|
margin-top: 1em;
|
|
}
|
|
|
|
#logFilterBar {
|
|
margin: .5em 0;
|
|
}
|
|
|
|
#logFilterBar>label {
|
|
font-weight: bold;
|
|
margin-right: .3em;
|
|
}
|
|
|
|
#logFilterBar>button {
|
|
display: inline-block;
|
|
padding: 4px 15px;
|
|
margin-left: .3em;
|
|
}
|
|
|
|
#logView {
|
|
padding: 0 20px;
|
|
overflow: auto;
|
|
}
|
|
|
|
#logContentView {
|
|
display: block;
|
|
vertical-align: top;
|
|
}
|
|
|
|
#logMessageTableFixedHeaderDiv .dynamicTableHeader,
|
|
#logPeerTableFixedHeaderDiv .dynamicTableHeader {
|
|
cursor: default;
|
|
}
|
|
|
|
#filterTextInput {
|
|
background-image: url("../images/edit-find.svg");
|
|
background-repeat: no-repeat;
|
|
background-position: left;
|
|
background-size: 1.5em;
|
|
padding: 4px 5px 4px 2em;
|
|
margin-left: .3em;
|
|
width: 200px;
|
|
border: 1px solid var(--color-border-default);
|
|
border-radius: 3px;
|
|
}
|
|
|
|
#logFilterSummary {
|
|
overflow: auto;
|
|
margin: 1em 0 .5em;
|
|
}
|
|
|
|
#numFilteredLogs,
|
|
#numTotalLogs {
|
|
font-style: italic;
|
|
}
|
|
|
|
.dynamicTable tbody tr.logTableRowlogNormal {
|
|
color: var(--darkmode-text);
|
|
}
|
|
|
|
.dynamicTable tbody tr.logTableRowlogInfo {
|
|
color: var(--highlight-color--blue);
|
|
}
|
|
|
|
.dynamicTable tbody tr.logTableRowlogWarning {
|
|
color: var(--highlight-color--orange);
|
|
}
|
|
|
|
.dynamicTable tbody tr.logTableRowlogCritical,
|
|
.dynamicTable tbody tr.logTableRowpeerBlocked {
|
|
color: var(--highlight-color--red);
|
|
}
|
|
|
|
.vsb-main>button {
|
|
padding: 4px 12px !important;
|
|
}
|
|
|
|
</style>
|
|
|
|
<div id="logView">
|
|
<div id="logTopBar">
|
|
<div id="logFilterBar">
|
|
<label for="logLevelSelect">Log Levels:</label>
|
|
<select multiple size="1" id="logLevelSelect" class="logLevelSelect" onchange="window.qBittorrent.Log.logLevelChanged()">
|
|
<option value="1">Normal Messages</option>
|
|
<option value="2">Information Messages</option>
|
|
<option value="4">Warning Messages</option>
|
|
<option value="8">Critical Messages</option>
|
|
</select>
|
|
|
|
<input type="text" id="filterTextInput" onkeyup="window.qBittorrent.Log.filterTextChanged()" placeholder="Filter logs" aria-label="Filter logs" autocomplete="off" autocorrect="off" autocapitalize="none">
|
|
<button type="button" title="Clear input" onclick="javascript:document.querySelector('#filterTextInput').value='';window.qBittorrent.Log.filterTextChanged();">Clear</button>
|
|
</div>
|
|
|
|
<div id="logFilterSummary">
|
|
<span>Results (showing <span id="numFilteredLogs">0</span> out of <span id="numTotalLogs">0</span>):</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="logContentView">
|
|
<div id="logMessageView">
|
|
<div id="logMessageTableFixedHeaderDiv" class="dynamicTableFixedHeaderDiv">
|
|
<table class="dynamicTable unselectable" style="position:relative;">
|
|
<thead>
|
|
<tr class="dynamicTableHeader"></tr>
|
|
</thead>
|
|
</table>
|
|
</div>
|
|
<div id="logMessageTableDiv" class="dynamicTableDiv">
|
|
<table class="dynamicTable unselectable">
|
|
<thead>
|
|
<tr class="dynamicTableHeader"></tr>
|
|
</thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<div id="logPeerView" class="invisible">
|
|
<div id="logPeerTableFixedHeaderDiv" class="dynamicTableFixedHeaderDiv">
|
|
<table class="dynamicTable unselectable" style="position:relative;">
|
|
<thead>
|
|
<tr class="dynamicTableHeader"></tr>
|
|
</thead>
|
|
</table>
|
|
</div>
|
|
<div id="logPeerTableDiv" class="dynamicTableDiv">
|
|
<table class="dynamicTable unselectable">
|
|
<thead>
|
|
<tr class="dynamicTableHeader"></tr>
|
|
</thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<ul id="logTableMenu" class="contextMenu">
|
|
<li><a href="#" class="copyLogDataToClipboard"><img src="images/edit-copy.svg" alt="Copy">Copy</a></li>
|
|
<li><a href="#Clear"><img src="images/list-remove.svg" alt="Clear">Clear</a></li>
|
|
</ul>
|
|
|
|
<script>
|
|
"use strict";
|
|
|
|
window.qBittorrent ??= {};
|
|
window.qBittorrent.Log ??= (() => {
|
|
const exports = () => {
|
|
return {
|
|
init: init,
|
|
unload: unload,
|
|
load: load,
|
|
setCurrentTab: setCurrentTab,
|
|
getFilterText: getFilterText,
|
|
getSelectedLevels: getSelectedLevels,
|
|
logLevelChanged: logLevelChanged,
|
|
filterTextChanged: filterTextChanged
|
|
};
|
|
};
|
|
|
|
let currentSelectedTab = "main";
|
|
const tableInfo = {
|
|
main: {
|
|
instance: new window.qBittorrent.DynamicTable.LogMessageTable(),
|
|
progress: false,
|
|
timer: null,
|
|
last_id: -1
|
|
},
|
|
peer: {
|
|
instance: new window.qBittorrent.DynamicTable.LogPeerTable(),
|
|
progress: false,
|
|
timer: null,
|
|
last_id: -1
|
|
}
|
|
};
|
|
|
|
let customSyncLogDataInterval = null;
|
|
let logFilterTimer = -1;
|
|
let inputtedFilterText = "";
|
|
let selectBox;
|
|
let selectedLogLevels = JSON.parse(LocalPreferences.get("qbt_selected_log_levels")) || ["1", "2", "4", "8"];
|
|
|
|
const init = () => {
|
|
$("logLevelSelect").getElements("option").each((x) => {
|
|
if (selectedLogLevels.indexOf(x.value.toString()) !== -1)
|
|
x.selected = true;
|
|
else
|
|
x.selected = false;
|
|
});
|
|
|
|
selectBox = new vanillaSelectBox("#logLevelSelect", {
|
|
maxHeight: 200,
|
|
search: false,
|
|
translations: {
|
|
all: "All",
|
|
item: "item",
|
|
items: "items",
|
|
selectAll: "Select All",
|
|
clearAll: "Clear All",
|
|
},
|
|
placeHolder: "Choose a log level...",
|
|
keepInlineStyles: false
|
|
});
|
|
|
|
const logTableContextMenu = new window.qBittorrent.ContextMenu.ContextMenu({
|
|
targets: ".logTableRow",
|
|
menu: "logTableMenu",
|
|
actions: {
|
|
Clear: () => {
|
|
tableInfo[currentSelectedTab].instance.selectedRowsIds().forEach((rowId) => {
|
|
tableInfo[currentSelectedTab].instance.removeRow(rowId);
|
|
});
|
|
|
|
updateLabelCount();
|
|
}
|
|
},
|
|
offsets: {
|
|
x: -16,
|
|
y: -57
|
|
}
|
|
});
|
|
|
|
tableInfo["main"].instance.setup("logMessageTableDiv", "logMessageTableFixedHeaderDiv", logTableContextMenu);
|
|
tableInfo["peer"].instance.setup("logPeerTableDiv", "logPeerTableFixedHeaderDiv", logTableContextMenu);
|
|
|
|
MUI.Panels.instances.LogPanel.contentEl.style.height = "100%";
|
|
$("logView").style.height = "inherit";
|
|
|
|
load();
|
|
};
|
|
|
|
const unload = () => {
|
|
for (const table in tableInfo) {
|
|
if (!Object.hasOwn(tableInfo, table))
|
|
continue;
|
|
resetTableTimer(table);
|
|
}
|
|
};
|
|
|
|
const load = () => {
|
|
customSyncLogDataInterval = null;
|
|
syncLogWithInterval(100);
|
|
};
|
|
|
|
const resetTableTimer = (curTab) => {
|
|
if (curTab === undefined)
|
|
curTab = currentSelectedTab;
|
|
|
|
clearTimeout(tableInfo[curTab].timer);
|
|
tableInfo[curTab].timer = null;
|
|
};
|
|
|
|
const syncLogWithInterval = (interval) => {
|
|
if (!tableInfo[currentSelectedTab].progress) {
|
|
clearTimeout(tableInfo[currentSelectedTab].timer);
|
|
tableInfo[currentSelectedTab].timer = syncLogData.delay(interval, null, currentSelectedTab);
|
|
}
|
|
};
|
|
|
|
const getFilterText = () => {
|
|
return inputtedFilterText;
|
|
};
|
|
|
|
const getSelectedLevels = () => {
|
|
return selectedLogLevels;
|
|
};
|
|
|
|
const getSyncLogDataInterval = () => {
|
|
return customSyncLogDataInterval ? customSyncLogDataInterval : serverSyncMainDataInterval;
|
|
};
|
|
|
|
const logLevelChanged = () => {
|
|
const value = selectBox.getResult().sort();
|
|
|
|
if (selectedLogLevels !== value) {
|
|
tableInfo[currentSelectedTab].last_id = -1;
|
|
selectedLogLevels = value;
|
|
LocalPreferences.set("qbt_selected_log_levels", JSON.stringify(selectedLogLevels));
|
|
logFilterChanged();
|
|
}
|
|
};
|
|
|
|
const filterTextChanged = () => {
|
|
const value = $("filterTextInput").value.trim();
|
|
if (inputtedFilterText !== value) {
|
|
inputtedFilterText = value;
|
|
logFilterChanged();
|
|
}
|
|
};
|
|
|
|
const logFilterChanged = () => {
|
|
clearTimeout(logFilterTimer);
|
|
logFilterTimer = setTimeout((curTab) => {
|
|
logFilterTimer = -1;
|
|
|
|
tableInfo[curTab].instance.updateTable(false);
|
|
updateLabelCount(curTab);
|
|
}, window.qBittorrent.Misc.FILTER_INPUT_DELAY, currentSelectedTab);
|
|
};
|
|
|
|
const setCurrentTab = (tab) => {
|
|
if (tab === currentSelectedTab)
|
|
return;
|
|
|
|
currentSelectedTab = tab;
|
|
if (currentSelectedTab === "main") {
|
|
selectBox.enable();
|
|
$("logMessageView").removeClass("invisible");
|
|
$("logPeerView").addClass("invisible");
|
|
resetTableTimer("peer");
|
|
}
|
|
else {
|
|
selectBox.disable();
|
|
$("logMessageView").addClass("invisible");
|
|
$("logPeerView").removeClass("invisible");
|
|
resetTableTimer("main");
|
|
}
|
|
|
|
clearTimeout(logFilterTimer);
|
|
logFilterTimer = -1;
|
|
load();
|
|
|
|
if (tableInfo[currentSelectedTab].instance.filterText !== getFilterText())
|
|
tableInfo[currentSelectedTab].instance.updateTable();
|
|
|
|
updateLabelCount();
|
|
};
|
|
|
|
const updateLabelCount = (curTab) => {
|
|
if (curTab === undefined)
|
|
curTab = currentSelectedTab;
|
|
|
|
$("numFilteredLogs").textContent = tableInfo[curTab].instance.filteredLength();
|
|
$("numTotalLogs").textContent = tableInfo[curTab].instance.getRowSize();
|
|
};
|
|
|
|
const syncLogData = (curTab) => {
|
|
if (curTab === undefined)
|
|
curTab = currentSelectedTab;
|
|
|
|
let url;
|
|
if (curTab === "main") {
|
|
url = new URI("api/v2/log/main");
|
|
url.setData({
|
|
normal: selectedLogLevels.indexOf("1") !== -1,
|
|
info: selectedLogLevels.indexOf("2") !== -1,
|
|
warning: selectedLogLevels.indexOf("4") !== -1,
|
|
critical: selectedLogLevels.indexOf("8") !== -1
|
|
});
|
|
}
|
|
else {
|
|
url = new URI("api/v2/log/peers");
|
|
}
|
|
|
|
url.setData("last_known_id", tableInfo[curTab].last_id);
|
|
tableInfo[curTab].progress = true;
|
|
|
|
new Request.JSON({
|
|
url: url,
|
|
method: "get",
|
|
noCache: true,
|
|
onFailure: function(response) {
|
|
const errorDiv = $("error_div");
|
|
if (errorDiv)
|
|
errorDiv.textContent = "qBittorrent client is not reachable";
|
|
tableInfo[curTab].progress = false;
|
|
syncLogWithInterval(10000);
|
|
},
|
|
onSuccess: function(response) {
|
|
$("error_div").textContent = "";
|
|
|
|
if ($("logTabColumn").hasClass("invisible"))
|
|
return;
|
|
|
|
if (response.length > 0) {
|
|
clearTimeout(logFilterTimer);
|
|
logFilterTimer = -1;
|
|
|
|
for (let i = 0; i < response.length; ++i) {
|
|
let row;
|
|
if (curTab === "main") {
|
|
row = {
|
|
rowId: response[i].id,
|
|
message: response[i].message,
|
|
timestamp: response[i].timestamp,
|
|
type: response[i].type,
|
|
};
|
|
}
|
|
else {
|
|
row = {
|
|
rowId: response[i].id,
|
|
ip: response[i].ip,
|
|
timestamp: response[i].timestamp,
|
|
blocked: response[i].blocked,
|
|
reason: response[i].reason,
|
|
};
|
|
}
|
|
tableInfo[curTab].instance.updateRowData(row);
|
|
tableInfo[curTab].last_id = Math.max(response[i].id.toInt(), tableInfo[curTab].last_id);
|
|
}
|
|
|
|
tableInfo[curTab].instance.updateTable();
|
|
updateLabelCount(curTab);
|
|
}
|
|
|
|
tableInfo[curTab].progress = false;
|
|
syncLogWithInterval(getSyncLogDataInterval());
|
|
}
|
|
}).send();
|
|
};
|
|
|
|
new ClipboardJS(".copyLogDataToClipboard", {
|
|
text: function() {
|
|
const msg = [];
|
|
tableInfo[currentSelectedTab].instance.selectedRowsIds().forEach((rowId) => {
|
|
msg.push(tableInfo[currentSelectedTab].instance.getRow(rowId).full_data[(currentSelectedTab === "main") ? "message" : "ip"]);
|
|
});
|
|
|
|
return msg.join("\n");
|
|
}
|
|
});
|
|
|
|
return exports();
|
|
})();
|
|
Object.freeze(window.qBittorrent.Log);
|
|
</script>
|