diff --git a/.envrc b/.envrc index 1d953f4..894571b 100644 --- a/.envrc +++ b/.envrc @@ -1 +1,3 @@ -use nix +source_url "https://raw.githubusercontent.com/cachix/devenv/82c0147677e510b247d8b9165c54f73d32dfd899/direnvrc" "sha256-7u4iDd1nZpxL4tCzmPG0dQgC5V+/44Ba+tHkPob1v2k=" + +use devenv diff --git a/.gitignore b/.gitignore index 92b2793..81f0f67 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,10 @@ .direnv +# Devenv +.devenv* +devenv.local.nix + +# direnv +.direnv + +# pre-commit +.pre-commit-config.yaml diff --git a/devenv.lock b/devenv.lock new file mode 100644 index 0000000..effe809 --- /dev/null +++ b/devenv.lock @@ -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 +} diff --git a/devenv.nix b/devenv.nix new file mode 100644 index 0000000..e0c64a3 --- /dev/null +++ b/devenv.nix @@ -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 + ''; +} diff --git a/devenv.yaml b/devenv.yaml new file mode 100644 index 0000000..116a2ad --- /dev/null +++ b/devenv.yaml @@ -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 diff --git a/qbth.py b/qbth.py index 59025cb..e5ae3db 100755 --- a/qbth.py +++ b/qbth.py @@ -17,15 +17,25 @@ Options: import json import os import subprocess +from shutil import which +import qbittorrentapi import requests from bs4 import BeautifulSoup from docopt import docopt -from qbittorrent import Client args = docopt(__doc__) -qb = Client(args["HOSTNAME"]) -qb.login(username=args["USERNAME"], password=args["PASSWORD"]) +conn_info = dict( + 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]): @@ -35,9 +45,16 @@ def add_torrents(urls: list[str]): Params: urls: list of strings that are URLs. """ - for url in urls: - qb.download_from_link(url, category="distro") - print(f"Added {os.path.basename(url)}") + with qbittorrentapi.Client(**conn_info) as qbt_client: + for url in urls: + 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): @@ -52,11 +69,21 @@ def add_torrents_from_html(webpage_url: str, torrent_substring: str): """ reqs = requests.get(webpage_url, timeout=60) soup = BeautifulSoup(reqs.text, "html.parser") - for link in soup.find_all("a"): - if torrent_substring in link.get("href"): - url = f"{webpage_url}/{link.get('href')}" - qb.download_from_link(url, category="distro") - print(f"Added {link.get('href')}") + with qbittorrentapi.Client(**conn_info) as qbt_client: + for link in soup.find_all("a"): + if torrent_substring in link.get("href"): + url = f"{webpage_url}/{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): @@ -69,10 +96,13 @@ def remove_torrents(distro_substring: str): distro_substring: a string that is a substring of the distro torrent's file name. """ - for torrent in qb.torrents(): - if distro_substring in torrent.get("name"): # type: ignore - qb.delete_permanently(torrent.get("hash")) # type: ignore - print(f"Removed {torrent.get('name')}") # type: ignore + with qbittorrentapi.Client(**conn_info) as qbt_client: + for torrent in qbt_client.torrents_info(): + if distro_substring in torrent.name: + qbt_client.torrents_delete( + torrent_hashes=torrent.hash, delete_files=True + ) + print(f"Removed {torrent.name}") def add_almalinux(rel_ver: str): @@ -142,8 +172,7 @@ def add_devuan(rel_ver: str): relver: the Devuan release version. """ url = f"https://files.devuan.org/devuan_{rel_ver}.torrent" - qb.download_from_link(url, category="distro") - print(f"Added {os.path.basename(url)}") + add_torrents([url]) def remove_devuan(rel_ver: str): @@ -175,11 +204,13 @@ def remove_fedora(rel_ver: str): Params: relver: the Fedora release version. """ - torrents = qb.torrents() - for torrent in torrents: - if torrent.get("name").startswith("Fedora") and torrent.get("name").endswith(rel_ver): # type: ignore - qb.delete_permanently(torrent.get("hash")) # type: ignore - print(f"Removed {torrent.get('name')}") # type: ignore + with qbittorrentapi.Client(**conn_info) as qbt_client: + for torrent in qbt_client.torrents_info(): + if torrent.name.startswith("Fedora") and torrent.name.endswith(rel_ver): + qbt_client.torrents_delete( + torrent_hashes=torrent.hash, delete_files=True + ) + print(f"Removed {torrent.name}") def add_freebsd(rel_ver: str): @@ -194,10 +225,13 @@ def add_freebsd(rel_ver: str): reqs = requests.get(url, timeout=60) data = reqs.text.split("\n") - for line in data: - if line.startswith("magnet:"): - qb.download_from_link(line, category="distro") - print(f"Added {line.split('=')[2]}") + with qbittorrentapi.Client(**conn_info) as qbt_client: + for line in data: + if line.startswith("magnet:"): + 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): @@ -266,9 +300,25 @@ def add_nixos(): url = "https://api.github.com/repos/AnimMouse/NixOS-ISO-Torrents/releases/latest" reqs = requests.get(url, timeout=60) json_data = json.loads(reqs.text) - for item in json_data["assets"]: - qb.download_from_link(item["browser_download_url"], category="distro") - print(f"Added {os.path.basename(item['browser_download_url'])}") + + with qbittorrentapi.Client(**conn_info) as qbt_client: + 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(): @@ -289,8 +339,16 @@ def add_qubes(rel_ver: str): relver: the QubesOS release version. """ 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): @@ -356,6 +414,10 @@ def remove_tails(rel_ver: str): 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 # torrents. distro_selection = subprocess.run( diff --git a/shell.nix b/shell.nix deleted file mode 100644 index 12eed60..0000000 --- a/shell.nix +++ /dev/null @@ -1,44 +0,0 @@ -with import { }; - -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 - ]; -}