ytp-dl 0.6.3__py3-none-any.whl → 0.6.4__py3-none-any.whl

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.
scripts/api.py CHANGED
@@ -5,9 +5,9 @@ import os
5
5
  import shutil
6
6
  import tempfile
7
7
  import time
8
+ from threading import BoundedSemaphore, Lock
8
9
 
9
10
  from flask import Flask, request, send_file, jsonify
10
- from gevent.lock import Semaphore
11
11
 
12
12
  from .downloader import validate_environment, download_video
13
13
 
@@ -17,10 +17,15 @@ BASE_DOWNLOAD_DIR = os.environ.get("YTPDL_JOB_BASE_DIR", "/root/ytpdl_jobs")
17
17
  os.makedirs(BASE_DOWNLOAD_DIR, exist_ok=True)
18
18
 
19
19
  MAX_CONCURRENT = int(os.environ.get("YTPDL_MAX_CONCURRENT", "1"))
20
- _sem = Semaphore(MAX_CONCURRENT)
20
+
21
+ # Thread-safe concurrency gate (caps actual download jobs).
22
+ _sem = BoundedSemaphore(MAX_CONCURRENT)
23
+
24
+ # Track in-flight jobs for /healthz reporting.
25
+ _in_use = 0
26
+ _in_use_lock = Lock()
21
27
 
22
28
  # Failsafe: delete abandoned job dirs older than this many seconds.
23
- # (keep 21600 if you prefer; 3600 is fine too)
24
29
  STALE_JOB_TTL_S = int(os.environ.get("YTPDL_STALE_JOB_TTL_S", "3600"))
25
30
 
26
31
  _ALLOWED_EXTENSIONS = {"mp3", "mp4", "best"}
@@ -43,11 +48,28 @@ def _cleanup_stale_jobs() -> None:
43
48
  pass
44
49
 
45
50
 
51
+ def _try_acquire_job_slot() -> bool:
52
+ global _in_use
53
+ if not _sem.acquire(blocking=False):
54
+ return False
55
+ with _in_use_lock:
56
+ _in_use += 1
57
+ return True
58
+
59
+
60
+ def _release_job_slot() -> None:
61
+ global _in_use
62
+ with _in_use_lock:
63
+ if _in_use > 0:
64
+ _in_use -= 1
65
+ _sem.release()
66
+
67
+
46
68
  @app.route("/api/download", methods=["POST"])
47
69
  def handle_download():
48
70
  _cleanup_stale_jobs()
49
71
 
50
- if not _sem.acquire(blocking=False):
72
+ if not _try_acquire_job_slot():
51
73
  return jsonify(error="Server busy, try again later"), 503
52
74
 
53
75
  job_dir: str | None = None
@@ -57,7 +79,7 @@ def handle_download():
57
79
  nonlocal released
58
80
  if not released:
59
81
  released = True
60
- _sem.release()
82
+ _release_job_slot()
61
83
 
62
84
  try:
63
85
  data = request.get_json(force=True)
@@ -90,8 +112,7 @@ def handle_download():
90
112
  if not (filename and os.path.exists(filename)):
91
113
  raise RuntimeError("Download failed")
92
114
 
93
- # Release semaphore as soon as yt-dlp is done.
94
- # Streaming the file should not block the next download job.
115
+ # Release slot as soon as yt-dlp is done.
95
116
  _release_once()
96
117
 
97
118
  response = send_file(filename, as_attachment=True)
@@ -126,7 +147,9 @@ def handle_download():
126
147
 
127
148
  @app.route("/healthz", methods=["GET"])
128
149
  def healthz():
129
- return jsonify(ok=True, in_use=(MAX_CONCURRENT - _sem.counter), capacity=MAX_CONCURRENT), 200
150
+ with _in_use_lock:
151
+ in_use = _in_use
152
+ return jsonify(ok=True, in_use=in_use, capacity=MAX_CONCURRENT), 200
130
153
 
131
154
 
