ytp-dl 0.6.3__py3-none-any.whl → 0.6.5__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 +31 -8
- {ytp_dl-0.6.3.dist-info → ytp_dl-0.6.5.dist-info}/METADATA +160 -72
- ytp_dl-0.6.5.dist-info/RECORD +8 -0
- ytp_dl-0.6.3.dist-info/RECORD +0 -8
- {ytp_dl-0.6.3.dist-info → ytp_dl-0.6.5.dist-info}/WHEEL +0 -0
- {ytp_dl-0.6.3.dist-info → ytp_dl-0.6.5.dist-info}/entry_points.txt +0 -0
- {ytp_dl-0.6.3.dist-info → ytp_dl-0.6.5.dist-info}/top_level.txt +0 -0
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
+
Version: 0.6.5
|
|
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
|
|
@@ -34,39 +33,39 @@ Dynamic: summary
|
|
|
34
33
|
|
|
35
34
|
# ytp-dl
|
|
36
35
|
|
|
37
|
-
|
|
36
|
+
A lightweight YouTube downloader with Mullvad VPN integration and an HTTP API.
|
|
38
37
|
|
|
39
38
|
[](https://pypi.org/project/ytp-dl/)
|
|
40
39
|
[](https://pypi.org/project/ytp-dl/)
|
|
41
40
|
[](https://pypi.org/project/ytp-dl/)
|
|
42
41
|
[](https://pypi.org/project/ytp-dl/)
|
|
43
42
|
|
|
44
|
-
**ytp-dl** is a privacy-focused YouTube downloader that
|
|
43
|
+
**ytp-dl** is a privacy-focused YouTube downloader that routes downloads through Mullvad VPN via an HTTP API.
|
|
45
44
|
|
|
46
45
|
---
|
|
47
46
|
|
|
48
|
-
##
|
|
47
|
+
## Features
|
|
49
48
|
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
49
|
+
* Privacy-first: connect/disconnect Mullvad per download
|
|
50
|
+
* Smart quality selection: prefers 1080p H.264 + AAC (no transcoding)
|
|
51
|
+
* Audio downloads: extract audio as MP3
|
|
52
|
+
* HTTP API: simple Flask API with concurrency controls
|
|
53
|
+
* VPS-ready: automated installer script for Ubuntu
|
|
55
54
|
|
|
56
55
|
---
|
|
57
56
|
|
|
58
|
-
##
|
|
57
|
+
## Installation
|
|
59
58
|
|
|
60
59
|
```bash
|
|
61
|
-
pip install ytp-dl==0.6.
|
|
60
|
+
pip install ytp-dl==0.6.5 yt-dlp[default]
|
|
62
61
|
```
|
|
63
62
|
|
|
64
|
-
|
|
63
|
+
Requirements:
|
|
65
64
|
|
|
66
|
-
* Linux
|
|
67
|
-
*
|
|
68
|
-
* FFmpeg (
|
|
69
|
-
* Deno (system-wide
|
|
65
|
+
* Linux (tested on Ubuntu 24.04/25.04)
|
|
66
|
+
* Mullvad CLI installed and configured
|
|
67
|
+
* FFmpeg (audio/video handling)
|
|
68
|
+
* Deno (system-wide; required by yt-dlp for modern YouTube extraction)
|
|
70
69
|
* Python 3.8+
|
|
71
70
|
|
|
72
71
|
Notes:
|
|
@@ -75,29 +74,38 @@ Notes:
|
|
|
75
74
|
|
|
76
75
|
---
|
|
77
76
|
|
|
78
|
-
##
|
|
77
|
+
## Using your VPS
|
|
79
78
|
|
|
80
|
-
Once your VPS is running, you can download videos using simple HTTP requests
|
|
79
|
+
Once your VPS is running, you can download videos using simple HTTP requests.
|
|
81
80
|
|
|
82
|
-
### Download a
|
|
81
|
+
### Download a video (1080p MP4)
|
|
83
82
|
|
|
84
83
|
```bash
|
|
85
|
-
curl -X POST http://YOUR_VPS_IP:5000/api/download
|
|
84
|
+
curl -X POST http://YOUR_VPS_IP:5000/api/download \
|
|
85
|
+
-H "Content-Type: application/json" \
|
|
86
|
+
-d '{"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"}' \
|
|
87
|
+
--output video.mp4
|
|
86
88
|
```
|
|
87
89
|
|
|
88
|
-
### Download
|
|
90
|
+
### Download audio only (MP3)
|
|
89
91
|
|
|
90
92
|
```bash
|
|
91
|
-
curl -X POST http://YOUR_VPS_IP:5000/api/download
|
|
93
|
+
curl -X POST http://YOUR_VPS_IP:5000/api/download \
|
|
94
|
+
-H "Content-Type: application/json" \
|
|
95
|
+
-d '{"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", "extension": "mp3"}' \
|
|
96
|
+
--output audio.mp3
|
|
92
97
|
```
|
|
93
98
|
|
|
94
|
-
### Download at
|
|
99
|
+
### Download at a specific resolution
|
|
95
100
|
|
|
96
101
|
```bash
|
|
97
|
-
curl -X POST http://YOUR_VPS_IP:5000/api/download
|
|
102
|
+
curl -X POST http://YOUR_VPS_IP:5000/api/download \
|
|
103
|
+
-H "Content-Type: application/json" \
|
|
104
|
+
-d '{"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", "resolution": 720}' \
|
|
105
|
+
--output video.mp4
|
|
98
106
|
```
|
|
99
107
|
|
|
100
|
-
### Check
|
|
108
|
+
### Check server health
|
|
101
109
|
|
|
102
110
|
```bash
|
|
103
111
|
curl http://YOUR_VPS_IP:5000/healthz
|
|
@@ -125,7 +133,7 @@ response = requests.post(
|
|
|
125
133
|
"resolution": 1080,
|
|
126
134
|
"extension": "mp4"
|
|
127
135
|
},
|
|
128
|
-
stream=True
|
|
136
|
+
stream=True,
|
|
129
137
|
)
|
|
130
138
|
|
|
131
139
|
if response.status_code == 200:
|
|
@@ -140,28 +148,29 @@ else:
|
|
|
140
148
|
|
|
141
149
|
---
|
|
142
150
|
|
|
143
|
-
##
|
|
151
|
+
## Configuration
|
|
144
152
|
|
|
145
|
-
### Installation
|
|
153
|
+
### Installation script variables
|
|
146
154
|
|
|
147
|
-
These environment variables configure the VPS installation (
|
|
155
|
+
These environment variables configure the VPS installation (you can override them when running the script).
|
|
148
156
|
|
|
149
157
|
| Variable | Description | Default |
|
|
150
158
|
| ------------------------ | --------------------------------------- | --------------------- |
|
|
151
159
|
| `PORT` | API server port | `5000` |
|
|
152
160
|
| `APP_DIR` | Installation directory | `/opt/yt-dlp-mullvad` |
|
|
153
|
-
| `MV_ACCOUNT` | Mullvad account number |
|
|
161
|
+
| `MV_ACCOUNT` | Mullvad account number | (empty) |
|
|
154
162
|
| `YTPDL_MAX_CONCURRENT` | Max simultaneous downloads (API cap) | `1` |
|
|
155
163
|
| `YTPDL_MULLVAD_LOCATION` | Mullvad relay location code (e.g. `us`) | `us` |
|
|
164
|
+
| `GUNICORN_THREADS` | Threads per Gunicorn worker | `4` |
|
|
156
165
|
|
|
157
166
|
Notes:
|
|
158
167
|
|
|
159
168
|
* If `MV_ACCOUNT` is set, the installer attempts `mullvad account login <MV_ACCOUNT>` once.
|
|
160
169
|
* If `MV_ACCOUNT` is left empty, the script skips login and assumes Mullvad is already configured.
|
|
161
170
|
|
|
162
|
-
### Runtime
|
|
171
|
+
### Runtime environment variables
|
|
163
172
|
|
|
164
|
-
After installation, these
|
|
173
|
+
After installation, these are set in `/etc/default/ytp-dl-api` and can be edited manually.
|
|
165
174
|
|
|
166
175
|
| Variable | Description | Default |
|
|
167
176
|
| ------------------------ | ----------------------------- | -------------------------- |
|
|
@@ -178,50 +187,33 @@ sudo systemctl restart ytp-dl-api
|
|
|
178
187
|
|
|
179
188
|
---
|
|
180
189
|
|
|
181
|
-
##
|
|
182
|
-
|
|
183
|
-
### View Service Status
|
|
190
|
+
## Managing your VPS service
|
|
184
191
|
|
|
185
192
|
```bash
|
|
186
193
|
sudo systemctl status ytp-dl-api
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
### View Logs
|
|
190
|
-
|
|
191
|
-
```bash
|
|
192
194
|
sudo journalctl -u ytp-dl-api -f
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
### Restart Service
|
|
196
|
-
|
|
197
|
-
```bash
|
|
198
195
|
sudo systemctl restart ytp-dl-api
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
### Stop/Start Service
|
|
202
|
-
|
|
203
|
-
```bash
|
|
204
196
|
sudo systemctl stop ytp-dl-api
|
|
205
197
|
sudo systemctl start ytp-dl-api
|
|
206
198
|
```
|
|
207
199
|
|
|
208
200
|
---
|
|
209
201
|
|
|
210
|
-
##
|
|
202
|
+
## API reference
|
|
211
203
|
|
|
212
204
|
### POST `/api/download`
|
|
213
205
|
|
|
214
|
-
|
|
206
|
+
Request body:
|
|
215
207
|
|
|
216
208
|
```json
|
|
217
209
|
{
|
|
218
210
|
"url": "string (required)",
|
|
219
211
|
"resolution": "integer (optional, default: 1080)",
|
|
220
|
-
"extension": "string (optional, 'mp4' or '
|
|
212
|
+
"extension": "string (optional, 'mp4', 'mp3', or 'best')"
|
|
221
213
|
}
|
|
222
214
|
```
|
|
223
215
|
|
|
224
|
-
|
|
216
|
+
Response:
|
|
225
217
|
|
|
226
218
|
* `200 OK` - File download stream
|
|
227
219
|
* `400 Bad Request` - Missing or invalid URL
|
|
@@ -230,8 +222,6 @@ sudo systemctl start ytp-dl-api
|
|
|
230
222
|
|
|
231
223
|
### GET `/healthz`
|
|
232
224
|
|
|
233
|
-
**Response:**
|
|
234
|
-
|
|
235
225
|
```json
|
|
236
226
|
{
|
|
237
227
|
"ok": true,
|
|
@@ -242,16 +232,22 @@ sudo systemctl start ytp-dl-api
|
|
|
242
232
|
|
|
243
233
|
---
|
|
244
234
|
|
|
245
|
-
##
|
|
235
|
+
## VPS deployment
|
|
236
|
+
|
|
237
|
+
### Why policy routing is included
|
|
238
|
+
|
|
239
|
+
When Mullvad connects/disconnects, Linux routing can change in a way that breaks inbound TCP handshakes (clients see intermittent connection timeouts). The installer sets up a small policy-routing rule that forces replies sourced from your public VPS IP to use the public interface, keeping the API reachable even while Mullvad is cycling.
|
|
246
240
|
|
|
247
|
-
|
|
241
|
+
### What the installer does
|
|
248
242
|
|
|
249
|
-
*
|
|
250
|
-
*
|
|
251
|
-
*
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
*
|
|
243
|
+
* Installs Python, FFmpeg, Mullvad CLI, and Deno
|
|
244
|
+
* Creates a virtualenv at `/opt/yt-dlp-mullvad/venv`
|
|
245
|
+
* Installs `ytp-dl==0.6.5` + `yt-dlp[default]` + `gunicorn`
|
|
246
|
+
* Installs a policy-routing oneshot service to keep the public API reachable
|
|
247
|
+
* Sets up a systemd service on port 5000
|
|
248
|
+
* Runs Gunicorn with `gthread` (threaded) workers
|
|
249
|
+
|
|
250
|
+
Note: `gthread` is a built-in Gunicorn worker class (no extra Python dependency).
|
|
255
251
|
|
|
256
252
|
```bash
|
|
257
253
|
#!/usr/bin/env bash
|
|
@@ -260,9 +256,10 @@ sudo systemctl start ytp-dl-api
|
|
|
260
256
|
# What this does:
|
|
261
257
|
# - Installs Python, ffmpeg, Mullvad CLI
|
|
262
258
|
# - Installs Deno system-wide (JS runtime required for modern YouTube extraction via yt-dlp)
|
|
259
|
+
# - Configures policy routing so the public API stays reachable while Mullvad toggles
|
|
263
260
|
# - Creates a virtualenv at /opt/yt-dlp-mullvad/venv
|
|
264
|
-
# - Installs ytp-dl==0.6.
|
|
265
|
-
# - Creates a
|
|
261
|
+
# - Installs ytp-dl==0.6.5 + yt-dlp[default] + gunicorn in that venv
|
|
262
|
+
# - Creates a systemd service ytp-dl-api.service on port 5000
|
|
266
263
|
#
|
|
267
264
|
# Mullvad connect/disconnect is handled per-job by downloader.py.
|
|
268
265
|
|
|
@@ -273,17 +270,33 @@ PORT="${PORT:-5000}" # API listen port
|
|
|
273
270
|
APP_DIR="${APP_DIR:-/opt/yt-dlp-mullvad}" # app/venv root
|
|
274
271
|
VENV_DIR="${VENV_DIR:-${APP_DIR}/venv}" # python venv
|
|
275
272
|
|
|
276
|
-
MV_ACCOUNT="${MV_ACCOUNT:-}" # Mullvad account (
|
|
273
|
+
MV_ACCOUNT="${MV_ACCOUNT:-}" # Mullvad account (optional)
|
|
277
274
|
YTPDL_MAX_CONCURRENT="${YTPDL_MAX_CONCURRENT:-1}" # API concurrency cap
|
|
278
275
|
YTPDL_MULLVAD_LOCATION="${YTPDL_MULLVAD_LOCATION:-us}" # default Mullvad relay hint
|
|
276
|
+
GUNICORN_THREADS="${GUNICORN_THREADS:-4}" # keep API responsive on small VPS
|
|
279
277
|
### -------------------------------------------------------------------------
|
|
280
278
|
|
|
281
279
|
[[ "${EUID}" -eq 0 ]] || { echo "Please run as root"; exit 1; }
|
|
282
280
|
export DEBIAN_FRONTEND=noninteractive
|
|
283
281
|
|
|
282
|
+
echo "==> 0) Capture public routing (pre-VPN)"
|
|
283
|
+
PUB_DEV="$(ip route show default | awk '/default/ {print $5; exit}')"
|
|
284
|
+
PUB_GW="$(ip route show default | awk '/default/ {print $3; exit}')"
|
|
285
|
+
PUB_IP="$(ip -4 addr show dev "${PUB_DEV}" | awk '/inet / {print $2}' | cut -d/ -f1 | head -n1)"
|
|
286
|
+
|
|
287
|
+
if [[ -z "${PUB_DEV}" || -z "${PUB_GW}" || -z "${PUB_IP}" ]]; then
|
|
288
|
+
echo "Failed to detect public routing (PUB_DEV/PUB_GW/PUB_IP)."
|
|
289
|
+
echo "PUB_DEV=${PUB_DEV} PUB_GW=${PUB_GW} PUB_IP=${PUB_IP}"
|
|
290
|
+
exit 1
|
|
291
|
+
fi
|
|
292
|
+
|
|
293
|
+
echo "Public dev: ${PUB_DEV} | gw: ${PUB_GW} | ip: ${PUB_IP}"
|
|
294
|
+
|
|
284
295
|
echo "==> 1) Base packages & Mullvad CLI"
|
|
285
296
|
apt-get update
|
|
286
|
-
apt-get install -yq --no-install-recommends
|
|
297
|
+
apt-get install -yq --no-install-recommends \
|
|
298
|
+
python3-venv python3-pip curl ffmpeg ca-certificates unzip \
|
|
299
|
+
iproute2 iptables
|
|
287
300
|
|
|
288
301
|
if ! command -v mullvad >/dev/null 2>&1; then
|
|
289
302
|
curl -fsSLo /tmp/mullvad.deb https://mullvad.net/download/app/deb/latest/
|
|
@@ -297,6 +310,77 @@ fi
|
|
|
297
310
|
|
|
298
311
|
mullvad status || true
|
|
299
312
|
|
|
313
|
+
# Keep the public API reachable even if Mullvad disconnects between jobs.
|
|
314
|
+
# (Lockdown mode can block all traffic while disconnected.)
|
|
315
|
+
mullvad lockdown-mode set off || true
|
|
316
|
+
mullvad lan set allow || true
|
|
317
|
+
|
|
318
|
+
echo "==> 1.1) Policy routing: keep replies from ${PUB_IP} on ${PUB_DEV}"
|
|
319
|
+
# Loose reverse-path filtering avoids drops when the default route changes under VPN.
|
|
320
|
+
tee /etc/sysctl.d/99-ytpdl-policy-routing.conf >/dev/null <<EOF
|
|
321
|
+
net.ipv4.conf.all.rp_filter=2
|
|
322
|
+
net.ipv4.conf.default.rp_filter=2
|
|
323
|
+
net.ipv4.conf.${PUB_DEV}.rp_filter=2
|
|
324
|
+
EOF
|
|
325
|
+
sysctl --system >/dev/null
|
|
326
|
+
|
|
327
|
+
# Persist the detected public route info for re-apply at boot.
|
|
328
|
+
tee /etc/default/ytpdl-policy-routing >/dev/null <<EOF
|
|
329
|
+
PUB_DEV=${PUB_DEV}
|
|
330
|
+
PUB_GW=${PUB_GW}
|
|
331
|
+
PUB_IP=${PUB_IP}
|
|
332
|
+
EOF
|
|
333
|
+
|
|
334
|
+
# Add a routing table id if it doesn't already exist.
|
|
335
|
+
grep -qE '^100\s+ytpdl-public$' /etc/iproute2/rt_tables || echo '100 ytpdl-public' >> /etc/iproute2/rt_tables
|
|
336
|
+
|
|
337
|
+
# Idempotent apply script.
|
|
338
|
+
tee /usr/local/sbin/ytpdl-policy-routing.sh >/dev/null <<'EOF'
|
|
339
|
+
#!/usr/bin/env bash
|
|
340
|
+
set -euo pipefail
|
|
341
|
+
|
|
342
|
+
source /etc/default/ytpdl-policy-routing
|
|
343
|
+
|
|
344
|
+
TABLE_ID="100"
|
|
345
|
+
TABLE_NAME="ytpdl-public"
|
|
346
|
+
PRIO="11000"
|
|
347
|
+
|
|
348
|
+
# Ensure table has the public default route.
|
|
349
|
+
ip route replace default via "${PUB_GW}" dev "${PUB_DEV}" table "${TABLE_NAME}"
|
|
350
|
+
|
|
351
|
+
# Ensure rule exists (replace is not supported for rules).
|
|
352
|
+
if ip rule show | grep -qE "^${PRIO}:.*from ${PUB_IP}/32 lookup ${TABLE_NAME}\b"; then
|
|
353
|
+
:
|
|
354
|
+
else
|
|
355
|
+
# remove any stale rule at this priority
|
|
356
|
+
while ip rule show | grep -qE "^${PRIO}:"; do
|
|
357
|
+
ip rule del priority "${PRIO}" || true
|
|
358
|
+
done
|
|
359
|
+
ip rule add priority "${PRIO}" from "${PUB_IP}/32" table "${TABLE_NAME}"
|
|
360
|
+
fi
|
|
361
|
+
|
|
362
|
+
ip route flush cache || true
|
|
363
|
+
EOF
|
|
364
|
+
chmod +x /usr/local/sbin/ytpdl-policy-routing.sh
|
|
365
|
+
|
|
366
|
+
tee /etc/systemd/system/ytpdl-policy-routing.service >/dev/null <<EOF
|
|
367
|
+
[Unit]
|
|
368
|
+
Description=ytp-dl policy routing (keep public API reachable)
|
|
369
|
+
After=network-online.target
|
|
370
|
+
Wants=network-online.target
|
|
371
|
+
|
|
372
|
+
[Service]
|
|
373
|
+
Type=oneshot
|
|
374
|
+
ExecStart=/usr/local/sbin/ytpdl-policy-routing.sh
|
|
375
|
+
RemainAfterExit=yes
|
|
376
|
+
|
|
377
|
+
[Install]
|
|
378
|
+
WantedBy=multi-user.target
|
|
379
|
+
EOF
|
|
380
|
+
|
|
381
|
+
systemctl daemon-reload
|
|
382
|
+
systemctl enable --now ytpdl-policy-routing.service
|
|
383
|
+
|
|
300
384
|
echo "==> 1.5) Install Deno (system-wide, for yt-dlp YouTube extraction)"
|
|
301
385
|
# Install into /usr/local/bin/deno so systemd PATH can see it.
|
|
302
386
|
# Official installer supports system-wide install via DENO_INSTALL=/usr/local.
|
|
@@ -310,7 +394,7 @@ mkdir -p "${APP_DIR}"
|
|
|
310
394
|
python3 -m venv "${VENV_DIR}"
|
|
311
395
|
source "${VENV_DIR}/bin/activate"
|
|
312
396
|
pip install --upgrade pip
|
|
313
|
-
pip install "ytp-dl==0.6.
|
|
397
|
+
pip install "ytp-dl==0.6.5" "yt-dlp[default]" gunicorn
|
|
314
398
|
deactivate
|
|
315
399
|
|
|
316
400
|
echo "==> 3) API environment file (/etc/default/ytp-dl-api)"
|
|
@@ -324,8 +408,9 @@ echo "==> 4) Gunicorn systemd service (ytp-dl-api.service on :${PORT})"
|
|
|
324
408
|
tee /etc/systemd/system/ytp-dl-api.service >/dev/null <<EOF
|
|
325
409
|
[Unit]
|
|
326
410
|
Description=Gunicorn for ytp-dl Mullvad API (minimal)
|
|
327
|
-
After=network-online.target
|
|
411
|
+
After=network-online.target ytpdl-policy-routing.service
|
|
328
412
|
Wants=network-online.target
|
|
413
|
+
Requires=ytpdl-policy-routing.service
|
|
329
414
|
|
|
330
415
|
[Service]
|
|
331
416
|
User=root
|
|
@@ -334,7 +419,9 @@ EnvironmentFile=/etc/default/ytp-dl-api
|
|
|
334
419
|
Environment=VIRTUAL_ENV=${VENV_DIR}
|
|
335
420
|
Environment=PATH=${VENV_DIR}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
|
|
336
421
|
|
|
337
|
-
ExecStart=${VENV_DIR}/bin/gunicorn -k
|
|
422
|
+
ExecStart=${VENV_DIR}/bin/gunicorn -k gthread -w 1 --threads ${GUNICORN_THREADS} \
|
|
423
|
+
--timeout 0 --graceful-timeout 15 --keep-alive 20 \
|
|
424
|
+
--bind 0.0.0.0:${PORT} scripts.api:app
|
|
338
425
|
|
|
339
426
|
Restart=always
|
|
340
427
|
RestartSec=3
|
|
@@ -363,5 +450,6 @@ echo "========================================="
|
|
|
363
450
|
echo "Installation complete!"
|
|
364
451
|
echo "API running on port ${PORT}"
|
|
365
452
|
echo "Test from outside: curl http://YOUR_VPS_IP:${PORT}/healthz"
|
|
453
|
+
echo "If you use UFW: sudo ufw allow ${PORT}/tcp"
|
|
366
454
|
echo "========================================="
|
|
367
455
|
```
|
|
@@ -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.5.dist-info/METADATA,sha256=XVL2522dMVIjIPh8cHZXSkkJM-7GRb-KyS_lfAwcOBI,14547
|
|
5
|
+
ytp_dl-0.6.5.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
6
|
+
ytp_dl-0.6.5.dist-info/entry_points.txt,sha256=QqjqZZAEt3Y7RGrleqZ312sjjboUpbMLdo7qFxuCH30,48
|
|
7
|
+
ytp_dl-0.6.5.dist-info/top_level.txt,sha256=rmzd5mewlrJy4sT608KPib7sM7edoY75AeqJeY3SPB4,8
|
|
8
|
+
ytp_dl-0.6.5.dist-info/RECORD,,
|
ytp_dl-0.6.3.dist-info/RECORD
DELETED
|
@@ -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
|
|
File without changes
|
|
File without changes
|