unit3dprep 1.0.4__tar.gz → 1.0.5__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 (57) hide show
  1. {unit3dprep-1.0.4/unit3dprep.egg-info → unit3dprep-1.0.5}/PKG-INFO +6 -1
  2. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/README.md +5 -0
  3. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/pyproject.toml +1 -1
  4. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/api/version.py +73 -6
  5. unit3dprep-1.0.5/unit3dprep/web/dist/assets/index-BlAGpoR4.js +255 -0
  6. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/dist/index.html +1 -1
  7. {unit3dprep-1.0.4 → unit3dprep-1.0.5/unit3dprep.egg-info}/PKG-INFO +6 -1
  8. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep.egg-info/SOURCES.txt +1 -1
  9. unit3dprep-1.0.4/unit3dprep/web/dist/assets/index-BizNr_oP.js +0 -255
  10. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/LICENSE +0 -0
  11. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/MANIFEST.in +0 -0
  12. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/setup.cfg +0 -0
  13. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/__init__.py +0 -0
  14. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/cli.py +0 -0
  15. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/core.py +0 -0
  16. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/i18n.py +0 -0
  17. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/media.py +0 -0
  18. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/upload.py +0 -0
  19. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/__init__.py +0 -0
  20. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/_env.py +0 -0
  21. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/api/__init__.py +0 -0
  22. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/api/auth.py +0 -0
  23. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/api/fs.py +0 -0
  24. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/api/library.py +0 -0
  25. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/api/logs.py +0 -0
  26. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/api/queue.py +0 -0
  27. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/api/quickupload.py +0 -0
  28. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/api/search.py +0 -0
  29. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/api/settings.py +0 -0
  30. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/api/tmdb.py +0 -0
  31. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/api/trackers.py +0 -0
  32. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/api/uploaded.py +0 -0
  33. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/api/webup.py +0 -0
  34. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/api/wizard.py +0 -0
  35. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/app.py +0 -0
  36. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/auth.py +0 -0
  37. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/clients.py +0 -0
  38. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/config.py +0 -0
  39. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/db.py +0 -0
  40. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/dist/assets/JetBrainsMono-Italic-VariableFont_wght-CZO9PUqx.ttf +0 -0
  41. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/dist/assets/JetBrainsMono-VariableFont_wght-BrlcHZ7m.ttf +0 -0
  42. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/dist/assets/SpaceGrotesk-VariableFont_wght-DIScfSlK.ttf +0 -0
  43. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/dist/assets/index-DChRHChM.css +0 -0
  44. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/duplicate_check.py +0 -0
  45. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/lang_cache.py +0 -0
  46. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/logbuf.py +0 -0
  47. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/tmdb_cache.py +0 -0
  48. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/trackers.py +0 -0
  49. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/webup_client.py +0 -0
  50. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/webup_job_fix.py +0 -0
  51. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/webup_logclass.py +0 -0
  52. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/webup_orchestrator.py +0 -0
  53. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep/web/webup_ws.py +0 -0
  54. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep.egg-info/dependency_links.txt +0 -0
  55. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep.egg-info/entry_points.txt +0 -0
  56. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/unit3dprep.egg-info/requires.txt +0 -0
  57. {unit3dprep-1.0.4 → unit3dprep-1.0.5}/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.4
3
+ Version: 1.0.5
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
  ![GitHub Actions](https://img.shields.io/badge/GitHub_Actions-2088FF?style=for-the-badge&logo=github-actions&logoColor=white)
723
723
  ![GitHub Pages](https://img.shields.io/badge/GitHub%20Pages-222222?style=for-the-badge&logo=github%20Pages&logoColor=white)
724
724
 
725
+ [![PyPI](https://img.shields.io/pypi/v/unit3dprep?style=for-the-badge&logo=pypi&logoColor=white&label=PyPI)](https://pypi.org/project/unit3dprep/)
726
+ [![Docker Hub](https://img.shields.io/docker/v/hashdeveloper512/unit3dprep?style=for-the-badge&logo=docker&logoColor=white&label=Docker%20Hub&sort=semver)](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
  ![GitHub Actions](https://img.shields.io/badge/GitHub_Actions-2088FF?style=for-the-badge&logo=github-actions&logoColor=white)
9
9
  ![GitHub Pages](https://img.shields.io/badge/GitHub%20Pages-222222?style=for-the-badge&logo=github%20Pages&logoColor=white)
10
10
 
11
+ [![PyPI](https://img.shields.io/pypi/v/unit3dprep?style=for-the-badge&logo=pypi&logoColor=white&label=PyPI)](https://pypi.org/project/unit3dprep/)
12
+ [![Docker Hub](https://img.shields.io/docker/v/hashdeveloper512/unit3dprep?style=for-the-badge&logo=docker&logoColor=white&label=Docker%20Hub&sort=semver)](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.4"
7
+ version = "1.0.5"
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
- if not _webup_can_update():
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
- yield _sse("log", f"restarting systemd unit {_systemd_unit()}…")
754
- yield _sse("done", {"ok": True, "target": "app", "from": before, "to": after, "mode": "pip"})
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)