132
155
  def main():
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: ytp-dl
3
- Version: 0.6.3
3
+ Version: 0.6.4
4
4
  Summary: YouTube video downloader with Mullvad VPN integration and Flask API
5
5
  Home-page: https://github.com/yourusername/ytp-dl
6
6
  Author: dumgum82
@@ -20,7 +20,6 @@ Description-Content-Type: text/markdown
20
20
  Requires-Dist: yt-dlp[default]
21
21
  Requires-Dist: flask
22
22
  Requires-Dist: requests
23
- Requires-Dist: gevent
24
23
  Requires-Dist: gunicorn
25
24
  Dynamic: author
26
25
  Dynamic: author-email
@@ -58,7 +57,7 @@ Dynamic: summary
58
57
  ## 📦 Installation
59
58
 
60
59
  ```bash
61
- pip install ytp-dl==0.6.3 yt-dlp[default]
60
+ pip install ytp-dl==0.6.4 yt-dlp[default]
62
61
  ```
63
62
 
64
63
  **Requirements:**
@@ -249,9 +248,9 @@ sudo systemctl start ytp-dl-api
249
248
  * ✅ Installs Python, FFmpeg, and Mullvad CLI
250
249
  * ✅ Installs Deno system-wide (required by yt-dlp for modern YouTube extraction)
251
250
  * ✅ Creates virtualenv at `/opt/yt-dlp-mullvad/venv`
252
- * ✅ Installs `ytp-dl==0.6.3` + `yt-dlp[default]` into the virtualenv
251
+ * ✅ Installs `ytp-dl==0.6.4` + `yt-dlp[default]` into the virtualenv
253
252
  * ✅ Sets up systemd service on port 5000
254
- * ✅ Configures Gunicorn with gevent workers
253
+ * ✅ Configures Gunicorn with gthread (threaded) workers
255
254
 
