Unit3DwebUp 0.0.26__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.26 → unit3dwebup-0.0.27}/PKG-INFO +2 -1
  2. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/Unit3DwebUp.egg-info/PKG-INFO +2 -1
  3. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/Unit3DwebUp.egg-info/SOURCES.txt +2 -0
  4. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/Unit3DwebUp.egg-info/requires.txt +1 -0
  5. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/pyproject.toml +2 -1
  6. {unit3dwebup-0.0.26 → 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/routers/dependencies.py +22 -0
  9. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/routers/jobs.py +18 -9
  10. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/routers/posters.py +6 -0
  11. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/routers/process.py +20 -5
  12. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/routers/scan.py +22 -9
  13. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/routers/search.py +2 -0
  14. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/routers/settings.py +34 -22
  15. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/start.py +10 -0
  16. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/README.md +0 -0
  17. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/Unit3DwebUp.egg-info/dependency_links.txt +0 -0
  18. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/Unit3DwebUp.egg-info/entry_points.txt +0 -0
  19. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/Unit3DwebUp.egg-info/top_level.txt +0 -0
  20. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/setup.cfg +0 -0
  21. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/__init__.py +0 -0
  22. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/config/api_data.py +0 -0
  23. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/config/constants.py +0 -0
  24. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/config/host_data.py +0 -0
  25. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/config/itt.py +0 -0
  26. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/config/logger.py +0 -0
  27. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/config/settings.py +0 -0
  28. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/config/sis.py +0 -0
  29. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/config/tags.py +0 -0
  30. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/config/trackers.py +0 -0
  31. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/external/__init__.py +0 -0
  32. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/external/async_http_client_service.py +0 -0
  33. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/external/websocket.py +0 -0
  34. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/lifespan.py +0 -0
  35. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/models/__init__.py +0 -0
  36. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/models/interfaces.py +0 -0
  37. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/models/keywords.py +0 -0
  38. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/models/media.py +0 -0
  39. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/models/media_info.py +0 -0
  40. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/models/movie.py +0 -0
  41. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/models/subtitles.py +0 -0
  42. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/models/tv.py +0 -0
  43. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/models/tvdb_search.py +0 -0
  44. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/models/videos.py +0 -0
  45. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/repositories/__init__.py +0 -0
  46. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/repositories/db_online.py +0 -0
  47. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/repositories/interfaces.py +0 -0
  48. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/repositories/job_repos.py +0 -0
  49. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/repositories/media_info_factory.py +0 -0
  50. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/routers/__init__.py +0 -0
  51. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/routers/ws.py +0 -0
  52. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/schemas.py +0 -0
  53. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/services/__init__.py +0 -0
  54. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/services/auto_async_service.py +0 -0
  55. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/services/create_torrent_service.py +0 -0
  56. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/services/interfaces.py +0 -0
  57. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/services/itt_tracker_helper.py +0 -0
  58. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/services/itt_tracker_service.py +0 -0
  59. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/services/lifespan_service.py +0 -0
  60. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/services/media_service.py +0 -0
  61. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/services/tags_service.py +0 -0
  62. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/services/tmdb.py +0 -0
  63. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/services/torrent_client_service.py +0 -0
  64. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/services/torrent_service.py +0 -0
  65. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/services/tvdb.py +0 -0
  66. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/services/utility.py +0 -0
  67. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/services/video_service.py +0 -0
  68. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/start_backup.py +0 -0
  69. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/use_case/__init__.py +0 -0
  70. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/use_case/make_torrent_usecase.py +0 -0
  71. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/use_case/process_all_usecase.py +0 -0
  72. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/use_case/scan_media_usecase.py +0 -0
  73. {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/use_case/seed_usecase.py +0 -0
  74. {unit3dwebup-0.0.26 → 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.26
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.26
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
@@ -16,6 +16,7 @@ unit3dwup/config/api_data.py
16
16
  unit3dwup/config/constants.py
17
17
  unit3dwup/config/host_data.py
18
18
  unit3dwup/config/itt.py
19
+ unit3dwup/config/limiter.py
19
20
  unit3dwup/config/logger.py
20
21
  unit3dwup/config/settings.py
21
22
  unit3dwup/config/sis.py
@@ -40,6 +41,7 @@ unit3dwup/repositories/interfaces.py
40
41
  unit3dwup/repositories/job_repos.py
41
42
  unit3dwup/repositories/media_info_factory.py
42
43
  unit3dwup/routers/__init__.py
44
+ unit3dwup/routers/dependencies.py
43
45
  unit3dwup/routers/jobs.py
44
46
  unit3dwup/routers/posters.py
45
47
  unit3dwup/routers/process.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.26"
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.26"
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,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
@@ -2,17 +2,28 @@
2
2
  import inspect
3
3
  import json
4
4
 
5
- from fastapi import APIRouter, Request
5
+ from fastapi import APIRouter, Depends, Request
6
6
 
7
7
  from unit3dwup.config import get_logger
8
+ from unit3dwup.config.limiter import limiter
8
9
 
9
10
  from unit3dwup.schemas import ClearJobListRequest
10
11
 
12
+ from unit3dwup.external.websocket import WebSocketManager
13
+ from unit3dwup.routers.dependencies import (
14
+ get_job_repo,
15
+ get_ws_manager,
16
+ )
17
+
11
18
  router = APIRouter()
12
19
 
13
20
 
14
21
  @router.post("/cjoblist")
15
- async def clear_job_list_id(payload: ClearJobListRequest, request: Request):
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
+ ):
16
27
  """
17
28
  This endpoint deletes a job list and all related posters
18
29
 
@@ -23,12 +34,10 @@ async def clear_job_list_id(payload: ClearJobListRequest, request: Request):
23
34
  - status: operation result
24
35
  """
25
36
 
26
- app = request.app
27
-
28
37
  # Load the job list
29
38
  # Carica la joblist per ottenere il percorso e inviarlo a log. Mi sembra troppo per una stampa
30
- job_list = await app.state.job.get_job_list(job_id=payload.job_list_id)
31
- results = [json.loads(await app.state.job.get_job(job_id)) for job_id in job_list]
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]
32
41
 
33
42
  # Logger
34
43
  frame = inspect.currentframe()
@@ -36,17 +45,17 @@ async def clear_job_list_id(payload: ClearJobListRequest, request: Request):
36
45
 
37
46
  # Delete the job list
38
47
  # TODO: delete all job ids
39
- await app.state.job.delete_job_list(job_id=payload.job_list_id)
48
+ await job_repo.delete_job_list(job_id=payload.job_list_id)
40
49
 
41
50
  if results:
42
- await app.state.ws_manager.broadcast({
51
+ await ws_manager.broadcast({
43
52
  "type": "log",
44
53
  "level": "success",
45
54
  "message": f"Clear JobList {payload.job_list_id} {results[0]['folder']}",
46
55
  })
47
56
  logger.info(f"-> Clear JobList ID° {payload.job_list_id} {results[0]['folder']}\n")
48
57
  else:
49
- await app.state.ws_manager.broadcast({
58
+ await ws_manager.broadcast({
50
59
  "type": "log",
51
60
  "level": "error",
52
61
  "message": f"Clear JobList : JobList id not found",
@@ -5,6 +5,7 @@ from fastapi import APIRouter, Request
5
5
 
6
6
  from unit3dwup.config import MediaStatus
7
7
  from unit3dwup.config import get_logger
8
+ from unit3dwup.config.limiter import limiter
8
9
 
9
10
  from unit3dwup.schemas import UpdatePosterRequest
10
11
 
@@ -34,6 +35,7 @@ async def update_poster(app, msg: str, job_id: str, field_id: str, new_id: str):
34
35
 
35
36
 
36
37
  @router.post("/settmdbid")
38
+ @limiter.limit("5/minute")
37
39
  async def set_poster_id(payload: UpdatePosterRequest, request: Request):
38
40
  """
39
41
  Set a Tmdb id for example when tmdb returns an empty result
@@ -49,6 +51,7 @@ async def set_poster_id(payload: UpdatePosterRequest, request: Request):
49
51
 
50
52
 
51
53
  @router.post("/settvdbid")
54
+ @limiter.limit("5/minute")
52
55
  async def set_tvdb_id(payload: UpdatePosterRequest, request: Request):
53
56
  """
54
57
  Set a TVdb id for example when tvdb returns an empty result
@@ -64,6 +67,7 @@ async def set_tvdb_id(payload: UpdatePosterRequest, request: Request):
64
67
 
65
68
 
66
69
  @router.post("/setimdbid")
70
+ @limiter.limit("5/minute")
67
71
  async def set_imdb_id(payload: UpdatePosterRequest, request: Request):
68
72
  """
69
73
  Set an Imdb id for example when the remote list of tvdb is empty
@@ -79,6 +83,7 @@ async def set_imdb_id(payload: UpdatePosterRequest, request: Request):
79
83
 
80
84
 
81
85
  @router.post("/setposterurl")
86
+ @limiter.limit("5/minute")
82
87
  async def set_poster_url(payload: UpdatePosterRequest, request: Request):
83
88
  """
84
89
  Set a poster url for example when tmdb returns an empty result only for frontend
@@ -94,6 +99,7 @@ async def set_poster_url(payload: UpdatePosterRequest, request: Request):
94
99
 
95
100
 
96
101
  @router.post("/setposterdname")
102
+ @limiter.limit("5/minute")
97
103
  async def set_poster_dname(payload: UpdatePosterRequest, request: Request):
98
104
  """
99
105
  Set a poster display name for example if you dont like it
@@ -1,7 +1,10 @@
1
1
  # -*- coding: utf-8 -*-
2
- from fastapi import APIRouter, Request
2
+ from fastapi import APIRouter, Request, Depends
3
3
  from fastapi.responses import JSONResponse
4
4
 
5
+ from unit3dwup.config import Settings
6
+ from unit3dwup.config.limiter import limiter
7
+
5
8
  from unit3dwup.use_case.process_all_usecase import ProcessAllUseCase
6
9
  from unit3dwup.use_case.upload_usecase import UploadUseCase
7
10
  from unit3dwup.use_case.seed_usecase import SeedUseCase
@@ -9,11 +12,18 @@ from unit3dwup.use_case.make_torrent_usecase import MakeTorrentUseCase
9
12
 
10
13
  from unit3dwup.schemas import ProcessAllRequest, JobRequest
11
14
 
15
+ from unit3dwup.routers.dependencies import (
16
+ load_settings,
17
+ )
18
+
12
19
  router = APIRouter()
13
20
 
14
21
 
15
22
  @router.post("/processall")
16
- async def process_all(payload: ProcessAllRequest, request: Request):
23
+ @limiter.limit("5/minute")
24
+ async def process_all(payload: ProcessAllRequest, request: Request,
25
+ settings: Settings = Depends(load_settings),
26
+ ):
17
27
  """
18
28
  Start a chain load the joblist filter for existing torrent create torrent upload the complete joblist
19
29
 
@@ -30,11 +40,12 @@ async def process_all(payload: ProcessAllRequest, request: Request):
30
40
  app = request.app
31
41
 
32
42
  use_case = ProcessAllUseCase(app=app, job_list_id=payload.job_list_id,
33
- torrent_client_name=app.state.settings.torrent.TORRENT_CLIENT)
43
+ torrent_client_name=settings.torrent.TORRENT_CLIENT)
34
44
  await use_case.execute()
35
45
 
36
46
 
37
47
  @router.post("/maketorrent")
48
+ @limiter.limit("5/minute")
38
49
  async def make(payload: JobRequest, request: Request):
39
50
  """
40
51
  Create one or more torrent files
@@ -52,6 +63,7 @@ async def make(payload: JobRequest, request: Request):
52
63
 
53
64
 
54
65
  @router.post("/upload")
66
+ @limiter.limit("5/minute")
55
67
  async def upload(payload: JobRequest, request: Request):
56
68
  """
57
69
  Upload a single torrent file
@@ -75,7 +87,10 @@ async def upload(payload: JobRequest, request: Request):
75
87
 
76
88
 
77
89
  @router.post("/seed")
78
- async def seed(payload: JobRequest, request: Request) -> JSONResponse:
90
+ @limiter.limit("5/minute")
91
+ async def seed(payload: JobRequest, request: Request,
92
+ settings: Settings = Depends(load_settings),
93
+ ) -> JSONResponse:
79
94
  """
80
95
  Required
81
96
  - job_id identifies each poster corresponds to Media.job_id
@@ -86,5 +101,5 @@ async def seed(payload: JobRequest, request: Request) -> JSONResponse:
86
101
 
87
102
  app = request.app
88
103
 
89
- use_case = SeedUseCase(app=app, client=app.state.settings.torrent.TORRENT_CLIENT, job_id=payload.job_id)
104
+ use_case = SeedUseCase(app=app, client=settings.torrent.TORRENT_CLIENT, job_id=payload.job_id)
90
105
  return await use_case.execute()
@@ -6,10 +6,11 @@ import time
6
6
 
7
7
  import aiohttp
8
8
 
9
- from fastapi import APIRouter, Request, status
9
+ from fastapi import APIRouter, WebSocket, status, FastAPI, Request, Depends
10
10
  from fastapi.responses import JSONResponse
11
11
 
12
- from unit3dwup.config import get_logger
12
+ from unit3dwup.config import get_logger, Settings
13
+ from unit3dwup.config.limiter import limiter
13
14
 
14
15
  from unit3dwup.repositories.db_online import Tmdb, Tvdb
15
16
 
@@ -20,11 +21,23 @@ from unit3dwup.use_case.scan_media_usecase import ScanMediaUseCase
20
21
 
21
22
  from unit3dwup.schemas import ScanRequest
22
23
 
24
+ from unit3dwup.external.websocket import WebSocketManager
25
+ from unit3dwup.routers.dependencies import (
26
+ load_settings,
27
+ get_job_repo,
28
+ get_ws_manager,
29
+ )
30
+
23
31
  router = APIRouter()
24
32
 
25
33
 
26
34
  @router.post("/scan")
27
- async def scan(payload: ScanRequest, request: Request) -> JSONResponse:
35
+ @limiter.limit("5/minute")
36
+ async def scan(payload: ScanRequest, request: Request,
37
+ settings: Settings = Depends(load_settings),
38
+ job_repo=Depends(get_job_repo),
39
+ ws_manager: WebSocketManager = Depends(get_ws_manager),
40
+ ) -> JSONResponse:
28
41
  """
29
42
  This endpoint scans the local files and creates a Media object for each associating it with its description
30
43
 
@@ -62,16 +75,16 @@ async def scan(payload: ScanRequest, request: Request) -> JSONResponse:
62
75
  start_time = time.perf_counter()
63
76
 
64
77
  # Get the id for the current path
65
- job_list_id = hashlib.sha256(app.state.settings.prefs.SCAN_PATH.encode()).hexdigest()
66
- logger.info(f"Current joblist_id {job_list_id} {app.state.settings.prefs.SCAN_PATH}")
78
+ job_list_id = hashlib.sha256(settings.prefs.SCAN_PATH.encode()).hexdigest()
79
+ logger.info(f"Current joblist_id {job_list_id} {settings.prefs.SCAN_PATH}")
67
80
 
68
81
  # Load the jobs list using the previous id
69
- job_list = await app.state.job.get_job_list(job_id=job_list_id)
82
+ job_list = await job_repo.get_job_list(job_id=job_list_id)
70
83
 
71
84
  # Load Media for each job id from the job_list
72
85
  job_list_results = []
73
86
  for job_id in job_list:
74
- job = await app.state.job.get_job(job_id)
87
+ job = await job_repo.get_job(job_id)
75
88
  if job:
76
89
  job_list_results.append(json.loads(job))
77
90
 
@@ -106,7 +119,7 @@ async def scan(payload: ScanRequest, request: Request) -> JSONResponse:
106
119
  results = await use_case.execute()
107
120
 
108
121
  # Send a message to the frontend by ws
109
- await app.state.ws_manager.broadcast({
122
+ await ws_manager.broadcast({
110
123
  "type": "log",
111
124
  "level": "success",
112
125
  "message": f"Scan completato in {time.perf_counter() - start_time:.2f} secondi",
@@ -131,7 +144,7 @@ async def scan(payload: ScanRequest, request: Request) -> JSONResponse:
131
144
 
132
145
  if new_job_list:
133
146
  # Save the new job_list
134
- await app.state.job.create_job_list(
147
+ await job_repo.create_job_list(
135
148
  job_id=job_list_id,
136
149
  job_list=new_job_list
137
150
  )
@@ -6,6 +6,7 @@ from fastapi.responses import JSONResponse
6
6
 
7
7
  from unit3dwup.services.itt_tracker_service import ITTtrackerService
8
8
  from unit3dwup.services.interfaces import TrackerServiceInterface
9
+ from unit3dwup.config.limiter import limiter
9
10
 
10
11
  from unit3dwup.schemas import FilterRequest
11
12
 
@@ -13,6 +14,7 @@ router = APIRouter()
13
14
 
14
15
 
15
16
  @router.post("/filter")
17
+ @limiter.limit("5/minute")
16
18
  async def filter_search(payload: FilterRequest, request: Request):
17
19
  """
18
20
  Search words or title in the tracker
@@ -3,23 +3,34 @@ import inspect
3
3
  import json
4
4
  import os
5
5
 
6
- from fastapi import APIRouter, Request, status
6
+ from fastapi import APIRouter, Request, status, Depends
7
7
  from fastapi.responses import JSONResponse
8
8
 
9
9
  from dotenv import load_dotenv, dotenv_values
10
10
 
11
11
  from unit3dwup.config import get_settings
12
- from unit3dwup.config import get_logger
12
+ from unit3dwup.config import get_logger, Settings
13
13
 
14
14
  from unit3dwup.services.lifespan_service import update_mounted_paths
15
15
 
16
16
  from unit3dwup.schemas import SetEnvRequest
17
17
 
18
- router = APIRouter()
19
18
 
19
+ from unit3dwup.external.websocket import WebSocketManager
20
+ from unit3dwup.routers.dependencies import (
21
+ load_settings,
22
+ get_job_repo,
23
+ get_ws_manager,
24
+ )
25
+
26
+ router = APIRouter()
20
27
 
21
28
  @router.post("/setting")
22
- async def configuration(request: Request):
29
+ async def configuration(request: Request,
30
+ settings: Settings = Depends(load_settings),
31
+ job_repo=Depends(get_job_repo),
32
+ ws_manager: WebSocketManager = Depends(get_ws_manager),
33
+ ):
23
34
  """
24
35
  Load setting from the local configuration file
25
36
 
@@ -30,33 +41,32 @@ async def configuration(request: Request):
30
41
  - none
31
42
  """
32
43
 
33
- app = request.app
34
-
35
44
  # Logger
36
45
  frame = inspect.currentframe()
37
46
  logger = get_logger(frame.f_code.co_name)
38
47
 
39
48
  # Load json settings and return it to the client
40
- job_data = await app.state.job.get_job(job_id='0')
49
+ job_data = await job_repo.get_job(job_id='0')
50
+
41
51
 
42
52
  # /// Check main paths
43
- if app.state.settings.prefs.SCAN_PATH == os.getcwd():
53
+ if settings.prefs.SCAN_PATH == os.getcwd():
44
54
  logger.warning("SCAN PATHS NO SET")
45
- await app.state.ws_manager.broadcast({
55
+ await ws_manager.broadcast({
46
56
  "type": "log",
47
57
  "level": "warn",
48
58
  "message": f"{frame.f_code.co_name} Scan Path not set",
49
59
  })
50
- if app.state.settings.prefs.TORRENT_ARCHIVE_PATH == os.getcwd():
60
+ if settings.prefs.TORRENT_ARCHIVE_PATH == os.getcwd():
51
61
  logger.warning("TORRENT ARCHIVE PATH NOT SET")
52
- await app.state.ws_manager.broadcast({
62
+ await ws_manager.broadcast({
53
63
  "type": "log",
54
64
  "level": "warn",
55
65
  "message": f"{frame.f_code.co_name} Torrent archive path not set",
56
66
  })
57
- if os.getcwd() in [app.state.settings.prefs.WATCHER_DESTINATION_PATH, app.state.settings.prefs.WATCHER_PATH]:
67
+ if os.getcwd() in [settings.prefs.WATCHER_DESTINATION_PATH, settings.prefs.WATCHER_PATH]:
58
68
  logger.warning("WATCHER PATHS NO SET")
59
- await app.state.ws_manager.broadcast({
69
+ await ws_manager.broadcast({
60
70
  "type": "log",
61
71
  "level": "warn",
62
72
  "message": f"{frame.f_code.co_name} Watcher Paths not set",
@@ -72,7 +82,11 @@ async def configuration(request: Request):
72
82
 
73
83
 
74
84
  @router.post("/setenv")
75
- async def set_env(payload: SetEnvRequest, request: Request):
85
+ async def set_env(payload: SetEnvRequest, request: Request,
86
+ settings: Settings = Depends(load_settings),
87
+ job_repo=Depends(get_job_repo),
88
+ ws_manager: WebSocketManager = Depends(get_ws_manager),
89
+ ):
76
90
  """
77
91
  Set the environment variables from the frontend
78
92
  User need to restart the docker
@@ -95,7 +109,7 @@ async def set_env(payload: SetEnvRequest, request: Request):
95
109
  # Permission denied when tries to create a temporary file but it fails because the container is not running as root
96
110
  # set_key(str(app.state.env_file), payload.key, payload.value)
97
111
 
98
- # Current variabiles value
112
+ # Current variables value
99
113
  env_vars = dotenv_values(str(app.state.env_file))
100
114
  # update the key
101
115
  env_vars[payload.key] = payload.value
@@ -110,11 +124,8 @@ async def set_env(payload: SetEnvRequest, request: Request):
110
124
  # Clear cache and reload
111
125
  get_settings.cache_clear()
112
126
 
113
- # Refresh the lru cache
114
- settings = get_settings()
115
-
116
- # Update the fast api app state
117
- app.state.settings = settings
127
+ # Call and Refresh the lru cache
128
+ app.state.settings = get_settings()
118
129
 
119
130
  # This key requires a Docker restart
120
131
  if payload.key.upper() in ["PREFS__WATCHER_DESTINATION_PATH", "PREFS__WATCHER_PATH", "PREFS__TORRENT_ARCHIVE_PATH",
@@ -125,7 +136,8 @@ async def set_env(payload: SetEnvRequest, request: Request):
125
136
  await update_mounted_paths(app=app)
126
137
 
127
138
  # Ricreate profile (overwrite -> it uses hset command)
128
- await app.state.job.create_profile(dict(settings.prefs))
139
+ await job_repo.create_profile(dict(settings.prefs))
140
+
129
141
 
130
142
  # Logger
131
143
  frame = inspect.currentframe()
@@ -135,7 +147,7 @@ async def set_env(payload: SetEnvRequest, request: Request):
135
147
  logger.info(f"-> Update {payload.key} value -> {payload.value}\n")
136
148
 
137
149
  # Send log to the client
138
- await app.state.ws_manager.broadcast({
150
+ await ws_manager.broadcast({
139
151
  "type": "log",
140
152
  "level": "success",
141
153
  "message": f"-> Update {payload.key} value -> {payload.value}\n",
@@ -2,14 +2,24 @@
2
2
  from fastapi import FastAPI
3
3
  from fastapi.middleware.cors import CORSMiddleware
4
4
 
5
+ from slowapi.middleware import SlowAPIMiddleware
6
+ from slowapi.errors import RateLimitExceeded
7
+ from slowapi import _rate_limit_exceeded_handler
8
+
5
9
  from unit3dwup.lifespan import lifespan
6
10
 
7
11
  from unit3dwup.routers import scan, process, jobs, posters, settings, search, ws
8
12
 
13
+ # Set a limiter
14
+ from config.limiter import limiter
9
15
 
10
16
  # Initialize FastApi
11
17
  app = FastAPI(lifespan=lifespan)
12
18
 
19
+ app.state.limiter = limiter
20
+ app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
21
+ app.add_middleware(SlowAPIMiddleware)
22
+
13
23
  # TODO middleware
14
24
  app.add_middleware(
15
25
  CORSMiddleware,
File without changes
File without changes