Use qbittorrentapi, refactor qbth

This commit is contained in:
Jeffrey Serio 2024-11-05 15:46:06 -06:00
parent 7e09519e62
commit ae478e4675
7 changed files with 270 additions and 76 deletions

4
.envrc
View File

@ -1 +1,3 @@
use nix source_url "https://raw.githubusercontent.com/cachix/devenv/82c0147677e510b247d8b9165c54f73d32dfd899/direnvrc" "sha256-7u4iDd1nZpxL4tCzmPG0dQgC5V+/44Ba+tHkPob1v2k="
use devenv

9
.gitignore vendored
View File

@ -1 +1,10 @@
.direnv .direnv
# Devenv
.devenv*
devenv.local.nix
# direnv
.direnv
# pre-commit
.pre-commit-config.yaml

116
devenv.lock Normal file
View File

@ -0,0 +1,116 @@
{
"nodes": {
"devenv": {
"locked": {
"dir": "src/modules",
"lastModified": 1730745597,
"owner": "cachix",
"repo": "devenv",
"rev": "7cfc04e544e67adf803c3634b53a911c670e046e",
"type": "github"
},
"original": {
"dir": "src/modules",
"owner": "cachix",
"repo": "devenv",
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1696426674,
"owner": "edolstra",
"repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"pre-commit-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1716977621,
"owner": "cachix",
"repo": "devenv-nixpkgs",
"rev": "4267e705586473d3e5c8d50299e71503f16a6fb6",
"type": "github"
},
"original": {
"owner": "cachix",
"ref": "rolling",
"repo": "devenv-nixpkgs",
"type": "github"
}
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1730741070,
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "d063c1dd113c91ab27959ba540c0d9753409edf3",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-24.05",
"repo": "nixpkgs",
"type": "github"
}
},
"pre-commit-hooks": {
"inputs": {
"flake-compat": "flake-compat",
"gitignore": "gitignore",
"nixpkgs": [
"nixpkgs"
],
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
"lastModified": 1730814269,
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"rev": "d70155fdc00df4628446352fc58adc640cd705c2",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"type": "github"
}
},
"root": {
"inputs": {
"devenv": "devenv",
"nixpkgs": "nixpkgs",
"pre-commit-hooks": "pre-commit-hooks"
}
}
},
"root": "root",
"version": 7
}

34
devenv.nix Normal file
View File

@ -0,0 +1,34 @@
{ pkgs, lib, config, inputs, ... }:
{
# https://devenv.sh/basics/
env.GREET = "devenv";
# https://devenv.sh/packages/
packages = [
pkgs.git
pkgs.python312Packages.beautifulsoup4
pkgs.python312Packages.black
pkgs.python312Packages.bpython
pkgs.python312Packages.docopt
pkgs.python312Packages.isort
pkgs.python312Packages.pandas
pkgs.python312Packages.pytest
pkgs.python312Packages.qbittorrent-api
pkgs.python312Packages.requests
pkgs.python312Packages.resend
pkgs.python312Packages.rich
pkgs.python312Packages.tabulate
pkgs.pyright
pkgs.shellcheck
];
# https://devenv.sh/languages/
# languages.rust.enable = true;
languages.python.enable = true;
enterShell = ''
hello
git --version
'';
}

15
devenv.yaml Normal file
View File

@ -0,0 +1,15 @@
# yaml-language-server: $schema=https://devenv.sh/devenv.schema.json
inputs:
nixpkgs:
url: github:cachix/devenv-nixpkgs/rolling
# If you're using non-OSS software, you can set allowUnfree to true.
# allowUnfree: true
# If you're willing to use a package that's vulnerable
# permittedInsecurePackages:
# - "openssl-1.1.1w"
# If you have more than one devenv you can merge them
#imports:
# - ./backend

124
qbth.py
View File

