unit3dprep 1.1.0__tar.gz → 1.1.2__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 (59) hide show
  1. {unit3dprep-1.1.0/unit3dprep.egg-info → unit3dprep-1.1.2}/PKG-INFO +1 -1
  2. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/pyproject.toml +1 -1
  3. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/api/reseed.py +24 -1
  4. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/clients.py +31 -12
  5. unit3dprep-1.1.2/unit3dprep/web/dist/assets/index-C7oz5Og_.js +255 -0
  6. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/dist/index.html +1 -1
  7. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/reseed.py +244 -56
  8. {unit3dprep-1.1.0 → unit3dprep-1.1.2/unit3dprep.egg-info}/PKG-INFO +1 -1
  9. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep.egg-info/SOURCES.txt +1 -1
  10. unit3dprep-1.1.0/unit3dprep/web/dist/assets/index-BkzkykEO.js +0 -255
  11. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/LICENSE +0 -0
  12. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/MANIFEST.in +0 -0
  13. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/README.md +0 -0
  14. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/setup.cfg +0 -0
  15. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/__init__.py +0 -0
  16. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/cli.py +0 -0
  17. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/core.py +0 -0
  18. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/i18n.py +0 -0
  19. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/media.py +0 -0
  20. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/upload.py +0 -0
  21. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/__init__.py +0 -0
  22. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/_env.py +0 -0
  23. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/api/__init__.py +0 -0
  24. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/api/auth.py +0 -0
  25. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/api/fs.py +0 -0
  26. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/api/library.py +0 -0
  27. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/api/logs.py +0 -0
  28. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/api/queue.py +0 -0
  29. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/api/quickupload.py +0 -0
  30. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/api/search.py +0 -0
  31. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/api/settings.py +0 -0
  32. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/api/tmdb.py +0 -0
  33. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/api/trackers.py +0 -0
  34. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/api/uploaded.py +0 -0
  35. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/api/version.py +0 -0
  36. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/api/webup.py +0 -0
  37. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/api/wizard.py +0 -0
  38. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/app.py +0 -0
  39. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/auth.py +0 -0
  40. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/config.py +0 -0
  41. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/db.py +0 -0
  42. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/dist/assets/JetBrainsMono-Italic-VariableFont_wght-CZO9PUqx.ttf +0 -0
  43. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/dist/assets/JetBrainsMono-VariableFont_wght-BrlcHZ7m.ttf +0 -0
  44. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/dist/assets/SpaceGrotesk-VariableFont_wght-DIScfSlK.ttf +0 -0
  45. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/dist/assets/index-FFoOmpDN.css +0 -0
  46. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/duplicate_check.py +0 -0
  47. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/lang_cache.py +0 -0
  48. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/logbuf.py +0 -0
  49. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/tmdb_cache.py +0 -0
  50. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/trackers.py +0 -0
  51. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/webup_client.py +0 -0
  52. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/webup_job_fix.py +0 -0
  53. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/webup_logclass.py +0 -0
  54. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/webup_orchestrator.py +0 -0
  55. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep/web/webup_ws.py +0 -0
  56. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep.egg-info/dependency_links.txt +0 -0
  57. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep.egg-info/entry_points.txt +0 -0
  58. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep.egg-info/requires.txt +0 -0
  59. {unit3dprep-1.1.0 → unit3dprep-1.1.2}/unit3dprep.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: unit3dprep
3
- Version: 1.1.0
3
+ Version: 1.1.2
4
4
  Summary: Web UI + CLI di pre-flight per tracker Unit3D, companion di Unit3DWebUp (audio ITA, nomenclatura ItaTorrents, hardlink, upload)
5
5
  Author: Davide Sidoti
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "unit3dprep"
7
- version = "1.1.0"
7
+ version = "1.1.2"
8
8
  description = "Web UI + CLI di pre-flight per tracker Unit3D, companion di Unit3DWebUp (audio ITA, nomenclatura ItaTorrents, hardlink, upload)"
9
9
  readme = "README.md"
10
10
  license = { file = "LICENSE" }
@@ -23,7 +23,13 @@ from ...i18n import get_request_lang, t as _i18n_t
23
23
  from ...media import media_root, seedings_root
24
24
  from .. import config as web_config
25
25
  from ..logbuf import emit as log_emit
26
- from ..reseed import perform_reseed, stream_reseed_candidates, suggest_local_files
26
+ from ..reseed import (
27
+ perform_reseed,
28
+ reseed_search,
29
+ stream_reseed_candidates,
30
+ stream_reseed_search,
31
+ suggest_local_files,
32
+ )
27
33
 
