Unit3DwebUp 0.0.25__tar.gz → 0.0.27__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/PKG-INFO +2 -1
  2. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/Unit3DwebUp.egg-info/PKG-INFO +2 -1
  3. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/Unit3DwebUp.egg-info/SOURCES.txt +13 -0
  4. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/Unit3DwebUp.egg-info/requires.txt +1 -0
  5. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/pyproject.toml +2 -1
  6. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/config/__init__.py +2 -2
  7. unit3dwebup-0.0.27/unit3dwup/config/limiter.py +6 -0
  8. unit3dwebup-0.0.27/unit3dwup/lifespan.py +154 -0
  9. {unit3dwebup-0.0.25/unit3dwup/services → unit3dwebup-0.0.27/unit3dwup/routers}/__init__.py +0 -0
  10. unit3dwebup-0.0.27/unit3dwup/routers/dependencies.py +22 -0
  11. unit3dwebup-0.0.27/unit3dwup/routers/jobs.py +63 -0
  12. unit3dwebup-0.0.27/unit3dwup/routers/posters.py +116 -0
  13. unit3dwebup-0.0.27/unit3dwup/routers/process.py +105 -0
  14. unit3dwebup-0.0.27/unit3dwup/routers/scan.py +159 -0
  15. unit3dwebup-0.0.27/unit3dwup/routers/search.py +49 -0
  16. unit3dwebup-0.0.27/unit3dwup/routers/settings.py +163 -0
  17. unit3dwebup-0.0.27/unit3dwup/routers/ws.py +42 -0
  18. unit3dwebup-0.0.27/unit3dwup/schemas.py +33 -0
  19. unit3dwebup-0.0.27/unit3dwup/services/__init__.py +0 -0
  20. unit3dwebup-0.0.27/unit3dwup/start.py +47 -0
  21. unit3dwebup-0.0.25/unit3dwup/start.py → unit3dwebup-0.0.27/unit3dwup/start_backup.py +47 -14
  22. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/README.md +0 -0
  23. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/Unit3DwebUp.egg-info/dependency_links.txt +0 -0
  24. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/Unit3DwebUp.egg-info/entry_points.txt +0 -0
  25. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/Unit3DwebUp.egg-info/top_level.txt +0 -0
  26. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/setup.cfg +0 -0
  27. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/__init__.py +0 -0
  28. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/config/api_data.py +0 -0
  29. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/config/constants.py +0 -0
  30. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/config/host_data.py +0 -0
  31. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/config/itt.py +0 -0
  32. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/config/logger.py +0 -0
  33. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/config/settings.py +0 -0
  34. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/config/sis.py +0 -0
  35. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/config/tags.py +0 -0
  36. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/config/trackers.py +0 -0
  37. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/external/__init__.py +0 -0
  38. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/external/async_http_client_service.py +0 -0
  39. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/external/websocket.py +0 -0
  40. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/models/__init__.py +0 -0
  41. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/models/interfaces.py +0 -0
  42. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/models/keywords.py +0 -0
  43. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/models/media.py +0 -0
  44. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/models/media_info.py +0 -0
  45. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/models/movie.py +0 -0
  46. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/models/subtitles.py +0 -0
  47. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/models/tv.py +0 -0
  48. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/models/tvdb_search.py +0 -0
  49. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/models/videos.py +0 -0
  50. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/repositories/__init__.py +0 -0
  51. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/repositories/db_online.py +0 -0
  52. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/repositories/interfaces.py +0 -0
  53. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/repositories/job_repos.py +0 -0
  54. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/repositories/media_info_factory.py +0 -0
  55. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/services/auto_async_service.py +0 -0
  56. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/services/create_torrent_service.py +0 -0
  57. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/services/interfaces.py +0 -0
  58. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/services/itt_tracker_helper.py +0 -0
  59. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/services/itt_tracker_service.py +0 -0
  60. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/services/lifespan_service.py +0 -0
  61. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/services/media_service.py +0 -0
  62. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/services/tags_service.py +0 -0
  63. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/services/tmdb.py +0 -0
  64. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/services/torrent_client_service.py +0 -0
  65. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/services/torrent_service.py +0 -0
  66. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/services/tvdb.py +0 -0
  67. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/services/utility.py +0 -0
  68. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/services/video_service.py +0 -0
  69. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/use_case/__init__.py +0 -0
  70. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/use_case/make_torrent_usecase.py +0 -0
  71. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/use_case/process_all_usecase.py +0 -0
  72. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/use_case/scan_media_usecase.py +0 -0
  73. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/use_case/seed_usecase.py +0 -0
  74. {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/use_case/upload_usecase.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Unit3DwebUp
3
- Version: 0.0.25
3
+ Version: 0.0.27
4
4
  Summary: A Unit3D uploader with a web interface
5
5
  Author: Parzival
6
6
  License-Expression: GPL-3.0-only
@@ -9,6 +9,7 @@ Requires-Python: >=3.12
9
9
  Description-Content-Type: text/markdown
10
10
  Requires-Dist: aiohttp==3.13.3
11
11
  Requires-Dist: fastapi==0.129.0
12
+ Requires-Dist: slowapi==0.1.9
12
13
  Requires-Dist: guessit==3.8.0
13
14
  Requires-Dist: numpy==2.4.2
14
15
  Requires-Dist: pathvalidate==3.3.1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Unit3DwebUp
3
- Version: 0.0.25
3
+ Version: 0.0.27
4
4
  Summary: A Unit3D uploader with a web interface
5
5
  Author: Parzival
6
6
  License-Expression: GPL-3.0-only
@@ -9,6 +9,7 @@ Requires-Python: >=3.12
9
9
  Description-Content-Type: text/markdown
10
10
  Requires-Dist: aiohttp==3.13.3
11
11
  Requires-Dist: fastapi==0.129.0
12
+ Requires-Dist: slowapi==0.1.9
12
13
  Requires-Dist: guessit==3.8.0
13
14
  Requires-Dist: numpy==2.4.2
14
15
  Requires-Dist: pathvalidate==3.3.1
@@ -7,12 +7,16 @@ Unit3DwebUp.egg-info/entry_points.txt
7
7
  Unit3DwebUp.egg-info/requires.txt
8
8
  Unit3DwebUp.egg-info/top_level.txt
9
9
  unit3dwup/__init__.py
10
+ unit3dwup/lifespan.py
11
+ unit3dwup/schemas.py
10
12
  unit3dwup/start.py
13
+ unit3dwup/start_backup.py
11
14
  unit3dwup/config/__init__.py
12
15
  unit3dwup/config/api_data.py
13
16
  unit3dwup/config/constants.py
14
17
  unit3dwup/config/host_data.py
15
18
  unit3dwup/config/itt.py
19
+ unit3dwup/config/limiter.py
16
20
  unit3dwup/config/logger.py
17
21
  unit3dwup/config/settings.py
18
22
  unit3dwup/config/sis.py
@@ -36,6 +40,15 @@ unit3dwup/repositories/db_online.py
36
40
  unit3dwup/repositories/interfaces.py
37
41
  unit3dwup/repositories/job_repos.py
38
42
  unit3dwup/repositories/media_info_factory.py
43
+ unit3dwup/routers/__init__.py
44
+ unit3dwup/routers/dependencies.py
45
+ unit3dwup/routers/jobs.py
46
+ unit3dwup/routers/posters.py
47
+ unit3dwup/routers/process.py
48
+ unit3dwup/routers/scan.py
49
+ unit3dwup/routers/search.py
50
+ unit3dwup/routers/settings.py
51
+ unit3dwup/routers/ws.py
39
52
  unit3dwup/services/__init__.py
40
53
  unit3dwup/services/auto_async_service.py
41
54
  unit3dwup/services/create_torrent_service.py
@@ -1,5 +1,6 @@
1
1
  aiohttp==3.13.3
2
2
  fastapi==0.129.0
3
+ slowapi==0.1.9
3
4
  guessit==3.8.0
4
5
  numpy==2.4.2
5
6
  pathvalidate==3.3.1
@@ -7,6 +7,7 @@ dynamic = []
7
7
  dependencies = [
8
8
  "aiohttp==3.13.3",
9
9
  "fastapi==0.129.0",
10
+ "slowapi==0.1.9",
10
11
  "guessit==3.8.0",
11
12
  "numpy==2.4.2",
12
13
  "pathvalidate==3.3.1",
@@ -24,7 +25,7 @@ dependencies = [
24
25
  "uvicorn[standard]==0.40.0"
25
26
  ]
26
27
  name = "Unit3DwebUp"
27
- version = "0.0.25"
28
+ version = "0.0.27"
28
29
 
29
30
  description = "A Unit3D uploader with a web interface"
30
31
  readme = { file = "README.md", content-type = "text/markdown" }
@@ -3,8 +3,8 @@ from .sis import sis_data
3
3
 
4
4
  tracker_list = {'ITT': itt_data, 'SIS': sis_data}
5
5
 
6
- from .settings import get_settings
6
+ from .settings import get_settings,Settings
7
7
  from .constants import MediaStatus
8
8
  from .logger import get_logger
9
9
 
10
- __version__ = "0.0.25"
10
+ __version__ = "0.0.27"
@@ -0,0 +1,6 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from slowapi import Limiter
4
+ from slowapi.util import get_remote_address
5
+
6
+ limiter = Limiter(key_func=get_remote_address)
@@ -0,0 +1,154 @@
1
+ # -*- coding: utf-8 -*-
2
+ import asyncio
3
+ import os
4
+
5
+ from contextlib import asynccontextmanager
6
+ from pathlib import Path
7
+
8
+ from unit3dwup.config import get_settings
9
+ from unit3dwup.config import get_logger
10
+
11
+ from unit3dwup.repositories.job_repos import JobRedisRepo
12
+
13
+ from unit3dwup.services.lifespan_service import update_mounted_paths, checking_env_file
14
+
15
+ from unit3dwup.external.websocket import WebSocketManager
16
+
17
+ from fastapi import FastAPI
18
+
19
+ from watchdog.observers import Observer
20
+ from watchdog.events import FileSystemEventHandler
21
+
22
+
23
+ class RedisEventHandler(FileSystemEventHandler):
24
+ """
25
+ Watch only a specific folder
26
+ This event is shared by app.state FastApi
27
+ it uses a queue 'redis_event'
28
+ """
29
+
30
+ def __init__(self, app):
31
+ self.app = app
32
+
33
+ def on_created(self, event):
34
+ self.app.state.redis_events.put_nowait({
35
+ "type": "created",
36
+ "path": event.src_path,
37
+ })
38
+
39
+ def on_deleted(self, event):
40
+ self.app.state.redis_events.put_nowait({
41
+ "type": "deleted",
42
+ "path": event.src_path,
43
+ })
44
+
45
+
46
+ async def redis_event_consumer(app: FastAPI):
47
+ """
48
+ :param app: it is mr FastApi
49
+ :return: None
50
+
51
+ It is a consumer. Wait for any news from the queue and extract the new path created or deleted
52
+ """
53
+
54
+ # > The queue :|
55
+ queue = app.state.redis_events
56
+
57
+ # Logger
58
+ logger = get_logger("settings_logger")
59
+
60
+ while True:
61
+ event = await queue.get()
62
+ try:
63
+ app.state.folder_event = event
64
+ relative = Path(app.state.folder_event['path']).relative_to(Path(app.state.watcher_path))
65
+ new_path = Path(app.state.settings.prefs.WATCHER_PATH) / relative.parts[0]
66
+
67
+ # Send logs to the client
68
+ await app.state.ws_manager.broadcast({
69
+ "type": "log",
70
+ "level": "info",
71
+ "message": f"{app.state.folder_event['type']} {new_path}",
72
+ })
73
+ except Exception as e:
74
+ logger.debug("Consumer Folder error", e)
75
+ finally:
76
+ queue.task_done()
77
+
78
+
79
+ @asynccontextmanager
80
+ async def lifespan(app: FastAPI):
81
+ """
82
+ Lifespan initialize DB and app configuration
83
+ We need to shared state
84
+ https://fastapi.tiangolo.com/advanced/events/
85
+
86
+ :param app: FastAPI
87
+ :return: None
88
+ """
89
+ # Load the configuration file
90
+ settings = get_settings()
91
+ app.state.settings = settings
92
+
93
+ # Check configuration file
94
+ await checking_env_file(app=app)
95
+
96
+ # Create a new state for the watcher queue
97
+ app.state.redis_events = asyncio.Queue()
98
+
99
+ # Environment variabile in the container backend
100
+ redis_host = os.getenv("REDIS_HOST", "localhost")
101
+ redis_port = int(os.getenv("REDIS_PORT", 6379))
102
+
103
+ # Build redis url
104
+ REDIS_URL = f"redis://{redis_host}:{redis_port}"
105
+
106
+ # Connect to redis
107
+ job = JobRedisRepo(url=REDIS_URL)
108
+ await job.connect(app=app)
109
+
110
+ # Store the job reference to app.state
111
+ app.state.job = job
112
+
113
+ # RestartDocker notify
114
+ # Set flag to true when setEnv is called from the frontend
115
+ app.state.restart_docker = False
116
+
117
+ # Create a new profile from user_preferences Job_id is '0'
118
+ # Later will be recalled from the setting endpoint
119
+ await job.create_profile(dict(settings.prefs))
120
+
121
+ # The WebSocket. Send to client progress bar value( Torrent creation) and short log message
122
+ app.state.ws_manager = WebSocketManager()
123
+
124
+ # Update mounted paths string
125
+ await update_mounted_paths(app=app)
126
+
127
+ # Watcher zone
128
+ # Shared event
129
+ app.state.folder_event = None
130
+
131
+ # back to watcher
132
+ observer = Observer()
133
+
134
+ # Callback
135
+ handler = RedisEventHandler(app)
136
+
137
+ # Start to watch
138
+ observer.schedule(handler, app.state.watcher_path, recursive=True)
139
+ if app.state.settings.prefs.WATCHER_DESTINATION_PATH != os.getcwd():
140
+ observer.start()
141
+
142
+ # Create a consumer that works in the background
143
+ consumer_task = asyncio.create_task(redis_event_consumer(app))
144
+
145
+ # Goes..
146
+ yield
147
+
148
+ # Come back to clean and close
149
+ if app.state.settings.prefs.WATCHER_DESTINATION_PATH != os.getcwd():
150
+ observer.stop()
151
+ observer.join()
152
+
153
+ consumer_task.cancel()
154
+ await job.close()
@@ -0,0 +1,22 @@
1
+ # -*- coding: utf-8 -*-
2
+ from fastapi import Request
3
+
4
+ from unit3dwup.config.settings import Settings
5
+ from unit3dwup.external.websocket import WebSocketManager
6
+ from unit3dwup.repositories.job_repos import JobRedisRepo
7
+
8
+
9
+ def load_settings(request: Request) -> Settings:
10
+ return request.app.state.settings
11
+
12
+
13
+ def get_job_repo(request: Request) -> JobRedisRepo:
14
+ return request.app.state.job
15
+
16
+
17
+ def get_ws_manager(request: Request) -> WebSocketManager:
18
+ return request.app.state.ws_manager
19
+
20
+
21
+ def get_restart_docker(request: Request) -> bool:
22
+ return request.app.state.restart_docker
@@ -0,0 +1,63 @@
1
+ # -*- coding: utf-8 -*-
2
+ import inspect
3
+ import json
4
+
5
+ from fastapi import APIRouter, Depends, Request
6
+
7
+ from unit3dwup.config import get_logger
8
+ from unit3dwup.config.limiter import limiter
9
+
10
+ from unit3dwup.schemas import ClearJobListRequest
11
+
12
+ from unit3dwup.external.websocket import WebSocketManager
13
+ from unit3dwup.routers.dependencies import (
14
+ get_job_repo,
15
+ get_ws_manager,
16
+ )
17
+
18
+ router = APIRouter()
19
+
20
+
21
+ @router.post("/cjoblist")
22
+ @limiter.limit("5/minute")
23
+ async def clear_job_list_id(payload: ClearJobListRequest, request: Request,
24
+ job_repo=Depends(get_job_repo),
25
+ ws_manager: WebSocketManager = Depends(get_ws_manager),
26
+ ):
27
+ """
28
+ This endpoint deletes a job list and all related posters
29
+
30
+ Required:
31
+ - job_list_id: identifier of the job list
32
+
33
+ Returns:
34
+ - status: operation result
35
+ """
36
+
37
+ # Load the job list
38
+ # Carica la joblist per ottenere il percorso e inviarlo a log. Mi sembra troppo per una stampa
39
+ job_list = await job_repo.job.get_job_list(job_id=payload.job_list_id)
40
+ results = [json.loads(await job_repo.get_job(job_id)) for job_id in job_list]
41
+
42
+ # Logger
43
+ frame = inspect.currentframe()
44
+ logger = get_logger(frame.f_code.co_name)
45
+
46
+ # Delete the job list
47
+ # TODO: delete all job ids
48
+ await job_repo.delete_job_list(job_id=payload.job_list_id)
49
+
50
+ if results:
51
+ await ws_manager.broadcast({
52
+ "type": "log",
53
+ "level": "success",
54
+ "message": f"Clear JobList {payload.job_list_id} {results[0]['folder']}",
55
+ })
56
+ logger.info(f"-> Clear JobList ID° {payload.job_list_id} {results[0]['folder']}\n")
57
+ else:
58
+ await ws_manager.broadcast({
59
+ "type": "log",
60
+ "level": "error",
61
+ "message": f"Clear JobList : JobList id not found",
62
+ })
63
+ logger.info(f"-> Clear JobList : JobList ID non trovato\n")
@@ -0,0 +1,116 @@
1
+ # -*- coding: utf-8 -*-
2
+ import inspect
3
+
4
+ from fastapi import APIRouter, Request
5
+
6
+ from unit3dwup.config import MediaStatus
7
+ from unit3dwup.config import get_logger
8
+ from unit3dwup.config.limiter import limiter
9
+
10
+ from unit3dwup.schemas import UpdatePosterRequest
11
+
12
+ router = APIRouter()
13
+
14
+
15
+ async def update_poster(app, msg: str, job_id: str, field_id: str, new_id: str):
16
+ # Fix the DB ID with the received one
17
+ await app.state.job.update_job(job_id=job_id, new_data={field_id: new_id})
18
+
19
+ # Update the Media status
20
+ await app.state.job.update_job(job_id=job_id, new_data={'status': str(MediaStatus.DB_IDENTIFIED)})
21
+
22
+ # Logger
23
+ frame = inspect.currentframe()
24
+ logger = get_logger(frame.f_code.co_name)
25
+
26
+ # Console message
27
+ logger.info(f"-> Update {msg} JOB_ID: {job_id}\n")
28
+
29
+ # Send log to the client
30
+ await app.state.ws_manager.broadcast({
31
+ "type": "log",
32
+ "level": "success",
33
+ "message": f"Update {msg} JOB_ID {job_id}",
34
+ })
35
+
36
+
37
+ @router.post("/settmdbid")
38
+ @limiter.limit("5/minute")
39
+ async def set_poster_id(payload: UpdatePosterRequest, request: Request):
40
+ """
41
+ Set a Tmdb id for example when tmdb returns an empty result
42
+
43
+ Required
44
+ - job_id identifies each poster corresponds to Media.job_id
45
+
46
+ Return
47
+ - none
48
+ """
49
+
50
+ await update_poster(request.app, msg="TMDB", job_id=payload.job_id, field_id=payload.field_id, new_id=payload.new_id)
51
+
52
+
53
+ @router.post("/settvdbid")
54
+ @limiter.limit("5/minute")
55
+ async def set_tvdb_id(payload: UpdatePosterRequest, request: Request):
56
+ """
57
+ Set a TVdb id for example when tvdb returns an empty result
58
+
59
+ Required
60
+ - job_id identifies each poster corresponds to Media.job_id
61
+
62
+ Return
63
+ - none
64
+ """
65
+
66
+ await update_poster(request.app, msg="TVDB", job_id=payload.job_id, field_id=payload.field_id, new_id=payload.new_id)
67
+
68
+
69
+ @router.post("/setimdbid")
70
+ @limiter.limit("5/minute")
71
+ async def set_imdb_id(payload: UpdatePosterRequest, request: Request):
72
+ """
73
+ Set an Imdb id for example when the remote list of tvdb is empty
74
+
75
+ Required
76
+ - job_id identifies each poster corresponds to Media.job_id
77
+
78
+ Return
79
+ - none
80
+ """
81
+
82
+ await update_poster(request.app, msg="IMDB", job_id=payload.job_id, field_id=payload.field_id, new_id=payload.new_id)
83
+
84
+
85
+ @router.post("/setposterurl")
86
+ @limiter.limit("5/minute")
87
+ async def set_poster_url(payload: UpdatePosterRequest, request: Request):
88
+ """
89
+ Set a poster url for example when tmdb returns an empty result only for frontend
90
+
91
+ Required
92
+ - job_id identifies each poster corresponds to Media.job_id
93
+
94
+ Return
95
+ - none
96
+ """
97
+
98
+ await update_poster(request.app, msg="TMDB Poster Url", job_id=payload.job_id, field_id=payload.field_id, new_id=payload.new_id)
99
+
100
+
101
+ @router.post("/setposterdname")
102
+ @limiter.limit("5/minute")
103
+ async def set_poster_dname(payload: UpdatePosterRequest, request: Request):
104
+ """
105
+ Set a poster display name for example if you dont like it
106
+ Display name is the name shown on the dedicated torrent page
107
+
108
+ Required
109
+ - job_id identifies each poster corresponds to Media.job_id
110
+
111
+ Return
112
+ - none
113
+ """
114
+
115
+ await update_poster(request.app, msg="Update DisplayName", job_id=payload.job_id, field_id=payload.field_id,
116
+ new_id=payload.new_id)
@@ -0,0 +1,105 @@
1
+ # -*- coding: utf-8 -*-
2
+ from fastapi import APIRouter, Request, Depends
3
+ from fastapi.responses import JSONResponse
4
+
5
+ from unit3dwup.config import Settings
6
+ from unit3dwup.config.limiter import limiter
7
+
8
+ from unit3dwup.use_case.process_all_usecase import ProcessAllUseCase
9
+ from unit3dwup.use_case.upload_usecase import UploadUseCase
10
+ from unit3dwup.use_case.seed_usecase import SeedUseCase
11
+ from unit3dwup.use_case.make_torrent_usecase import MakeTorrentUseCase
12
+
13
+ from unit3dwup.schemas import ProcessAllRequest, JobRequest
14
+
15
+ from unit3dwup.routers.dependencies import (
16
+ load_settings,
17
+ )
18
+
19
+ router = APIRouter()
20
+
21
+
22
+ @router.post("/processall")
23
+ @limiter.limit("5/minute")
24
+ async def process_all(payload: ProcessAllRequest, request: Request,
25
+ settings: Settings = Depends(load_settings),
26
+ ):
27
+ """
28
+ Start a chain load the joblist filter for existing torrent create torrent upload the complete joblist
29
+
30
+ Required
31
+ - job_list_id identifies the list created by the scan endpoint
32
+
33
+ Prerequisite
34
+ - valid description status media class attribute
35
+
36
+ Return
37
+ - none
38
+ """
39
+
40
+ app = request.app
41
+
42
+ use_case = ProcessAllUseCase(app=app, job_list_id=payload.job_list_id,
43
+ torrent_client_name=settings.torrent.TORRENT_CLIENT)
44
+ await use_case.execute()
45
+
46
+
47
+ @router.post("/maketorrent")
48
+ @limiter.limit("5/minute")
49
+ async def make(payload: JobRequest, request: Request):
50
+ """
51
+ Create one or more torrent files
52
+
53
+ Required
54
+ - job_id identifies each poster corresponds to Media.job_id
55
+
56
+ Return
57
+ - none
58
+ """
59
+ app = request.app
60
+
61
+ torrent_service = MakeTorrentUseCase(app=app, job_id=payload.job_id)
62
+ await torrent_service.execute()
63
+
64
+
65
+ @router.post("/upload")
66
+ @limiter.limit("5/minute")
67
+ async def upload(payload: JobRequest, request: Request):
68
+ """
69
+ Upload a single torrent file
70
+
71
+ Required
72
+ - job_id identifies each poster corresponds to Media.job_id
73
+
74
+ Return
75
+ - none
76
+
77
+ WebSocket events emitted
78
+ - posterLogMessage: sent for each uploaded torrent
79
+ - job_id: media identifier
80
+ - message: upload result message
81
+ """
82
+
83
+ app = request.app
84
+
85
+ upload_service = UploadUseCase(app=app, job_id=payload.job_id)
86
+ await upload_service.execute()
87
+
88
+
89
+ @router.post("/seed")
90
+ @limiter.limit("5/minute")
91
+ async def seed(payload: JobRequest, request: Request,
92
+ settings: Settings = Depends(load_settings),
93
+ ) -> JSONResponse:
94
+ """
95
+ Required
96
+ - job_id identifies each poster corresponds to Media.job_id
97
+
98
+ Return
99
+ - none
100
+ """
101
+
102
+ app = request.app
103
+
104
+ use_case = SeedUseCase(app=app, client=settings.torrent.TORRENT_CLIENT, job_id=payload.job_id)
105
+ return await use_case.execute()