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.
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/PKG-INFO +2 -1
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/Unit3DwebUp.egg-info/PKG-INFO +2 -1
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/Unit3DwebUp.egg-info/SOURCES.txt +2 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/Unit3DwebUp.egg-info/requires.txt +1 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/pyproject.toml +2 -1
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/config/__init__.py +2 -2
- unit3dwebup-0.0.27/unit3dwup/config/limiter.py +6 -0
- unit3dwebup-0.0.27/unit3dwup/routers/dependencies.py +22 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/routers/jobs.py +18 -9
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/routers/posters.py +6 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/routers/process.py +20 -5
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/routers/scan.py +22 -9
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/routers/search.py +2 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/routers/settings.py +34 -22
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/start.py +10 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/README.md +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/Unit3DwebUp.egg-info/dependency_links.txt +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/Unit3DwebUp.egg-info/entry_points.txt +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/Unit3DwebUp.egg-info/top_level.txt +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/setup.cfg +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/__init__.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/config/api_data.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/config/constants.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/config/host_data.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/config/itt.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/config/logger.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/config/settings.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/config/sis.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/config/tags.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/config/trackers.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/external/__init__.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/external/async_http_client_service.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/external/websocket.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/lifespan.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/models/__init__.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/models/interfaces.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/models/keywords.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/models/media.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/models/media_info.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/models/movie.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/models/subtitles.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/models/tv.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/models/tvdb_search.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/models/videos.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/repositories/__init__.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/repositories/db_online.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/repositories/interfaces.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/repositories/job_repos.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/repositories/media_info_factory.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/routers/__init__.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/routers/ws.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/schemas.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/services/__init__.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/services/auto_async_service.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/services/create_torrent_service.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/services/interfaces.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/services/itt_tracker_helper.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/services/itt_tracker_service.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/services/lifespan_service.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/services/media_service.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/services/tags_service.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/services/tmdb.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/services/torrent_client_service.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/services/torrent_service.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/services/tvdb.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/services/utility.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/services/video_service.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/start_backup.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/use_case/__init__.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/use_case/make_torrent_usecase.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/use_case/process_all_usecase.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/use_case/scan_media_usecase.py +0 -0
- {unit3dwebup-0.0.26 → unit3dwebup-0.0.27}/unit3dwup/use_case/seed_usecase.py +0 -0
- {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.
|
|
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.
|
|
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
|
|
@@ -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.
|
|
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.
|
|
10
|
+
__version__ = "0.0.27"
|
|
@@ -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
|
-
|
|
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
|
|
31
|
-
results = [json.loads(await
|
|
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
|
|
48
|
+
await job_repo.delete_job_list(job_id=payload.job_list_id)
|
|
40
49
|
|
|
41
50
|
if results:
|
|
42
|
-
await
|
|
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
|
|
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
|
-
|
|
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=
|
|
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
|
-
|
|
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=
|
|
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,
|
|
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
|
-
|
|
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(
|
|
66
|
-
logger.info(f"Current joblist_id {job_list_id} {
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
49
|
+
job_data = await job_repo.get_job(job_id='0')
|
|
50
|
+
|
|
41
51
|
|
|
42
52
|
# /// Check main paths
|
|
43
|
-
if
|
|
53
|
+
if settings.prefs.SCAN_PATH == os.getcwd():
|
|
44
54
|
logger.warning("SCAN PATHS NO SET")
|
|
45
|
-
await
|
|
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
|
|
60
|
+
if settings.prefs.TORRENT_ARCHIVE_PATH == os.getcwd():
|
|
51
61
|
logger.warning("TORRENT ARCHIVE PATH NOT SET")
|
|
52
|
-
await
|
|
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 [
|
|
67
|
+
if os.getcwd() in [settings.prefs.WATCHER_DESTINATION_PATH, settings.prefs.WATCHER_PATH]:
|
|
58
68
|
logger.warning("WATCHER PATHS NO SET")
|
|
59
|
-
await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|