unit3dprep 1.0.4__tar.gz → 1.0.6__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.
- {unit3dprep-1.0.4/unit3dprep.egg-info → unit3dprep-1.0.6}/PKG-INFO +6 -1
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/README.md +5 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/pyproject.toml +1 -1
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/web/api/version.py +73 -6
- unit3dprep-1.0.6/unit3dprep/web/dist/assets/index-DwUQS7yk.js +255 -0
- unit3dprep-1.0.6/unit3dprep/web/dist/assets/index-FFoOmpDN.css +1 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/web/dist/index.html +2 -2
- {unit3dprep-1.0.4 → unit3dprep-1.0.6/unit3dprep.egg-info}/PKG-INFO +6 -1
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep.egg-info/SOURCES.txt +2 -2
- unit3dprep-1.0.4/unit3dprep/web/dist/assets/index-BizNr_oP.js +0 -255
- unit3dprep-1.0.4/unit3dprep/web/dist/assets/index-DChRHChM.css +0 -1
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/LICENSE +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/MANIFEST.in +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/setup.cfg +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/__init__.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/cli.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/core.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/i18n.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/media.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/upload.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/web/__init__.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/web/_env.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/web/api/__init__.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/web/api/auth.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/web/api/fs.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/web/api/library.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/web/api/logs.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/web/api/queue.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/web/api/quickupload.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/web/api/search.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/web/api/settings.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/web/api/tmdb.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/web/api/trackers.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/web/api/uploaded.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/web/api/webup.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/web/api/wizard.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/web/app.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/web/auth.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/web/clients.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/web/config.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/web/db.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/web/dist/assets/JetBrainsMono-Italic-VariableFont_wght-CZO9PUqx.ttf +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/web/dist/assets/JetBrainsMono-VariableFont_wght-BrlcHZ7m.ttf +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/web/dist/assets/SpaceGrotesk-VariableFont_wght-DIScfSlK.ttf +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/web/duplicate_check.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/web/lang_cache.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/web/logbuf.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/web/tmdb_cache.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/web/trackers.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/web/webup_client.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/web/webup_job_fix.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/web/webup_logclass.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/web/webup_orchestrator.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep/web/webup_ws.py +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep.egg-info/dependency_links.txt +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep.egg-info/entry_points.txt +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/unit3dprep.egg-info/requires.txt +0 -0
- {unit3dprep-1.0.4 → unit3dprep-1.0.6}/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.0.
|
|
3
|
+
Version: 1.0.6
|
|
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
|
|
@@ -722,6 +722,9 @@ Dynamic: license-file
|
|
|
722
722
|

|
|
723
723
|