256
255
  ```bash
257
256
  #!/usr/bin/env bash
@@ -261,7 +260,7 @@ sudo systemctl start ytp-dl-api
261
260
  # - Installs Python, ffmpeg, Mullvad CLI
262
261
  # - Installs Deno system-wide (JS runtime required for modern YouTube extraction via yt-dlp)
263
262
  # - Creates a virtualenv at /opt/yt-dlp-mullvad/venv
264
- # - Installs ytp-dl==0.6.3 + yt-dlp[default] + gunicorn + gevent in that venv
263
+ # - Installs ytp-dl==0.6.4 + yt-dlp[default] + gunicorn + gevent in that venv
265
264
  # - Creates a simple systemd service ytp-dl-api.service on port 5000
266
265
  #
267
266
  # Mullvad connect/disconnect is handled per-job by downloader.py.
@@ -276,6 +275,7 @@ VENV_DIR="${VENV_DIR:-${APP_DIR}/venv}" # python venv
276
275
  MV_ACCOUNT="${MV_ACCOUNT:-}" # Mullvad account (put number after -)
277
276
  YTPDL_MAX_CONCURRENT="${YTPDL_MAX_CONCURRENT:-1}" # API concurrency cap
278
277
  YTPDL_MULLVAD_LOCATION="${YTPDL_MULLVAD_LOCATION:-us}" # default Mullvad relay hint
278
+ GUNICORN_THREADS="${GUNICORN_THREADS:-4}" # keep API responsive on tiny VPS
279
279
  ### -------------------------------------------------------------------------
280
280
 
281
281
  [[ "${EUID}" -eq 0 ]] || { echo "Please run as root"; exit 1; }
@@ -297,6 +297,11 @@ fi
297
297
 
298
298
  mullvad status || true
299
299
 
300
+ # Keep the public API reachable even if Mullvad disconnects between jobs.
301
+ # (Lockdown mode can block all traffic while disconnected.)
302
+ mullvad lockdown-mode set off || true
303
+ mullvad lan set allow || true
304
+
300
305
  echo "==> 1.5) Install Deno (system-wide, for yt-dlp YouTube extraction)"
301
306
  # Install into /usr/local/bin/deno so systemd PATH can see it.
302
307
  # Official installer supports system-wide install via DENO_INSTALL=/usr/local.
@@ -310,7 +315,7 @@ mkdir -p "${APP_DIR}"
310
315
  python3 -m venv "${VENV_DIR}"
311
316
  source "${VENV_DIR}/bin/activate"
312
317
  pip install --upgrade pip
313
- pip install "ytp-dl==0.6.3" "yt-dlp[default]" gunicorn gevent
318
+ pip install "ytp-dl==0.6.4" "yt-dlp[default]" gunicorn
314
319
  deactivate
315
320
 
316
321
  echo "==> 3) API environment file (/etc/default/ytp-dl-api)"
@@ -334,7 +339,7 @@ EnvironmentFile=/etc/default/ytp-dl-api
334
339
  Environment=VIRTUAL_ENV=${VENV_DIR}
335
340
  Environment=PATH=${VENV_DIR}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
336
341
 
337
- ExecStart=${VENV_DIR}/bin/gunicorn -k gevent -w 1 --worker-connections 200 --timeout 0 --graceful-timeout 15 --keep-alive 20 --bind 0.0.0.0:${PORT} scripts.api:app
342
+ ExecStart=${VENV_DIR}/bin/gunicorn -k gthread -w 1 --threads ${GUNICORN_THREADS} --timeout 0 --graceful-timeout 15 --keep-alive 20 --bind 0.0.0.0:${PORT} scripts.api:app
338
343
 
339
344
  Restart=always
340
345
  RestartSec=3
@@ -0,0 +1,8 @@
1
+ scripts/__init__.py,sha256=EbAplfCcyLD3Q_9sxemm6owCc5_UJv53vmlxy810p2s,152
2
+ scripts/api.py,sha256=cyLjHmelLwzh8-GOjqXsQdhm6wLX8bOkADZ_qU1naRQ,4331
3
+ scripts/downloader.py,sha256=vvHasu-41DGPDUzOTA4kz52tijTkaii1NnuU4cHQxg8,10825
4
+ ytp_dl-0.6.4.dist-info/METADATA,sha256=Euvl-2zd5I4uh-6RVV1xGp-E8VQan7DvGXWOO5LXh6Q,11627
5
+ ytp_dl-0.6.4.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
6
+ ytp_dl-0.6.4.dist-info/entry_points.txt,sha256=QqjqZZAEt3Y7RGrleqZ312sjjboUpbMLdo7qFxuCH30,48
7
+ ytp_dl-0.6.4.dist-info/top_level.txt,sha256=rmzd5mewlrJy4sT608KPib7sM7edoY75AeqJeY3SPB4,8
8
+ ytp_dl-0.6.4.dist-info/RECORD,,
@@ -1,8 +0,0 @@
1
- scripts/__init__.py,sha256=EbAplfCcyLD3Q_9sxemm6owCc5_UJv53vmlxy810p2s,152
2
- scripts/api.py,sha256=EmTmzhpElx5QaJJ5z8GiimJTVZOHRoKhcReIRUCShBg,3943
3
- scripts/downloader.py,sha256=vvHasu-41DGPDUzOTA4kz52tijTkaii1NnuU4cHQxg8,10825
4
- ytp_dl-0.6.3.dist-info/METADATA,sha256=Rloz8LictcjPy_qXwkUtYWjAKyefSAGbxQ3GvmSyBv8,11342
5
- ytp_dl-0.6.3.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
6
- ytp_dl-0.6.3.dist-info/entry_points.txt,sha256=QqjqZZAEt3Y7RGrleqZ312sjjboUpbMLdo7qFxuCH30,48
7
- ytp_dl-0.6.3.dist-info/top_level.txt,sha256=rmzd5mewlrJy4sT608KPib7sM7edoY75AeqJeY3SPB4,8
8
- ytp_dl-0.6.3.dist-info/RECORD,,
File without changes