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
		Add a link
		
	
		Reference in a new issue