Source code for mendevi.download.transmission
"""Interacted with transmission-daemon."""
import logging
import pathlib
import subprocess
import time
import orjson
import tqdm
from context_verbose import Printer
[docs]
def add_torrent(
torrent: pathlib.Path, download_dir: pathlib.Path | None = None,
) -> pathlib.Path:
"""Add the torrent to transmission.
Parameters
----------
torrent : pathlib.Path
The torrent file to be added.
download_dir : pathlib.Path, default=~/.cache/mendevi/
The download_dir in which to download the torrent content.
Returns
-------
file : pathlib.Path
Return the downloaded file path, related to the torrent.
"""
# verifications
assert isinstance(torrent, pathlib.Path), torrent.__class__.__name__
assert torrent.is_file(), torrent
if download_dir is None:
download_dir = pathlib.Path.home() / ".cache" / "mendevi"
download_dir.mkdir(mode=0o777, parents=True, exist_ok=True)
else:
assert isinstance(download_dir, pathlib.Path), download_dir.__class__.__name__
assert download_dir.is_dir(download_dir)
# shortcut
final_file = download_dir / torrent.stem
if (final_file).exists():
return final_file
# add torrent
test_transmission_is_installed() # after shortcut
with Printer(f"Add {torrent.name!r} to transmission...", color="green") as prt:
try:
out = subprocess.run(
[
"transmission-remote",
"--add", str(torrent),
"--download-dir", str(download_dir),
],
check=True,
capture_output=True,
)
except subprocess.CalledProcessError as err:
msg = "the transmission-daemon package appears to be corrupted"
raise ImportError(msg) from err
prt.print(out.stdout.decode().strip())
# download content
wait_until_finished(torrent.stem)
return final_file
[docs]
def test_transmission_is_installed() -> None:
"""Ensures that transmission-daemon has been installed correctly.
Raises
------
ImportError
If transmission-daemon is not well installed
"""
try:
out = subprocess.run(["transmission-remote", "--version"], check=True, capture_output=True)
except FileNotFoundError as err:
msg = (
"please install transmission-daemon "
"(https://mendevi.readthedocs.io/latest/developer_guide/installation.html#transmission)"
)
raise ImportError(
msg,
) from err
if not (out.stdout or out.stderr):
msg = "the transmission-daemon package appears to be corrupted"
raise logging.getLogger(__name__).warning(msg)
[docs]
def wait_until_finished(name: str) -> None:
"""Display the progress bar of the torrent download."""
with Printer(f"Download {name!r} (transmission-remote -l)...", color="green") as prt:
load = tqdm.tqdm(
dynamic_ncols=True,
leave=True,
smoothing=1e-8,
total=None,
unit="Mo",
)
while True:
out = subprocess.run(
["transmission-remote", "--json", "--list"],
check=True,
capture_output=True,
)
try:
states = orjson.loads(out.stdout)
except orjson.JSONDecodeError as err:
msg = f"the transmission-daemon package appears to be corrupted, {out.stdout}"
raise ImportError(
msg,
) from err
if len(state := [t for t in states["arguments"]["torrents"] if t["name"] == name]) == 0:
msg = f"the torrent {name} is not in the transmission-remote --list"
raise KeyError(msg)
state = state.pop()
if state["errorString"]:
load.set_description(state["errorString"])
else:
load.set_description(f"downloads from {state['peersSendingToUs']} pairs")
load.total = state["sizeWhenDone"]//1_000_000
load.update(load.total - state["leftUntilDone"]//1_000_000 - load.n)
if state["leftUntilDone"] == 0:
load.close()
break
time.sleep(1.0)
prt.print_time()