mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2026-01-08 16:42:30 -06:00
Compare commits
2 Commits
5edaf2cf10
...
c075097acd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c075097acd | ||
|
|
b0148ef36c |
43
.github/workflows/ci_python.yaml
vendored
43
.github/workflows/ci_python.yaml
vendored
@@ -59,44 +59,25 @@ jobs:
|
|||||||
python-version: '3.9'
|
python-version: '3.9'
|
||||||
|
|
||||||
- name: Install tools (search engine)
|
- name: Install tools (search engine)
|
||||||
run: pip install bandit isort mypy pycodestyle pyflakes pyright
|
working-directory: src/searchengine/nova3
|
||||||
|
|
||||||
- name: Gather files (search engine)
|
|
||||||
run: |
|
run: |
|
||||||
export "PY_FILES=$(find . -type f -name '*.py' -path '*searchengine*' ! -name 'socks.py' -printf '%p ')"
|
pip install uv
|
||||||
echo $PY_FILES
|
uv sync
|
||||||
echo "PY_FILES=$PY_FILES" >> "$GITHUB_ENV"
|
|
||||||
|
|
||||||
- name: Check typings (search engine)
|
- name: Check typings (search engine)
|
||||||
run: |
|
working-directory: src/searchengine/nova3
|
||||||
curl \
|
run: uv run just check
|
||||||
-L \
|
|
||||||
-o src/searchengine/nova3/socks.pyi "https://github.com/python/typeshed/raw/refs/heads/main/stubs/PySocks/socks.pyi"
|
|
||||||
MYPYPATH="src/searchengine/nova3" \
|
|
||||||
mypy \
|
|
||||||
--explicit-package-bases \
|
|
||||||
--strict \
|
|
||||||
$PY_FILES
|
|
||||||
pyright \
|
|
||||||
$PY_FILES
|
|
||||||
|
|
||||||
- name: Lint code (search engine)
|
- name: Lint code (search engine)
|
||||||
run: |
|
working-directory: src/searchengine/nova3
|
||||||
pyflakes $PY_FILES
|
run: uv run just lint
|
||||||
bandit --skip B110,B310,B314,B405 $PY_FILES
|
|
||||||
|
|
||||||
- name: Format code (search engine)
|
- name: Format code (search engine)
|
||||||
|
working-directory: src/searchengine/nova3
|
||||||
run: |
|
run: |
|
||||||
pycodestyle \
|
uv run just format
|
||||||
--ignore=E265,E402 \
|
git diff --exit-code
|
||||||
--max-line-length=1000 \
|
|
||||||
--statistics \
|
|
||||||
$PY_FILES
|
|
||||||
isort \
|
|
||||||
--check \
|
|
||||||
--diff \
|
|
||||||
$PY_FILES
|
|
||||||
|
|
||||||
- name: Build code (search engine)
|
- name: Build code (search engine)
|
||||||
run: |
|
working-directory: src/searchengine/nova3
|
||||||
python -m compileall $PY_FILES
|
run: uv run just build
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,4 +1,5 @@
|
|||||||
.vscode/
|
.vscode/
|
||||||
|
|
||||||
src/gui/geoip/GeoIP.dat
|
src/gui/geoip/GeoIP.dat
|
||||||
src/gui/geoip/GeoIP.dat.gz
|
src/gui/geoip/GeoIP.dat.gz
|
||||||
src/qbittorrent
|
src/qbittorrent
|
||||||
@@ -10,7 +11,6 @@ CMakeLists.txt.user*
|
|||||||
qbittorrent.pro.user*
|
qbittorrent.pro.user*
|
||||||
conf.pri
|
conf.pri
|
||||||
Makefile*
|
Makefile*
|
||||||
*.pyc
|
|
||||||
*.log
|
*.log
|
||||||
|
|
||||||
# Compiled object files
|
# Compiled object files
|
||||||
|
|||||||
4
src/searchengine/.gitignore
vendored
Normal file
4
src/searchengine/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
*.egg-info
|
||||||
|
*.lock
|
||||||
|
*.pyc
|
||||||
|
*.pyi
|
||||||
46
src/searchengine/nova3/README.md
Normal file
46
src/searchengine/nova3/README.md
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
nova3 Engine
|
||||||
|
===
|
||||||
|
|
||||||
|
## Development Workflow
|
||||||
|
|
||||||
|
0. Prerequisite
|
||||||
|
|
||||||
|
* A Linux-like environment
|
||||||
|
* [Python](https://www.python.org/) installed
|
||||||
|
* [uv](https://docs.astral.sh/uv/) installed
|
||||||
|
|
||||||
|
1. Setup development environment
|
||||||
|
|
||||||
|
1. Setup virtual environment and dependencies
|
||||||
|
```shell
|
||||||
|
uv sync
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Activate virtual environment
|
||||||
|
```shell
|
||||||
|
source .venv/bin/activate
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Run type check
|
||||||
|
|
||||||
|
```shell
|
||||||
|
just check
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Run static analyzer
|
||||||
|
|
||||||
|
```shell
|
||||||
|
just lint
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Apply formatting
|
||||||
|
|
||||||
|
```shell
|
||||||
|
just format
|
||||||
|
```
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
* [How to write a search plugin](https://github.com/qbittorrent/search-plugins/wiki/How-to-write-a-search-plugin)
|
||||||
|
* [just - Command runner](https://just.systems/man/en/)
|
||||||
|
* [uv - Python package and project manager](https://docs.astral.sh/uv/)
|
||||||
54
src/searchengine/nova3/justfile
Normal file
54
src/searchengine/nova3/justfile
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
PY_FILES := `find . -maxdepth 1 -type f -name '*.py' ! -name 'socks.py' -printf '%P '`
|
||||||
|
|
||||||
|
# Show available recipes to run
|
||||||
|
default:
|
||||||
|
just --list
|
||||||
|
|
||||||
|
# Run type check
|
||||||
|
check files=PY_FILES: fetch_aux
|
||||||
|
mypy \
|
||||||
|
{{ files }}
|
||||||
|
pyright \
|
||||||
|
{{ files }}
|
||||||
|
|
||||||
|
# Byte-compile files
|
||||||
|
build files=PY_FILES:
|
||||||
|
python \
|
||||||
|
-m compileall \
|
||||||
|
{{ files }}
|
||||||
|
|
||||||
|
# Fetch auxiliary files
|
||||||
|
[private]
|
||||||
|
fetch_aux:
|
||||||
|
#!/usr/bin/sh
|
||||||
|
if [ ! -f 'socks.pyi' ]; then
|
||||||
|
curl -L -o socks.pyi "https://github.com/python/typeshed/raw/refs/heads/main/stubs/PySocks/socks.pyi"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Apply formatting
|
||||||
|
format files=PY_FILES:
|
||||||
|
pycodestyle \
|
||||||
|
--ignore=E265,E402 \
|
||||||
|
--max-line-length=1000 \
|
||||||
|
--statistics \
|
||||||
|
{{ files }}
|
||||||
|
isort \
|
||||||
|
--line-length 1000 \
|
||||||
|
{{ files }}
|
||||||
|
just \
|
||||||
|
--fmt \
|
||||||
|
--unstable
|
||||||
|
|
||||||
|
# Run static analyzer
|
||||||
|
lint files=PY_FILES:
|
||||||
|
pyflakes \
|
||||||
|
{{ files }}
|
||||||
|
bandit \
|
||||||
|
--skip B110,B310,B314,B405 \
|
||||||
|
{{ files }}
|
||||||
|
|
||||||
|
# Run tests
|
||||||
|
test files='tests/*.py': fetch_aux
|
||||||
|
pytest \
|
||||||
|
--showlocals \
|
||||||
|
{{ files }}
|
||||||
25
src/searchengine/nova3/pyproject.toml
Normal file
25
src/searchengine/nova3/pyproject.toml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
[project]
|
||||||
|
name = "qBittorrent-search-engine"
|
||||||
|
description = "Search engine for qBittorrent search feature"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.9"
|
||||||
|
dynamic = ["version"]
|
||||||
|
|
||||||
|
[dependency-groups]
|
||||||
|
dev = [
|
||||||
|
"bandit",
|
||||||
|
"isort",
|
||||||
|
"mypy",
|
||||||
|
"pycodestyle",
|
||||||
|
"pyflakes",
|
||||||
|
"pyright",
|
||||||
|
"pytest",
|
||||||
|
"rust-just",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.mypy]
|
||||||
|
explicit_package_bases = true
|
||||||
|
strict = true
|
||||||
|
|
||||||
|
[tool.setuptools.packages.find]
|
||||||
|
where = ["./"]
|
||||||
@@ -36,6 +36,7 @@ export default [
|
|||||||
"curly": ["error", "multi-or-nest", "consistent"],
|
"curly": ["error", "multi-or-nest", "consistent"],
|
||||||
"eqeqeq": "error",
|
"eqeqeq": "error",
|
||||||
"guard-for-in": "error",
|
"guard-for-in": "error",
|
||||||
|
"no-implicit-coercion": "error",
|
||||||
"no-undef": "off",
|
"no-undef": "off",
|
||||||
"no-unused-vars": "off",
|
"no-unused-vars": "off",
|
||||||
"no-var": "error",
|
"no-var": "error",
|
||||||
@@ -73,7 +74,10 @@ export default [
|
|||||||
"Unicorn/no-array-for-each": "error",
|
"Unicorn/no-array-for-each": "error",
|
||||||
"Unicorn/no-for-loop": "error",
|
"Unicorn/no-for-loop": "error",
|
||||||
"Unicorn/no-zero-fractions": "error",
|
"Unicorn/no-zero-fractions": "error",
|
||||||
|
"Unicorn/prefer-classlist-toggle": "error",
|
||||||
|
"Unicorn/prefer-native-coercion-functions": "error",
|
||||||
"Unicorn/prefer-number-properties": "error",
|
"Unicorn/prefer-number-properties": "error",
|
||||||
|
"Unicorn/prefer-single-call": "error",
|
||||||
"Unicorn/switch-case-braces": ["error", "avoid"]
|
"Unicorn/switch-case-braces": ["error", "avoid"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -710,10 +710,8 @@ window.qBittorrent.DynamicTable ??= (() => {
|
|||||||
colElem.classList.toggle("reverse", isReverse);
|
colElem.classList.toggle("reverse", isReverse);
|
||||||
}
|
}
|
||||||
const oldColElem = getCol(this.dynamicTableFixedHeaderDivId, oldColumn);
|
const oldColElem = getCol(this.dynamicTableFixedHeaderDivId, oldColumn);
|
||||||
if (oldColElem !== null) {
|
if (oldColElem !== null)
|
||||||
oldColElem.classList.remove("sorted");
|
oldColElem.classList.remove("sorted", "reverse");
|
||||||
oldColElem.classList.remove("reverse");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getSelectedRowId() {
|
getSelectedRowId() {
|
||||||
|
|||||||
@@ -730,9 +730,7 @@ window.qBittorrent.Search ??= (() => {
|
|||||||
for (const plugin of responseJSON)
|
for (const plugin of responseJSON)
|
||||||
searchPlugins.push(plugin);
|
searchPlugins.push(plugin);
|
||||||
|
|
||||||
const pluginOptions = [];
|
const pluginOptions = [createOption("QBT_TR(Only enabled)QBT_TR[CONTEXT=SearchEngineWidget]", "enabled"), createOption("QBT_TR(All plugins)QBT_TR[CONTEXT=SearchEngineWidget]", "all")];
|
||||||
pluginOptions.push(createOption("QBT_TR(Only enabled)QBT_TR[CONTEXT=SearchEngineWidget]", "enabled"));
|
|
||||||
pluginOptions.push(createOption("QBT_TR(All plugins)QBT_TR[CONTEXT=SearchEngineWidget]", "all"));
|
|
||||||
|
|
||||||
const searchPluginsEmpty = (searchPlugins.length === 0);
|
const searchPluginsEmpty = (searchPlugins.length === 0);
|
||||||
if (!searchPluginsEmpty) {
|
if (!searchPluginsEmpty) {
|
||||||
|
|||||||
@@ -17,12 +17,8 @@
|
|||||||
MochaUI.initializeTabs("aboutTabs");
|
MochaUI.initializeTabs("aboutTabs");
|
||||||
|
|
||||||
const showContent = (element) => {
|
const showContent = (element) => {
|
||||||
for (const content of document.querySelectorAll(".aboutTabContent")) {
|
for (const content of document.querySelectorAll(".aboutTabContent"))
|
||||||
if (content === element)
|
content.classList.toggle("invisible", (content !== element));
|
||||||
content.classList.remove("invisible");
|
|
||||||
else
|
|
||||||
content.classList.add("invisible");
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
document.getElementById("aboutAboutLink").addEventListener("click", (event) => {
|
document.getElementById("aboutAboutLink").addEventListener("click", (event) => {
|
||||||
|
|||||||
@@ -36,12 +36,8 @@
|
|||||||
MochaUI.initializeTabs("preferencesTabs");
|
MochaUI.initializeTabs("preferencesTabs");
|
||||||
|
|
||||||
const showTab = (element) => {
|
const showTab = (element) => {
|
||||||
for (const tab of document.querySelectorAll(".PrefTab")) {
|
for (const tab of document.querySelectorAll(".PrefTab"))
|
||||||
if (tab === element)
|
tab.classList.toggle("invisible", (tab !== element));
|
||||||
tab.classList.remove("invisible");
|
|
||||||
else
|
|
||||||
tab.classList.add("invisible");
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
document.getElementById("PrefBehaviorLink").addEventListener("click", (e) => {
|
document.getElementById("PrefBehaviorLink").addEventListener("click", (e) => {
|
||||||
|
|||||||
@@ -620,8 +620,8 @@
|
|||||||
|
|
||||||
if (articlesDiffer) {
|
if (articlesDiffer) {
|
||||||
// update unread count
|
// update unread count
|
||||||
const oldUnread = feedData[r.uid].map((art) => !art.isRead).filter((v) => v).length;
|
const oldUnread = feedData[r.uid].map((art) => !art.isRead).filter(Boolean).length;
|
||||||
const newUnread = r.articles.map((art) => !art.isRead).filter((v) => v).length;
|
const newUnread = r.articles.map((art) => !art.isRead).filter(Boolean).length;
|
||||||
const unreadDifference = newUnread - oldUnread;
|
const unreadDifference = newUnread - oldUnread;
|
||||||
|
|
||||||
// find all parents (and self) and add unread difference
|
// find all parents (and self) and add unread difference
|
||||||
@@ -733,7 +733,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
// calculate number of unread
|
// calculate number of unread
|
||||||
const numberOfUnread = dataEntry.articles.map((art) => !art.isRead).filter((v) => v).length;
|
const numberOfUnread = dataEntry.articles.map((art) => !art.isRead).filter(Boolean).length;
|
||||||
// find all items that contain this rss feed and add unread count
|
// find all items that contain this rss feed and add unread count
|
||||||
for (const row of rssFeedTable.getRowValues()) {
|
for (const row of rssFeedTable.getRowValues()) {
|
||||||
if (dataEntry.fullName.slice(0, row.full_data.dataPath.length) === row.full_data.dataPath)
|
if (dataEntry.fullName.slice(0, row.full_data.dataPath.length) === row.full_data.dataPath)
|
||||||
|
|||||||
Reference in New Issue
Block a user