Add systemd start/stop for transmission.service
This commit is contained in:
parent
385694da91
commit
66acf6b421
7 changed files with 85 additions and 92 deletions
|
@ -1,7 +1,7 @@
|
||||||
[project]
|
[project]
|
||||||
name = "torrent-downloader"
|
name = "torrent-downloader"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
description = "Add your description here"
|
description = "Web interface for searching and downloading torrents"
|
||||||
authors = [
|
authors = [
|
||||||
{ name = "Vladan Popovic", email = "vladanovic@gmail.com" }
|
{ name = "Vladan Popovic", email = "vladanovic@gmail.com" }
|
||||||
]
|
]
|
||||||
|
@ -12,6 +12,7 @@ dependencies = [
|
||||||
"fastapi>=0.114.2",
|
"fastapi>=0.114.2",
|
||||||
"jinja2>=3.1.4",
|
"jinja2>=3.1.4",
|
||||||
"cinemagoer>=2023.5.1",
|
"cinemagoer>=2023.5.1",
|
||||||
|
"dbus-python>=1.3.2",
|
||||||
]
|
]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">= 3.8"
|
requires-python = ">= 3.8"
|
||||||
|
|
|
@ -11,92 +11,45 @@
|
||||||
|
|
||||||
-e file:.
|
-e file:.
|
||||||
annotated-types==0.7.0
|
annotated-types==0.7.0
|
||||||
# via pydantic
|
|
||||||
anyio==4.4.0
|
anyio==4.4.0
|
||||||
# via starlette
|
|
||||||
asttokens==2.4.1
|
asttokens==2.4.1
|
||||||
# via stack-data
|
|
||||||
certifi==2024.8.30
|
certifi==2024.8.30
|
||||||
# via requests
|
|
||||||
charset-normalizer==3.3.2
|
charset-normalizer==3.3.2
|
||||||
# via requests
|
|
||||||
cinemagoer==2023.5.1
|
cinemagoer==2023.5.1
|
||||||
# via torrent-downloader
|
|
||||||
click==8.1.7
|
click==8.1.7
|
||||||
# via uvicorn
|
dbus-python==1.3.2
|
||||||
decorator==5.1.1
|
decorator==5.1.1
|
||||||
# via ipython
|
|
||||||
executing==2.1.0
|
executing==2.1.0
|
||||||
# via stack-data
|
|
||||||
fastapi==0.114.2
|
fastapi==0.114.2
|
||||||
# via torrent-downloader
|
|
||||||
greenlet==3.1.0
|
greenlet==3.1.0
|
||||||
# via sqlalchemy
|
|
||||||
h11==0.14.0
|
h11==0.14.0
|
||||||
# via uvicorn
|
|
||||||
idna==3.8
|
idna==3.8
|
||||||
# via anyio
|
|
||||||
# via requests
|
|
||||||
ipython==8.27.0
|
ipython==8.27.0
|
||||||
jedi==0.19.1
|
jedi==0.19.1
|
||||||
# via ipython
|
|
||||||
jinja2==3.1.4
|
jinja2==3.1.4
|
||||||
# via torrent-downloader
|
|
||||||
lxml==5.3.0
|
lxml==5.3.0
|
||||||
# via cinemagoer
|
|
||||||
# via tpblite
|
|
||||||
markupsafe==2.1.5
|
markupsafe==2.1.5
|
||||||
# via jinja2
|
|
||||||
matplotlib-inline==0.1.7
|
matplotlib-inline==0.1.7
|
||||||
# via ipython
|
|
||||||
parso==0.8.4
|
parso==0.8.4
|
||||||
# via jedi
|
|
||||||
pexpect==4.9.0
|
pexpect==4.9.0
|
||||||
# via ipython
|
|
||||||
prompt-toolkit==3.0.47
|
prompt-toolkit==3.0.47
|
||||||
# via ipython
|
|
||||||
ptyprocess==0.7.0
|
ptyprocess==0.7.0
|
||||||
# via pexpect
|
|
||||||
pure-eval==0.2.3
|
pure-eval==0.2.3
|
||||||
# via stack-data
|
|
||||||
pydantic==2.8.2
|
pydantic==2.8.2
|
||||||
# via fastapi
|
|
||||||
# via pydantic-settings
|
|
||||||
pydantic-core==2.20.1
|
pydantic-core==2.20.1
|
||||||
# via pydantic
|
|
||||||
pydantic-settings==2.4.0
|
pydantic-settings==2.4.0
|
||||||
# via torrent-downloader
|
|
||||||
pygments==2.18.0
|
pygments==2.18.0
|
||||||
# via ipython
|
|
||||||
python-dotenv==1.0.1
|
python-dotenv==1.0.1
|
||||||
# via pydantic-settings
|
|
||||||
requests==2.32.3
|
requests==2.32.3
|
||||||
# via transmission-rpc
|
|
||||||
six==1.16.0
|
six==1.16.0
|
||||||
# via asttokens
|
|
||||||
sniffio==1.3.1
|
sniffio==1.3.1
|
||||||
# via anyio
|
|
||||||
sqlalchemy==2.0.34
|
sqlalchemy==2.0.34
|
||||||
# via cinemagoer
|
|
||||||
stack-data==0.6.3
|
stack-data==0.6.3
|
||||||
# via ipython
|
|
||||||
starlette==0.38.5
|
starlette==0.38.5
|
||||||
# via fastapi
|
|
||||||
tpblite==0.8.0
|
tpblite==0.8.0
|
||||||
# via torrent-downloader
|
|
||||||
traitlets==5.14.3
|
traitlets==5.14.3
|
||||||
# via ipython
|
|
||||||
# via matplotlib-inline
|
|
||||||
transmission-rpc==7.0.11
|
transmission-rpc==7.0.11
|
||||||
# via torrent-downloader
|
|
||||||
typing-extensions==4.12.2
|
typing-extensions==4.12.2
|
||||||
# via fastapi
|
|
||||||
# via pydantic
|
|
||||||
# via pydantic-core
|
|
||||||
# via sqlalchemy
|
|
||||||
# via transmission-rpc
|
|
||||||
urllib3==2.2.2
|
urllib3==2.2.2
|
||||||
# via requests
|
|
||||||
uvicorn==0.30.6
|
uvicorn==0.30.6
|
||||||
wcwidth==0.2.13
|
wcwidth==0.2.13
|
||||||
# via prompt-toolkit
|
|
||||||
|
|
|
@ -11,55 +11,26 @@
|
||||||
|
|
||||||
-e file:.
|
-e file:.
|
||||||
annotated-types==0.7.0
|
annotated-types==0.7.0
|
||||||
# via pydantic
|
|
||||||
anyio==4.4.0
|
anyio==4.4.0
|
||||||
# via starlette
|
|
||||||
certifi==2024.8.30
|
certifi==2024.8.30
|
||||||
# via requests
|
|
||||||
charset-normalizer==3.3.2
|
charset-normalizer==3.3.2
|
||||||
# via requests
|
|
||||||
cinemagoer==2023.5.1
|
cinemagoer==2023.5.1
|
||||||
# via torrent-downloader
|
dbus-python==1.3.2
|
||||||
fastapi==0.114.2
|
fastapi==0.114.2
|
||||||
# via torrent-downloader
|
|
||||||
greenlet==3.1.0
|
greenlet==3.1.0
|
||||||
# via sqlalchemy
|
|
||||||
idna==3.8
|
idna==3.8
|
||||||
# via anyio
|
|
||||||
# via requests
|
|
||||||
jinja2==3.1.4
|
jinja2==3.1.4
|
||||||
# via torrent-downloader
|
|
||||||
lxml==5.3.0
|
lxml==5.3.0
|
||||||
# via cinemagoer
|
|
||||||
# via tpblite
|
|
||||||
markupsafe==2.1.5
|
markupsafe==2.1.5
|
||||||
# via jinja2
|
|
||||||
pydantic==2.8.2
|
pydantic==2.8.2
|
||||||
# via fastapi
|
|
||||||
# via pydantic-settings
|
|
||||||
pydantic-core==2.20.1
|
pydantic-core==2.20.1
|
||||||
# via pydantic
|
|
||||||
pydantic-settings==2.4.0
|
pydantic-settings==2.4.0
|
||||||
# via torrent-downloader
|
|
||||||
python-dotenv==1.0.1
|
python-dotenv==1.0.1
|
||||||
# via pydantic-settings
|
|
||||||
requests==2.32.3
|
requests==2.32.3
|
||||||
# via transmission-rpc
|
|
||||||
sniffio==1.3.1
|
sniffio==1.3.1
|
||||||
# via anyio
|
|
||||||
sqlalchemy==2.0.34
|
sqlalchemy==2.0.34
|
||||||
# via cinemagoer
|
|
||||||
starlette==0.38.5
|
starlette==0.38.5
|
||||||
# via fastapi
|
|
||||||
tpblite==0.8.0
|
tpblite==0.8.0
|
||||||
# via torrent-downloader
|
|
||||||
transmission-rpc==7.0.11
|
transmission-rpc==7.0.11
|
||||||
# via torrent-downloader
|
|
||||||
typing-extensions==4.12.2
|
typing-extensions==4.12.2
|
||||||
# via fastapi
|
|
||||||
# via pydantic
|
|
||||||
# via pydantic-core
|
|
||||||
# via sqlalchemy
|
|
||||||
# via transmission-rpc
|
|
||||||
urllib3==2.2.2
|
urllib3==2.2.2
|
||||||
# via requests
|
|
||||||
|
|
|
@ -3,12 +3,13 @@ import urllib.parse
|
||||||
|
|
||||||
import transmission_rpc
|
import transmission_rpc
|
||||||
from fastapi import Depends, FastAPI, Request
|
from fastapi import Depends, FastAPI, Request
|
||||||
from fastapi.responses import HTMLResponse, RedirectResponse
|
from fastapi.responses import HTMLResponse
|
||||||
from fastapi.staticfiles import StaticFiles
|
from fastapi.staticfiles import StaticFiles
|
||||||
from fastapi.templating import Jinja2Templates
|
from fastapi.templating import Jinja2Templates
|
||||||
|
|
||||||
from torrent_downloader.client import TorrentDownloader
|
from torrent_downloader.client import TorrentDownloader
|
||||||
from torrent_downloader.imdb import search_string_from_url
|
from torrent_downloader.imdb import search_string_from_url
|
||||||
|
from torrent_downloader.systemd import Systemd
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
app.mount("/static", StaticFiles(directory="static"), name="static")
|
app.mount("/static", StaticFiles(directory="static"), name="static")
|
||||||
|
@ -19,8 +20,16 @@ def get_downloader() -> TorrentDownloader:
|
||||||
return TorrentDownloader()
|
return TorrentDownloader()
|
||||||
|
|
||||||
|
|
||||||
def get_active_torrents(downloader=get_downloader()) -> list[transmission_rpc.Torrent]:
|
def get_systemd() -> Systemd:
|
||||||
return [t for t in downloader.get_active_torrents() if t.format_eta() != "not available"]
|
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):
|
def hx(func):
|
||||||
|
@ -41,13 +50,8 @@ def hx(func):
|
||||||
|
|
||||||
|
|
||||||
@app.get("/")
|
@app.get("/")
|
||||||
async def index(_: Request):
|
|
||||||
return RedirectResponse("/search")
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/search", response_class=HTMLResponse)
|
|
||||||
@hx
|
@hx
|
||||||
async def search_for_torrents(request: Request):
|
async def index(request: Request):
|
||||||
return templates.TemplateResponse(request=request, name="search.html")
|
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)
|
downloader.download_magnet(magnet)
|
||||||
active = downloader.get_active_torrents()
|
active = downloader.get_active_torrents()
|
||||||
return templates.TemplateResponse(
|
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
|
@hx
|
||||||
async def active(request: Request, active=Depends(get_active_torrents)):
|
async def active(request: Request, active=Depends(get_active_torrents)):
|
||||||
return templates.TemplateResponse(
|
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")},
|
||||||
)
|
)
|
||||||
|
|
24
src/torrent_downloader/systemd.py
Normal file
24
src/torrent_downloader/systemd.py
Normal file
|
@ -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
|
|
@ -5,7 +5,7 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Search and download torrents directly to mediacenter</title>
|
<title>Search and download torrents directly to mediacenter</title>
|
||||||
<script src="https://unpkg.com/htmx.org@2.0.0" integrity="sha384-wS5l5IKJBvK6sPTKa2WZ1js3d947pvWXbPJ1OmWfEuxLgeHcEbjUUA5i9V5ZkpCw" crossorigin="anonymous"></script>
|
<script src="https://unpkg.com/htmx.org@2.0.0" integrity="sha384-wS5l5IKJBvK6sPTKa2WZ1js3d947pvWXbPJ1OmWfEuxLgeHcEbjUUA5i9V5ZkpCw" crossorigin="anonymous"></script>
|
||||||
<link href="{{ url_for('static', path='/style.css') }}" rel="stylesheet">
|
<link href="/static/style.css" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<menu>
|
<menu>
|
||||||
|
|
9
templates/transmission.html
Normal file
9
templates/transmission.html
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{% block content %}
|
||||||
|
{% if is_active %}
|
||||||
|
<h3>Transmission is running!</h3>
|
||||||
|
<button hx-post="/transmission/stop" hx-target="#content">Stop now!</button>
|
||||||
|
{% else %}
|
||||||
|
<h3>Transmission is stopped!</h3>
|
||||||
|
<button hx-post="/transmission/start" hx-target="#content">Start now!</button>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
Loading…
Add table
Reference in a new issue