|
|
724
724
|
|
|
725
|
+
[](https://pypi.org/project/unit3dprep/)
|
|
726
|
+
[](https://hub.docker.com/r/hashdeveloper512/unit3dprep)
|
|
727
|
+
|
|
725
728
|
Web UI + CLI di pre-flight per tracker Unit3D, accoppiata via HTTP a [`Unit3DWebUp`](https://pypi.org/project/Unit3DwebUp/) come backend di upload.
|
|
726
729
|
Verifica tracce audio italiane, rinomina secondo la [nomenclatura ItaTorrents](docs/nomenclatura.md), crea hardlink in `~/seedings/` e orchestra il flusso `setenv → scan → maketorrent → upload → seed` su `Unit3DWebUp` con log live via WebSocket/SSE.
|
|
727
730
|
|
|
@@ -777,6 +780,8 @@ docker compose up -d
|
|
|
777
780
|
# apri http://127.0.0.1:8765
|
|
778
781
|
```
|
|
779
782
|
|
|
783
|
+
> 🐳 Immagine pubblicata su Docker Hub: [`hashdeveloper512/unit3dprep`](https://hub.docker.com/r/hashdeveloper512/unit3dprep). Per saltare la build locale, in `docker-compose.yml` usa `image: hashdeveloper512/unit3dprep:latest` al posto di `build: .` → `docker compose up -d` la scarica.
|
|
784
|
+
|
|
780
785
|
> Serve **Compose v2** (`docker compose`). La vecchia `docker-compose` v1 (1.29.2) crasha su `up` con Docker Engine 25+ (`KeyError: 'ContainerConfig'`). Se `docker compose version` non funziona, installa il plugin v2 (binario utente, senza repo): vedi [Deploy › Docker › §1](https://davidesidoti.github.io/unit3dprep/docker/#1-prerequisiti).
|
|
781
786
|
|
|
782
787
|
Guida completa: [Deploy › Docker](https://davidesidoti.github.io/unit3dprep/docker/).
|
|
@@ -8,6 +8,9 @@
|
|
|
8
8
|

|
|
9
9
|

|
|
10
10
|
|
|
11
|
+
[](https://pypi.org/project/unit3dprep/)
|
|
12
|
+
[](https://hub.docker.com/r/hashdeveloper512/unit3dprep)
|
|
13
|
+
|
|
11
14
|
Web UI + CLI di pre-flight per tracker Unit3D, accoppiata via HTTP a [`Unit3DWebUp`](https://pypi.org/project/Unit3DwebUp/) come backend di upload.
|
|
12
15
|
Verifica tracce audio italiane, rinomina secondo la [nomenclatura ItaTorrents](docs/nomenclatura.md), crea hardlink in `~/seedings/` e orchestra il flusso `setenv → scan → maketorrent → upload → seed` su `Unit3DWebUp` con log live via WebSocket/SSE.
|
|
13
16
|
|
|
@@ -63,6 +66,8 @@ docker compose up -d
|
|
|
63
66
|
# apri http://127.0.0.1:8765
|
|
64
67
|
```
|
|
65
68
|
|
|
69
|
+
> 🐳 Immagine pubblicata su Docker Hub: [`hashdeveloper512/unit3dprep`](https://hub.docker.com/r/hashdeveloper512/unit3dprep). Per saltare la build locale, in `docker-compose.yml` usa `image: hashdeveloper512/unit3dprep:latest` al posto di `build: .` → `docker compose up -d` la scarica.
|
|
70
|
+
|
|
66
71
|
> Serve **Compose v2** (`docker compose`). La vecchia `docker-compose` v1 (1.29.2) crasha su `up` con Docker Engine 25+ (`KeyError: 'ContainerConfig'`). Se `docker compose version` non funziona, installa il plugin v2 (binario utente, senza repo): vedi [Deploy › Docker › §1](https://davidesidoti.github.io/unit3dprep/docker/#1-prerequisiti).
|
|
67
72
|
|
|
68
73
|
Guida completa: [Deploy › Docker](https://davidesidoti.github.io/unit3dprep/docker/).
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "unit3dprep"
|
|
7
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.6"
|
|
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" }
|
|
@@ -249,8 +249,9 @@ async def _compute_info() -> dict:
|
|
|
249
249
|
"installed": webup_current is not None,
|
|
250
250
|
"repo_path": str(_webup_repo_path()),
|
|
251
251
|
},
|
|
252
|
-
"can_update_app": _systemd_available(),
|
|
253
|
-
"can_update_webup": _webup_can_update(),
|
|
252
|
+
"can_update_app": _systemd_available() or _in_docker(),
|
|
253
|
+
"can_update_webup": _webup_can_update() or (_in_docker() and webup_current is not None),
|
|
254
|
+
"docker": _in_docker(),
|
|
254
255
|
}
|
|
255
256
|
|
|
256
257
|
|
|
@@ -307,6 +308,37 @@ def _install_mode() -> str:
|
|
|
307
308
|
return "pip"
|
|
308
309
|
|
|
309
310
|
|
|
311
|
+
def _in_docker() -> bool:
|
|
312
|
+
"""True when running inside the all-in-one Docker image.
|
|
313
|
+
|
|
314
|
+
Primary signal is the `U3DP_IN_DOCKER` env var baked into the Dockerfile;
|
|
315
|
+
`/.dockerenv` is a robust fallback for images run with the env stripped.
|
|
316
|
+
In Docker there is no systemd, so updates restart the container instead
|
|
317
|
+
(see `_schedule_container_restart`).
|
|
318
|
+
"""
|
|
319
|
+
if str(runtime_setting("U3DP_IN_DOCKER", "")).strip().lower() in ("1", "true", "yes", "on"):
|
|
320
|
+
return True
|
|
321
|
+
return Path("/.dockerenv").exists()
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def _schedule_container_restart() -> None:
|
|
325
|
+
"""Restart the container by sending SIGTERM to PID 1 after a short delay.
|
|
326
|
+
|
|
327
|
+
In the all-in-one image `unit3dprep-web` (uvicorn) is exec'd as PID 1, so
|
|
328
|
+
SIGTERM triggers a graceful uvicorn shutdown → container exit → Docker's
|
|
329
|
+
`restart: unless-stopped` policy reboots the whole stack with the freshly
|
|
330
|
+
pip-installed version. The 3s delay lets the SSE stream emit `done` before
|
|
331
|
+
the teardown. Detached so it outlives this request handler.
|
|
332
|
+
"""
|
|
333
|
+
subprocess.Popen(
|
|
334
|
+
["sh", "-c", "sleep 3; kill -TERM 1"],
|
|
335
|
+
start_new_session=True,
|
|
336
|
+
stdin=subprocess.DEVNULL,
|
|
337
|
+
stdout=subprocess.DEVNULL,
|
|
338
|
+
stderr=subprocess.DEVNULL,
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
|
|
310
342
|
async def _get_info(force: bool = False) -> dict:
|
|
311
343
|
now = time.time()
|
|
312
344
|
if not force and _cache["data"] and (now - _cache["at"]) < _CACHE_TTL:
|
|
@@ -410,7 +442,9 @@ async def update_webup():
|
|
|
410
442
|
)
|
|
411
443
|
yield _sse("start", {"target": "webup", "current": before})
|
|
412
444
|
|
|
413
|
-
|
|
445
|
+
# In Docker we restart the container instead of a systemd unit, so the
|
|
446
|
+
# only requirement is a python interpreter with Unit3DwebUp installed.
|
|
447
|
+
if not _webup_can_update() and not (_in_docker() and Path(_webup_python()).exists()):
|
|
414
448
|
py = _webup_python()
|
|
415
449
|
if not Path(py).exists():
|
|
416
450
|
yield _sse("error", {"message": f"webup python not found at {py} (set WEBUP_VENV_BIN)"})
|
|
@@ -450,6 +484,23 @@ async def update_webup():
|
|
|
450
484
|
break
|
|
451
485
|
yield ev
|
|
452
486
|
|
|
487
|
+
# In Docker webup runs as a subprocess of the entrypoint, so restarting
|
|
488
|
+
# the container (SIGTERM to PID 1) reboots webup with the new version.
|
|
489
|
+
if _in_docker():
|
|
490
|
+
try:
|
|
491
|
+
_schedule_container_restart()
|
|
492
|
+
yield _sse("log", "container restart scheduled (SIGTERM to PID 1, 3s)")
|
|
493
|
+
except Exception as exc:
|
|
494
|
+
yield _sse("error", {"message": f"failed to schedule restart: {exc}"})
|
|
495
|
+
_cache["data"] = None
|
|
496
|
+
_cache["at"] = 0.0
|
|
497
|
+
after = _current_webup_pip_version() or _current_webup_repo_version()
|
|
498
|
+
yield _sse("done", {
|
|
499
|
+
"ok": True, "target": "webup", "from": before, "to": after,
|
|
500
|
+
"restart": "container",
|
|
501
|
+
})
|
|
502
|
+
return
|
|
503
|
+
|
|
453
504
|
# Restart webup service via systemd-run timer (fire-and-forget;
|
|
454
505
|
# outside our cgroup, won't be killed when this request finishes).
|
|
455
506
|
unit = _webup_systemd_unit()
|
|
@@ -532,7 +583,7 @@ async def update_app():
|
|
|
532
583
|
mode = _install_mode()
|
|
533
584
|
yield _sse("start", {"target": "app", "current": before, "mode": mode})
|
|
534
585
|
|
|
535
|
-
if not _systemd_available():
|
|
586
|
+
if not _systemd_available() and not _in_docker():
|
|
536
587
|
yield _sse("error", {"message": f"systemd unit '{_systemd_unit()}' not available"})
|
|
537
588
|
yield _sse("done", {"ok": False})
|
|
538
589
|
return
|
|
@@ -571,6 +622,16 @@ async def update_app():
|
|
|
571
622
|
|
|
572
623
|
await asyncio.sleep(1.5)
|
|
573
624
|
|
|
625
|
+
# In Docker there's no systemd: restart the container (SIGTERM to PID 1)
|
|
626
|
+
# so the auto-restart policy reboots the stack with the new version.
|
|
627
|
+
if _in_docker():
|
|
628
|
+
try:
|
|
629
|
+
_schedule_container_restart()
|
|
630
|
+
yield _sse("log", "container restart scheduled (SIGTERM to PID 1, 3s)")
|
|
631
|
+
except Exception as exc:
|
|
632
|
+
yield _sse("error", {"message": f"failed to schedule restart: {exc}"})
|
|
633
|
+
return
|
|
634
|
+
|
|
574
635
|
# Schedule the restart via a transient systemd-run timer so the restart
|
|
575
636
|
# command lives OUTSIDE this service's cgroup. A plain `Popen(...,
|
|
576
637
|
# start_new_session=True)` child stays in the parent unit's cgroup and
|
|
@@ -750,5 +811,11 @@ async def _update_app_from_pip() -> AsyncGenerator[dict, None]:
|
|
|
750
811
|
after = _current_app_version()
|
|
751
812
|
_cache["data"] = None
|
|
752
813
|
_cache["at"] = 0.0
|
|
753
|
-
|
|
754
|
-
|
|
814
|
+
if _in_docker():
|
|
815
|
+
yield _sse("log", "restarting container…")
|
|
816
|
+
else:
|
|
817
|
+
yield _sse("log", f"restarting systemd unit {_systemd_unit()}…")
|
|
818
|
+
done = {"ok": True, "target": "app", "from": before, "to": after, "mode": "pip"}
|
|
819
|
+
if _in_docker():
|
|
820
|
+
done["restart"] = "container"
|
|
821
|
+
yield _sse("done", done)
|