From 66acf6b421faf44a6f945234076e329ee9964400 Mon Sep 17 00:00:00 2001 From: Vladan Popovic Date: Sun, 27 Oct 2024 01:39:20 +0200 Subject: [PATCH] Add systemd start/stop for transmission.service --- pyproject.toml | 5 +-- requirements-dev.lock | 49 +------------------------- requirements.lock | 31 +---------------- src/torrent_downloader/app.py | 57 +++++++++++++++++++++++++------ src/torrent_downloader/systemd.py | 24 +++++++++++++ templates/index.html | 2 +- templates/transmission.html | 9 +++++ 7 files changed, 85 insertions(+), 92 deletions(-) create mode 100644 src/torrent_downloader/systemd.py create mode 100644 templates/transmission.html diff --git a/pyproject.toml b/pyproject.toml index 90f30b1..6954184 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "torrent-downloader" -version = "0.1.0" -description = "Add your description here" +version = "0.1.1" +description = "Web interface for searching and downloading torrents" authors = [ { name = "Vladan Popovic", email = "vladanovic@gmail.com" } ] @@ -12,6 +12,7 @@ dependencies = [ "fastapi>=0.114.2", "jinja2>=3.1.4", "cinemagoer>=2023.5.1", + "dbus-python>=1.3.2", ] readme = "README.md" requires-python = ">= 3.8" diff --git a/requirements-dev.lock b/requirements-dev.lock index 4cd9105..c73f687 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -11,92 +11,45 @@ -e file:. annotated-types==0.7.0 - # via pydantic anyio==4.4.0 - # via starlette asttokens==2.4.1 - # via stack-data certifi==2024.8.30 - # via requests charset-normalizer==3.3.2 - # via requests cinemagoer==2023.5.1 - # via torrent-downloader click==8.1.7 - # via uvicorn +dbus-python==1.3.2 decorator==5.1.1 - # via ipython executing==2.1.0 - # via stack-data fastapi==0.114.2 - # via torrent-downloader greenlet==3.1.0 - # via sqlalchemy h11==0.14.0 - # via uvicorn idna==3.8 - # via anyio - # via requests ipython==8.27.0 jedi==0.19.1 - # via ipython jinja2==3.1.4 - # via torrent-downloader lxml==5.3.0 - # via cinemagoer - # via tpblite markupsafe==2.1.5 - # via jinja2 matplotlib-inline==0.1.7 - # via ipython parso==0.8.4 - # via jedi pexpect==4.9.0 - # via ipython prompt-toolkit==3.0.47 - # via ipython ptyprocess==0.7.0 - # via pexpect pure-eval==0.2.3 - # via stack-data pydantic==2.8.2 - # via fastapi - # via pydantic-settings pydantic-core==2.20.1 - # via pydantic pydantic-settings==2.4.0 - # via torrent-downloader pygments==2.18.0 - # via ipython python-dotenv==1.0.1 - # via pydantic-settings requests==2.32.3 - # via transmission-rpc six==1.16.0 - # via asttokens sniffio==1.3.1 - # via anyio sqlalchemy==2.0.34 - # via cinemagoer stack-data==0.6.3 - # via ipython starlette==0.38.5 - # via fastapi tpblite==0.8.0 - # via torrent-downloader traitlets==5.14.3 - # via ipython - # via matplotlib-inline transmission-rpc==7.0.11 - # via torrent-downloader typing-extensions==4.12.2 - # via fastapi - # via pydantic - # via pydantic-core - # via sqlalchemy - # via transmission-rpc urllib3==2.2.2 - # via requests uvicorn==0.30.6 wcwidth==0.2.13 - # via prompt-toolkit diff --git a/requirements.lock b/requirements.lock index ce300e2..c89e4a4 100644 --- a/requirements.lock +++ b/requirements.lock @@ -11,55 +11,26 @@ -e file:. annotated-types==0.7.0 - # via pydantic anyio==4.4.0 - # via starlette certifi==2024.8.30 - # via requests charset-normalizer==3.3.2 - # via requests cinemagoer==2023.5.1 - # via torrent-downloader +dbus-python==1.3.2 fastapi==0.114.2 - # via torrent-downloader greenlet==3.1.0 - # via sqlalchemy idna==3.8 - # via anyio - # via requests jinja2==3.1.4 - # via torrent-downloader lxml==5.3.0 - # via cinemagoer - # via tpblite markupsafe==2.1.5 - # via jinja2 pydantic==2.8.2 - # via fastapi - # via pydantic-settings pydantic-core==2.20.1 - # via pydantic pydantic-settings==2.4.0 - # via torrent-downloader python-dotenv==1.0.1 - # via pydantic-settings requests==2.32.3 - # via transmission-rpc sniffio==1.3.1 - # via anyio sqlalchemy==2.0.34 - # via cinemagoer starlette==0.38.5 - # via fastapi tpblite==0.8.0 - # via torrent-downloader transmission-rpc==7.0.11 - # via torrent-downloader typing-extensions==4.12.2 - # via fastapi - # via pydantic - # via pydantic-core - # via sqlalchemy - # via transmission-rpc urllib3==2.2.2 - # via requests diff --git a/src/torrent_downloader/app.py b/src/torrent_downloader/app.py index b850c06..6602d47 100644 --- a/src/torrent_downloader/app.py +++ b/src/torrent_downloader/app.py @@ -3,12 +3,13 @@ import urllib.parse import transmission_rpc from fastapi import Depends, FastAPI, Request -from fastapi.responses import HTMLResponse, RedirectResponse +from fastapi.responses import HTMLResponse from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templates from torrent_downloader.client import TorrentDownloader from torrent_downloader.imdb import search_string_from_url +from torrent_downloader.systemd import Systemd app = FastAPI() app.mount("/static", StaticFiles(directory="static"), name="static") @@ -19,8 +20,16 @@ def get_downloader() -> TorrentDownloader: return TorrentDownloader() -def get_active_torrents(downloader=get_downloader()) -> list[transmission_rpc.Torrent]: - return [t for t in downloader.get_active_torrents() if t.format_eta() != "not available"] +def get_systemd() -> Systemd: + return Systemd() + + +def get_active_torrents( + downloader=get_downloader(), +) -> list[transmission_rpc.Torrent]: + return [ + t for t in downloader.get_active_torrents() if t.format_eta().lower() != "not available" + ] def hx(func): @@ -41,13 +50,8 @@ def hx(func): @app.get("/") -async def index(_: Request): - return RedirectResponse("/search") - - -@app.get("/search", response_class=HTMLResponse) @hx -async def search_for_torrents(request: Request): +async def index(request: Request): return templates.TemplateResponse(request=request, name="search.html") @@ -70,7 +74,9 @@ async def download(request: Request, downloader=Depends(get_downloader)): downloader.download_magnet(magnet) active = downloader.get_active_torrents() return templates.TemplateResponse( - request=request, name="active_torrents.html", context={"torrents": active} + request=request, + name="active_torrents.html", + context={"torrents": active}, ) @@ -78,5 +84,34 @@ async def download(request: Request, downloader=Depends(get_downloader)): @hx async def active(request: Request, active=Depends(get_active_torrents)): return templates.TemplateResponse( - request=request, name="active_torrents.html", context={"torrents": active} + request=request, + name="active_torrents.html", + context={"torrents": active}, + ) + + +@app.get("/transmission", response_class=HTMLResponse) +@hx +async def transmission_status(request: Request, systemd=Depends(get_systemd)): + return templates.TemplateResponse( + request=request, + name="transmission.html", + context={"is_active": systemd.service_is_active("transmission")}, + ) + + +@app.post("/transmission/{action}", response_class=HTMLResponse) +@hx +async def transmission_start(request: Request, action: str, systemd=Depends(get_systemd)): + match action: + case "start": + systemd.service_start("transmission") + case "stop": + systemd.service_stop("transmission") + case _: + return 404 + return templates.TemplateResponse( + request=request, + name="transmission.html", + context={"is_active": systemd.service_is_active("transmission")}, ) diff --git a/src/torrent_downloader/systemd.py b/src/torrent_downloader/systemd.py new file mode 100644 index 0000000..ffbf955 --- /dev/null +++ b/src/torrent_downloader/systemd.py @@ -0,0 +1,24 @@ +import dbus + + +class Systemd: + def __init__(self): + self.bus = dbus.SessionBus() + self.systemd = self.bus.get_object( + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + ) + self.manager = dbus.Interface(self.systemd, "org.freedesktop.systemd1.Manager") + + def service_start(self, service: str): + return self.manager.StartUnit(f"{service}.service", "replace") + + def service_stop(self, service: str): + return self.manager.StopUnit(f"{service}.service", "replace") + + def service_is_active(self, service: str): + try: + self.manager.GetUnit(service) + return True + except dbus.exceptions.DBusException: + return False diff --git a/templates/index.html b/templates/index.html index 6724897..9eae3d6 100644 --- a/templates/index.html +++ b/templates/index.html @@ -5,7 +5,7 @@ Search and download torrents directly to mediacenter - + diff --git a/templates/transmission.html b/templates/transmission.html new file mode 100644 index 0000000..7b01068 --- /dev/null +++ b/templates/transmission.html @@ -0,0 +1,9 @@ +{% block content %} +{% if is_active %} +

Transmission is running!

+ +{% else %} +

Transmission is stopped!

+ +{% endif %} +{% endblock %}