28
34
  router = APIRouter(prefix="/api", tags=["reseed"])
29
35
 
@@ -85,6 +91,23 @@ async def reseed_suggest(torrent_id: int):
85
91
  return JSONResponse(await suggest_local_files(cfg, torrent_id))
86
92
 
87
93
 
94
+ @router.get("/reseed/search")
95
+ async def reseed_search_ep(q: str, category: str = ""):
96
+ cfg = web_config.load()
97
+ return JSONResponse(await reseed_search(cfg, q, category or None))
98
+
99
+
100
+ @router.get("/reseed/search/stream")
101
+ async def reseed_search_stream(q: str, category: str = ""):
102
+ cfg = web_config.load()
103
+
104
+ async def generate() -> AsyncGenerator[dict, None]:
105
+ async for kind, data in stream_reseed_search(cfg, q, category or None):
106
+ yield {"event": kind, "data": json.dumps(data)}
107
+
108
+ return EventSourceResponse(generate())
109
+
110
+
88
111
  # ---------------------------------------------------------------------------
89
112
  # Reseed run
90
113
  # ---------------------------------------------------------------------------
@@ -141,11 +141,8 @@ class QBittorrentClient(TorrentClient):
141
141
  return out
142
142
 
143
143
  async def reseed(self, torrent_hash: str) -> None:
144
- cli = await self._http()
145
- r = await cli.post("/api/v2/torrents/recheck", data={"hashes": torrent_hash})
146
- r.raise_for_status()
147
- r2 = await cli.post("/api/v2/torrents/resume", data={"hashes": torrent_hash})
148
- r2.raise_for_status()
144
+ await self.recheck(torrent_hash)
145
+ await self.resume(torrent_hash)
149
146
 
150
147
  async def add_torrent(
151
148
  self,
@@ -168,9 +165,14 @@ class QBittorrentClient(TorrentClient):
168
165
  before/after.
169
166
  """
170
167
  cli = await self._http()
168
+ flag = "true" if paused else "false"
171
169
  data: dict[str, str] = {
172
170
  "savepath": save_path,
173
- "paused": "true" if paused else "false",
171
+ # qBit 5.0 renamed the add param `paused` `stopped`; send both so
172
+ # the torrent is correctly added stopped on either version (the
173
+ # unknown one is ignored).
174
+ "paused": flag,
175
+ "stopped": flag,
174
176
  "skip_checking": "true" if skip_checking else "false",
175
177
  "autoTMM": "false",
176
178
  }
@@ -206,20 +208,37 @@ class QBittorrentClient(TorrentClient):
206
208
  return data[0]
207
209
  return None
208
210
 
211
+ async def _torrent_action(self, paths: list[str], data: dict[str, str]) -> None:
212
+ """POST a torrent action tolerating the qBittorrent 5.0 endpoint rename
213
+ (resume→start, pause→stop): try each path in order, treat a 404 as
214
+ "wrong version, try the next", and surface any other error."""
215
+ cli = await self._http()
216
+ last: httpx.Response | None = None
217
+ for path in paths:
218
+ r = await cli.post(path, data=data)
219
+ if r.status_code != 404:
220
+ r.raise_for_status()
221
+ return
222
+ last = r
223
+ if last is not None:
224
+ last.raise_for_status()
225
+
209
226
  async def recheck(self, torrent_hash: str) -> None:
210
227
  cli = await self._http()
211
228
  r = await cli.post("/api/v2/torrents/recheck", data={"hashes": torrent_hash})
212
229
  r.raise_for_status()
213
230
 
214
231
  async def resume(self, torrent_hash: str) -> None:
215
- cli = await self._http()
216
- r = await cli.post("/api/v2/torrents/resume", data={"hashes": torrent_hash})
217
- r.raise_for_status()
232
+ await self._torrent_action(
233
+ ["/api/v2/torrents/start", "/api/v2/torrents/resume"],
234
+ {"hashes": torrent_hash},
235
+ )
218
236
 
219
237
  async def pause(self, torrent_hash: str) -> None:
220
- cli = await self._http()
221
- r = await cli.post("/api/v2/torrents/pause", data={"hashes": torrent_hash})
222
- r.raise_for_status()
238
+ await self._torrent_action(
239
+ ["/api/v2/torrents/stop", "/api/v2/torrents/pause"],
240
+ {"hashes": torrent_hash},
241
+ )
223
242
 
224
243
  async def remove(self, torrent_hash: str, delete_files: bool = False) -> None:
225
244
  cli = await self._http()