@ -17,15 +17,25 @@ Options:
import json import json
import os import os
import subprocess import subprocess
from shutil import which
import qbittorrentapi
import requests import requests
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from docopt import docopt from docopt import docopt
from qbittorrent import Client
args = docopt(__doc__) args = docopt(__doc__)
qb = Client(args["HOSTNAME"]) conn_info = dict(
qb.login(username=args["USERNAME"], password=args["PASSWORD"]) host=args["HOSTNAME"],
username=args["USERNAME"],
password=args["PASSWORD"],
)
qbt_client = qbittorrentapi.Client(**conn_info)
try:
qbt_client.auth_log_in()
except qbittorrentapi.LoginFailed as e:
print(e)
def add_torrents(urls: list[str]): def add_torrents(urls: list[str]):
@ -35,9 +45,16 @@ def add_torrents(urls: list[str]):
Params: Params:
urls: list of strings that are URLs. urls: list of strings that are URLs.
""" """
for url in urls: with qbittorrentapi.Client(**conn_info) as qbt_client:
qb.download_from_link(url, category="distro") for url in urls:
print(f"Added {os.path.basename(url)}") response = requests.get(url)
if response.status_code == 200:
if qbt_client.torrents_add(url, category="distro") != "Ok.":
raise Exception("Failed to add torrent: " + os.path.basename(url))
else:
print(f"Added {os.path.basename(url)}")
else:
print(f"{response.status_code}: {url}")
def add_torrents_from_html(webpage_url: str, torrent_substring: str): def add_torrents_from_html(webpage_url: str, torrent_substring: str):
@ -52,11 +69,21 @@ def add_torrents_from_html(webpage_url: str, torrent_substring: str):
""" """
reqs = requests.get(webpage_url, timeout=60) reqs = requests.get(webpage_url, timeout=60)
soup = BeautifulSoup(reqs.text, "html.parser") soup = BeautifulSoup(reqs.text, "html.parser")
for link in soup.find_all("a"): with qbittorrentapi.Client(**conn_info) as qbt_client:
if torrent_substring in link.get("href"): for link in soup.find_all("a"):
url = f"{webpage_url}/{link.get('href')}" if torrent_substring in link.get("href"):
qb.download_from_link(url, category="distro") url = f"{webpage_url}/{link.get('href')}"
print(f"Added {link.get('href')}") response = requests.get(url)
if response.status_code == 200:
if qbt_client.torrents_add(url, category="distro") != "Ok.":
raise Exception(
"Failed to add torrent: " + os.path.basename(url)
)
else:
print(f"Added {os.path.basename(url)}")
else:
print(f"{response.status_code}: {url}")
def remove_torrents(distro_substring: str): def remove_torrents(distro_substring: str):
@ -69,10 +96,13 @@ def remove_torrents(distro_substring: str):
distro_substring: a string that is a substring of the distro distro_substring: a string that is a substring of the distro
torrent's file name. torrent's file name.
""" """
for torrent in qb.torrents(): with qbittorrentapi.Client(**conn_info) as qbt_client:
if distro_substring in torrent.get("name"): # type: ignore for torrent in qbt_client.torrents_info():
qb.delete_permanently(torrent.get("hash")) # type: ignore if distro_substring in torrent.name:
print(f"Removed {torrent.get('name')}") # type: ignore qbt_client.torrents_delete(
torrent_hashes=torrent.hash, delete_files=True
)
print(f"Removed {torrent.name}")
def add_almalinux(rel_ver: str): def add_almalinux(rel_ver: str):
@ -142,8 +172,7 @@ def add_devuan(rel_ver: str):
relver: the Devuan release version. relver: the Devuan release version.
""" """
url = f"https://files.devuan.org/devuan_{rel_ver}.torrent" url = f"https://files.devuan.org/devuan_{rel_ver}.torrent"
qb.download_from_link(url, category="distro") add_torrents([url])
print(f"Added {os.path.basename(url)}")
def remove_devuan(rel_ver: str): def remove_devuan(rel_ver: str):
@ -175,11 +204,13 @@ def remove_fedora(rel_ver: str):
Params: Params:
relver: the Fedora release version. relver: the Fedora release version.
""" """
torrents = qb.torrents() with qbittorrentapi.Client(**conn_info) as qbt_client:
for torrent in torrents: for torrent in qbt_client.torrents_info():
if torrent.get("name").startswith("Fedora") and torrent.get("name").endswith(rel_ver): # type: ignore if torrent.name.startswith("Fedora") and torrent.name.endswith(rel_ver):
qb.delete_permanently(torrent.get("hash")) # type: ignore qbt_client.torrents_delete(
print(f"Removed {torrent.get('name')}") # type: ignore torrent_hashes=torrent.hash, delete_files=True
)
print(f"Removed {torrent.name}")
def add_freebsd(rel_ver: str): def add_freebsd(rel_ver: str):
@ -194,10 +225,13 @@ def add_freebsd(rel_ver: str):
reqs = requests.get(url, timeout=60) reqs = requests.get(url, timeout=60)
data = reqs.text.split("\n") data = reqs.text.split("\n")
for line in data: with qbittorrentapi.Client(**conn_info) as qbt_client:
if line.startswith("magnet:"): for line in data:
qb.download_from_link(line, category="distro") if line.startswith("magnet:"):
print(f"Added {line.split('=')[2]}") if qbt_client.torrents_add(line) != "Ok.":
raise Exception("Failed to add torrent: " + line.split("=")[2])
print(f"Added {line.split('=')[2]}")
def remove_freebsd(rel_ver: str): def remove_freebsd(rel_ver: str):
@ -266,9 +300,25 @@ def add_nixos():
url = "https://api.github.com/repos/AnimMouse/NixOS-ISO-Torrents/releases/latest" url = "https://api.github.com/repos/AnimMouse/NixOS-ISO-Torrents/releases/latest"
reqs = requests.get(url, timeout=60) reqs = requests.get(url, timeout=60)
json_data = json.loads(reqs.text) json_data = json.loads(reqs.text)
for item in json_data["assets"]:
qb.download_from_link(item["browser_download_url"], category="distro") with qbittorrentapi.Client(**conn_info) as qbt_client:
print(f"Added {os.path.basename(item['browser_download_url'])}") for item in json_data["assets"]:
response = requests.get(item["browser_download_url"])
if response.status_code == 200:
if (
qbt_client.torrents_add(
item["browser_download_url"], category="distro"
)
!= "Ok."
):
raise Exception(
"Failed to add torrent: "
+ os.path.basename(item["browser_download_url"])
)
else:
print(f"Added {os.path.basename(item['browser_download_url'])}")
else:
print(f"{response.status_code}: {item['browser_download_url']}")
def remove_nixos(): def remove_nixos():
@ -289,8 +339,16 @@ def add_qubes(rel_ver: str):
relver: the QubesOS release version. relver: the QubesOS release version.
""" """
url = f"https://mirrors.edge.kernel.org/qubes/iso/Qubes-R{rel_ver}-x86_64.torrent" url = f"https://mirrors.edge.kernel.org/qubes/iso/Qubes-R{rel_ver}-x86_64.torrent"
qb.download_from_link(url, category="distro")
print(f"Added {os.path.basename(url)}") response = requests.get(url)
if response.status_code == 200:
with qbittorrentapi.Client(**conn_info) as qbt_client:
if qbt_client.torrents_add(url, category="distro") != "Ok.":
raise Exception("Failed to add torrent: " + os.path.basename(url))
else:
print(f"Added {os.path.basename(url)}")
else:
print(f"{response.status_code}: {url}")
def remove_qubes(rel_ver: str): def remove_qubes(rel_ver: str):
@ -356,6 +414,10 @@ def remove_tails(rel_ver: str):
if __name__ == "__main__": if __name__ == "__main__":
# Check if gum is installed.
if which("gum") is None:
exit("Please install gum first. https://github.com/charmbracelet/gum")
# Run the gum program in a subprocess to allow easy selecting of distro # Run the gum program in a subprocess to allow easy selecting of distro
# torrents. # torrents.
distro_selection = subprocess.run( distro_selection = subprocess.run(

View File

@ -1,44 +0,0 @@
with import <nixpkgs> { };
let
python-qbittorrent = pkgs.python312Packages.buildPythonPackage rec {
name = "python-qbittorrent-${version}";
version = "0.4.3";
src = pkgs.fetchurl {
url = "https://files.pythonhosted.org/packages/86/25/a5ad35ad229c8016a8c98327495e649cb795be2fda63f8cace6c9a739af7/python-qbittorrent-${version}.tar.gz";
sha256 = "4e22cf89890628b054a60aa4bd1161a68c2b0fad48ef0886fa4d325e69d3828a";
};
meta = {
homepage = "https://github.com/v1k45/python-qBittorrent";
description = "Python wrapper for qBittorrent Web API (for versions above v3.1.x)";
license = lib.licenses.mit;
maintainers = with maintainers; [ v1k45 ];
};
nativeBuildInputs = with pkgs.python312Packages; [
pip
requests
];
};
in
mkShell {
buildInputs = with pkgs; [
python312Packages.beautifulsoup4
python312Packages.black
python312Packages.bpython
python312Packages.docopt
python312Packages.isort
python312Packages.pandas
python312Packages.pytest
python312Packages.requests
python312Packages.resend
python312Packages.rich
python312Packages.tabulate
pyright
python-qbittorrent
shellcheck
];
}