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.
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/PKG-INFO +2 -1
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/Unit3DwebUp.egg-info/PKG-INFO +2 -1
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/Unit3DwebUp.egg-info/SOURCES.txt +13 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/Unit3DwebUp.egg-info/requires.txt +1 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/pyproject.toml +2 -1
- {unit3dwebup-0.0.25 → 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/lifespan.py +154 -0
- {unit3dwebup-0.0.25/unit3dwup/services → unit3dwebup-0.0.27/unit3dwup/routers}/__init__.py +0 -0
- unit3dwebup-0.0.27/unit3dwup/routers/dependencies.py +22 -0
- unit3dwebup-0.0.27/unit3dwup/routers/jobs.py +63 -0
- unit3dwebup-0.0.27/unit3dwup/routers/posters.py +116 -0
- unit3dwebup-0.0.27/unit3dwup/routers/process.py +105 -0
- unit3dwebup-0.0.27/unit3dwup/routers/scan.py +159 -0
- unit3dwebup-0.0.27/unit3dwup/routers/search.py +49 -0
- unit3dwebup-0.0.27/unit3dwup/routers/settings.py +163 -0
- unit3dwebup-0.0.27/unit3dwup/routers/ws.py +42 -0
- unit3dwebup-0.0.27/unit3dwup/schemas.py +33 -0
- unit3dwebup-0.0.27/unit3dwup/services/__init__.py +0 -0
- unit3dwebup-0.0.27/unit3dwup/start.py +47 -0
- unit3dwebup-0.0.25/unit3dwup/start.py → unit3dwebup-0.0.27/unit3dwup/start_backup.py +47 -14
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/README.md +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/Unit3DwebUp.egg-info/dependency_links.txt +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/Unit3DwebUp.egg-info/entry_points.txt +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/Unit3DwebUp.egg-info/top_level.txt +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/setup.cfg +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/__init__.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/config/api_data.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/config/constants.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/config/host_data.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/config/itt.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/config/logger.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/config/settings.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/config/sis.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/config/tags.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/config/trackers.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/external/__init__.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/external/async_http_client_service.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/external/websocket.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/models/__init__.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/models/interfaces.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/models/keywords.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/models/media.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/models/media_info.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/models/movie.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/models/subtitles.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/models/tv.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/models/tvdb_search.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/models/videos.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/repositories/__init__.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/repositories/db_online.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/repositories/interfaces.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/repositories/job_repos.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/repositories/media_info_factory.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/services/auto_async_service.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/services/create_torrent_service.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/services/interfaces.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/services/itt_tracker_helper.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/services/itt_tracker_service.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/services/lifespan_service.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/services/media_service.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/services/tags_service.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/services/tmdb.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/services/torrent_client_service.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/services/torrent_service.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/services/tvdb.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/services/utility.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/services/video_service.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/use_case/__init__.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/use_case/make_torrent_usecase.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/use_case/process_all_usecase.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/use_case/scan_media_usecase.py +0 -0
- {unit3dwebup-0.0.25 → unit3dwebup-0.0.27}/unit3dwup/use_case/seed_usecase.py +0 -0
- {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.
|
|
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
|
|
@@ -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
|
|
@@ -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,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()
|
|
File without changes
|
|
@@ -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()
|