ytp-dl 0.6.2__py3-none-any.whl → 0.6.3__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 +9 -2
- scripts/downloader.py +102 -48
- {ytp_dl-0.6.2.dist-info → ytp_dl-0.6.3.dist-info}/METADATA +70 -46
- ytp_dl-0.6.3.dist-info/RECORD +8 -0
- ytp_dl-0.6.2.dist-info/RECORD +0 -8
- {ytp_dl-0.6.2.dist-info → ytp_dl-0.6.3.dist-info}/WHEEL +0 -0
- {ytp_dl-0.6.2.dist-info → ytp_dl-0.6.3.dist-info}/entry_points.txt +0 -0
- {ytp_dl-0.6.2.dist-info → ytp_dl-0.6.3.dist-info}/top_level.txt +0 -0
scripts/api.py
CHANGED
|
@@ -20,9 +20,9 @@ MAX_CONCURRENT = int(os.environ.get("YTPDL_MAX_CONCURRENT", "1"))
|
|
|
20
20
|
_sem = Semaphore(MAX_CONCURRENT)
|
|
21
21
|
|
|
22
22
|
# Failsafe: delete abandoned job dirs older than this many seconds.
|
|
23
|
+
# (keep 21600 if you prefer; 3600 is fine too)
|
|
23
24
|
STALE_JOB_TTL_S = int(os.environ.get("YTPDL_STALE_JOB_TTL_S", "3600"))
|
|
24
25
|
|
|
25
|
-
# extension is now a "mode": mp3 | mp4 | best
|
|
26
26
|
_ALLOWED_EXTENSIONS = {"mp3", "mp4", "best"}
|
|
27
27
|
|
|
28
28
|
|
|
@@ -63,6 +63,8 @@ def handle_download():
|
|
|
63
63
|
data = request.get_json(force=True)
|
|
64
64
|
url = (data.get("url") or "").strip()
|
|
65
65
|
resolution = data.get("resolution")
|
|
66
|
+
|
|
67
|
+
# extension is now a "mode": mp3 | mp4 | best
|
|
66
68
|
extension = (data.get("extension") or "mp4").strip().lower()
|
|
67
69
|
|
|
68
70
|
if not url:
|
|
@@ -89,6 +91,7 @@ def handle_download():
|
|
|
89
91
|
raise RuntimeError("Download failed")
|
|
90
92
|
|
|
91
93
|
# Release semaphore as soon as yt-dlp is done.
|
|
94
|
+
# Streaming the file should not block the next download job.
|
|
92
95
|
_release_once()
|
|
93
96
|
|
|
94
97
|
response = send_file(filename, as_attachment=True)
|
|
@@ -108,7 +111,11 @@ def handle_download():
|
|
|
108
111
|
if job_dir:
|
|
109
112
|
shutil.rmtree(job_dir, ignore_errors=True)
|
|
110
113
|
_release_once()
|
|
111
|
-
|
|
114
|
+
|
|
115
|
+
msg = str(e)
|
|
116
|
+
if "Mullvad not logged in" in msg:
|
|
117
|
+
return jsonify(error=msg), 503
|
|
118
|
+
return jsonify(error=f"Download failed: {msg}"), 500
|
|
112
119
|
|
|
113
120
|
except Exception as e:
|
|
114
121
|
if job_dir:
|
scripts/downloader.py
CHANGED
|
@@ -5,13 +5,15 @@ import os
|
|
|
5
5
|
import shlex
|
|
6
6
|
import shutil
|
|
7
7
|
import subprocess
|
|
8
|
+
import time
|
|
8
9
|
from typing import Optional, List, Tuple
|
|
9
10
|
|
|
10
11
|
# =========================
|
|
11
12
|
# Config / constants
|
|
12
13
|
# =========================
|
|
13
|
-
VENV_PATH = os.environ.get("YTPDL_VENV", "/opt/
|
|
14
|
+
VENV_PATH = os.environ.get("YTPDL_VENV", "/opt/yt-dlp-mullvad/venv")
|
|
14
15
|
YTDLP_BIN = os.path.join(VENV_PATH, "bin", "yt-dlp")
|
|
16
|
+
MULLVAD_LOCATION = os.environ.get("YTPDL_MULLVAD_LOCATION", "us")
|
|
15
17
|
|
|
16
18
|
MODERN_UA = os.environ.get(
|
|
17
19
|
"YTPDL_USER_AGENT",
|
|
@@ -20,11 +22,6 @@ MODERN_UA = os.environ.get(
|
|
|
20
22
|
"Chrome/124.0.0.0 Safari/537.36",
|
|
21
23
|
)
|
|
22
24
|
|
|
23
|
-
# Optional: explicitly pin a JS runtime (useful if systemd PATH is odd).
|
|
24
|
-
# Example: YTPDL_JS_RUNTIMES="deno:/usr/local/bin/deno"
|
|
25
|
-
# Deno is enabled by default in yt-dlp; supplying a path is optional. :contentReference[oaicite:3]{index=3}
|
|
26
|
-
JS_RUNTIMES = os.environ.get("YTPDL_JS_RUNTIMES", "").strip()
|
|
27
|
-
|
|
28
25
|
FFMPEG_BIN = shutil.which("ffmpeg") or "ffmpeg"
|
|
29
26
|
DEFAULT_OUT_DIR = os.environ.get("YTPDL_DOWNLOAD_DIR", "/root")
|
|
30
27
|
|
|
@@ -63,8 +60,13 @@ def _tail(out: str) -> str:
|
|
|
63
60
|
return txt.strip()
|
|
64
61
|
|
|
65
62
|
|
|
63
|
+
def _is_youtube_url(url: str) -> bool:
|
|
64
|
+
u = (url or "").lower()
|
|
65
|
+
return any(h in u for h in ("youtube.com", "youtu.be", "youtube-nocookie.com"))
|
|
66
|
+
|
|
67
|
+
|
|
66
68
|
# =========================
|
|
67
|
-
# Environment
|
|
69
|
+
# Environment / Mullvad
|
|
68
70
|
# =========================
|
|
69
71
|
def validate_environment() -> None:
|
|
70
72
|
if not os.path.exists(YTDLP_BIN):
|
|
@@ -73,12 +75,59 @@ def validate_environment() -> None:
|
|
|
73
75
|
raise RuntimeError("ffmpeg not found on PATH")
|
|
74
76
|
|
|
75
77
|
|
|
78
|
+
def _mullvad_present() -> bool:
|
|
79
|
+
return shutil.which("mullvad") is not None
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def mullvad_logged_in() -> bool:
|
|
83
|
+
if not _mullvad_present():
|
|
84
|
+
return False
|
|
85
|
+
res = subprocess.run(
|
|
86
|
+
["mullvad", "account", "get"],
|
|
87
|
+
stdout=subprocess.PIPE,
|
|
88
|
+
stderr=subprocess.STDOUT,
|
|
89
|
+
text=True,
|
|
90
|
+
)
|
|
91
|
+
return "not logged in" not in (res.stdout or "").lower()
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def require_mullvad_login() -> None:
|
|
95
|
+
if _mullvad_present() and not mullvad_logged_in():
|
|
96
|
+
raise RuntimeError("Mullvad not logged in. Run: mullvad account login <ACCOUNT>")
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def mullvad_connect(location: Optional[str] = None) -> None:
|
|
100
|
+
if not _mullvad_present():
|
|
101
|
+
return
|
|
102
|
+
loc = (location or MULLVAD_LOCATION).strip()
|
|
103
|
+
_run_argv(["mullvad", "disconnect"], check=False)
|
|
104
|
+
if loc:
|
|
105
|
+
_run_argv(["mullvad", "relay", "set", "location", loc], check=False)
|
|
106
|
+
_run_argv(["mullvad", "connect"], check=False)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def mullvad_wait_connected(timeout: int = 20) -> bool:
|
|
110
|
+
if not _mullvad_present():
|
|
111
|
+
return True
|
|
112
|
+
for _ in range(timeout):
|
|
113
|
+
res = subprocess.run(
|
|
114
|
+
["mullvad", "status"],
|
|
115
|
+
stdout=subprocess.PIPE,
|
|
116
|
+
stderr=subprocess.STDOUT,
|
|
117
|
+
text=True,
|
|
118
|
+
)
|
|
119
|
+
if "Connected" in (res.stdout or ""):
|
|
120
|
+
return True
|
|
121
|
+
time.sleep(1)
|
|
122
|
+
return False
|
|
123
|
+
|
|
124
|
+
|
|
76
125
|
# =========================
|
|
77
126
|
# yt-dlp helpers
|
|
78
127
|
# =========================
|
|
79
128
|
def _common_flags() -> List[str]:
|
|
80
129
|
# --no-playlist prevents accidental channel/playlist pulls (and disk blowups)
|
|
81
|
-
|
|
130
|
+
return [
|
|
82
131
|
"--no-playlist",
|
|
83
132
|
"--retries", "10",
|
|
84
133
|
"--fragment-retries", "10",
|
|
@@ -90,13 +139,6 @@ def _common_flags() -> List[str]:
|
|
|
90
139
|
"--sleep-interval", "1",
|
|
91
140
|
]
|
|
92
141
|
|
|
93
|
-
# If you want to hard-pin deno (or any runtime), set YTPDL_JS_RUNTIMES.
|
|
94
|
-
# Example: deno:/usr/local/bin/deno :contentReference[oaicite:4]{index=4}
|
|
95
|
-
if JS_RUNTIMES:
|
|
96
|
-
flags.extend(["--js-runtimes", JS_RUNTIMES])
|
|
97
|
-
|
|
98
|
-
return flags
|
|
99
|
-
|
|
100
142
|
|
|
101
143
|
def _extract_final_path(stdout: str, out_dir: str) -> Optional[str]:
|
|
102
144
|
"""
|
|
@@ -215,6 +257,7 @@ def _download_with_format(
|
|
|
215
257
|
]
|
|
216
258
|
|
|
217
259
|
if extract_mp3:
|
|
260
|
+
# Force audio extraction to MP3 (requires ffmpeg)
|
|
218
261
|
argv.extend(["--extract-audio", "--audio-format", "mp3"])
|
|
219
262
|
|
|
220
263
|
# Only force merge container when we actually want MP4 output.
|
|
@@ -266,44 +309,55 @@ def download_video(
|
|
|
266
309
|
|
|
267
310
|
validate_environment()
|
|
268
311
|
|
|
269
|
-
|
|
312
|
+
require_mullvad_login()
|
|
313
|
+
mullvad_connect(MULLVAD_LOCATION)
|
|
314
|
+
if not mullvad_wait_connected():
|
|
315
|
+
raise RuntimeError("Mullvad connection failed")
|
|
270
316
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
url=url,
|
|
274
|
-
out_dir=out_dir,
|
|
275
|
-
fmt="bestaudio",
|
|
276
|
-
merge_output_format=None,
|
|
277
|
-
extract_mp3=True,
|
|
278
|
-
)
|
|
279
|
-
|
|
280
|
-
cap = int(resolution or 1080)
|
|
317
|
+
try:
|
|
318
|
+
mode = (extension or "mp4").lower().strip()
|
|
281
319
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
try:
|
|
320
|
+
if mode == "mp3":
|
|
321
|
+
# bestaudio -> ffmpeg -> mp3 (post-processed by yt-dlp)
|
|
285
322
|
return _download_with_format(
|
|
286
323
|
url=url,
|
|
287
324
|
out_dir=out_dir,
|
|
288
|
-
fmt=
|
|
325
|
+
fmt="bestaudio",
|
|
289
326
|
merge_output_format=None,
|
|
290
|
-
extract_mp3=
|
|
291
|
-
)
|
|
292
|
-
except Exception:
|
|
293
|
-
# If best fails, fall back to Apple-safe MP4.
|
|
294
|
-
return _download_with_format(
|
|
295
|
-
url=url,
|
|
296
|
-
out_dir=out_dir,
|
|
297
|
-
fmt=_fmt_mp4_apple_safe(cap),
|
|
298
|
-
merge_output_format="mp4",
|
|
299
|
-
extract_mp3=False,
|
|
327
|
+
extract_mp3=True,
|
|
300
328
|
)
|
|
301
329
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
330
|
+
cap = int(resolution or 1080)
|
|
331
|
+
|
|
332
|
+
if mode == "best":
|
|
333
|
+
# Try best first (may produce webm/mkv/etc).
|
|
334
|
+
try:
|
|
335
|
+
return _download_with_format(
|
|
336
|
+
url=url,
|
|
337
|
+
out_dir=out_dir,
|
|
338
|
+
fmt=_fmt_best(cap),
|
|
339
|
+
merge_output_format=None,
|
|
340
|
+
extract_mp3=False,
|
|
341
|
+
)
|
|
342
|
+
except Exception:
|
|
343
|
+
# If best fails for any reason, fall back to Apple-safe MP4.
|
|
344
|
+
return _download_with_format(
|
|
345
|
+
url=url,
|
|
346
|
+
out_dir=out_dir,
|
|
347
|
+
fmt=_fmt_mp4_apple_safe(cap),
|
|
348
|
+
merge_output_format="mp4",
|
|
349
|
+
extract_mp3=False,
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
# Default / "mp4" mode: force Apple-safe MP4 up to cap.
|
|
353
|
+
return _download_with_format(
|
|
354
|
+
url=url,
|
|
355
|
+
out_dir=out_dir,
|
|
356
|
+
fmt=_fmt_mp4_apple_safe(cap),
|
|
357
|
+
merge_output_format="mp4",
|
|
358
|
+
extract_mp3=False,
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
finally:
|
|
362
|
+
if _mullvad_present():
|
|
363
|
+
_run_argv(["mullvad", "disconnect"], check=False)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: ytp-dl
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.3
|
|
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
|
|
@@ -34,35 +34,37 @@ Dynamic: summary
|
|
|
34
34
|
|
|
35
35
|
# ytp-dl
|
|
36
36
|
|
|
37
|
-
> A lightweight YouTube downloader with
|
|
37
|
+
> A lightweight YouTube downloader with Mullvad VPN integration and HTTP API
|
|
38
38
|
|
|
39
39
|
[](https://pypi.org/project/ytp-dl/)
|
|
40
40
|
[](https://pypi.org/project/ytp-dl/)
|
|
41
41
|
[](https://pypi.org/project/ytp-dl/)
|
|
42
42
|
[](https://pypi.org/project/ytp-dl/)
|
|
43
43
|
|
|
44
|
-
**ytp-dl** is a privacy-focused YouTube downloader that
|
|
44
|
+
**ytp-dl** is a privacy-focused YouTube downloader that automatically routes downloads through Mullvad VPN via an HTTP API.
|
|
45
45
|
|
|
46
46
|
---
|
|
47
47
|
|
|
48
|
-
## Features
|
|
48
|
+
## ✨ Features
|
|
49
49
|
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
50
|
+
* 🔒 **Privacy First** — Automatically connects/disconnects Mullvad VPN per download
|
|
51
|
+
* 🎥 **Smart Quality Selection** — Prefers 1080p H.264 + AAC (no transcoding needed)
|
|
52
|
+
* 🎵 **Audio Downloads** — Extract audio as MP3
|
|
53
|
+
* 🚀 **HTTP API** — Simple Flask-based API with concurrency controls
|
|
54
|
+
* ⚡ **VPS Ready** — Includes automated installer script for Ubuntu
|
|
54
55
|
|
|
55
56
|
---
|
|
56
57
|
|
|
57
|
-
## Installation
|
|
58
|
+
## 📦 Installation
|
|
58
59
|
|
|
59
60
|
```bash
|
|
60
|
-
pip install ytp-dl==0.6.
|
|
61
|
+
pip install ytp-dl==0.6.3 yt-dlp[default]
|
|
61
62
|
```
|
|
62
63
|
|
|
63
64
|
**Requirements:**
|
|
64
65
|
|
|
65
66
|
* Linux operating system (tested on Ubuntu 24.04/25.04)
|
|
67
|
+
* [Mullvad CLI](https://mullvad.net/en/download/vpn/linux) installed and configured
|
|
66
68
|
* FFmpeg (for handling audio + video)
|
|
67
69
|
* Deno (system-wide, required by yt-dlp for modern YouTube extraction)
|
|
68
70
|
* Python 3.8+
|
|
@@ -73,7 +75,7 @@ Notes:
|
|
|
73
75
|
|
|
74
76
|
---
|
|
75
77
|
|
|
76
|
-
## Using Your VPS
|
|
78
|
+
## 🎯 Using Your VPS
|
|
77
79
|
|
|
78
80
|
Once your VPS is running, you can download videos using simple HTTP requests:
|
|
79
81
|
|
|
@@ -138,26 +140,34 @@ else:
|
|
|
138
140
|
|
|
139
141
|
---
|
|
140
142
|
|
|
141
|
-
## Configuration
|
|
143
|
+
## ⚙️ Configuration
|
|
142
144
|
|
|
143
145
|
### Installation Script Variables
|
|
144
146
|
|
|
145
147
|
These environment variables configure the VPS installation (they can be overridden when running the script):
|
|
146
148
|
|
|
147
|
-
| Variable
|
|
148
|
-
|
|
|
149
|
-
| `PORT`
|
|
150
|
-
| `APP_DIR`
|
|
151
|
-
| `
|
|
149
|
+
| Variable | Description | Default |
|
|
150
|
+
| ------------------------ | --------------------------------------- | --------------------- |
|
|
151
|
+
| `PORT` | API server port | `5000` |
|
|
152
|
+
| `APP_DIR` | Installation directory | `/opt/yt-dlp-mullvad` |
|
|
153
|
+
| `MV_ACCOUNT` | Mullvad account number | your mullvad id |
|
|
154
|
+
| `YTPDL_MAX_CONCURRENT` | Max simultaneous downloads (API cap) | `1` |
|
|
155
|
+
| `YTPDL_MULLVAD_LOCATION` | Mullvad relay location code (e.g. `us`) | `us` |
|
|
156
|
+
|
|
157
|
+
Notes:
|
|
158
|
+
|
|
159
|
+
* If `MV_ACCOUNT` is set, the installer attempts `mullvad account login <MV_ACCOUNT>` once.
|
|
160
|
+
* If `MV_ACCOUNT` is left empty, the script skips login and assumes Mullvad is already configured.
|
|
152
161
|
|
|
153
162
|
### Runtime Environment Variables
|
|
154
163
|
|
|
155
164
|
After installation, these variables control the API behavior. They are set in `/etc/default/ytp-dl-api` and can be edited manually:
|
|
156
165
|
|
|
157
|
-
| Variable
|
|
158
|
-
|
|
|
159
|
-
| `YTPDL_MAX_CONCURRENT`
|
|
160
|
-
| `
|
|
166
|
+
| Variable | Description | Default |
|
|
167
|
+
| ------------------------ | ----------------------------- | -------------------------- |
|
|
168
|
+
| `YTPDL_MAX_CONCURRENT` | Maximum concurrent downloads | `1` |
|
|
169
|
+
| `YTPDL_MULLVAD_LOCATION` | Mullvad relay location code | `us` |
|
|
170
|
+
| `YTPDL_VENV` | Path to virtualenv for ytp-dl | `/opt/yt-dlp-mullvad/venv` |
|
|
161
171
|
|
|
162
172
|
To change configuration after installation:
|
|
163
173
|
|
|
@@ -168,7 +178,7 @@ sudo systemctl restart ytp-dl-api
|
|
|
168
178
|
|
|
169
179
|
---
|
|
170
180
|
|
|
171
|
-
## Managing Your VPS Service
|
|
181
|
+
## 🔧 Managing Your VPS Service
|
|
172
182
|
|
|
173
183
|
### View Service Status
|
|
174
184
|
|
|
@@ -197,7 +207,7 @@ sudo systemctl start ytp-dl-api
|
|
|
197
207
|
|
|
198
208
|
---
|
|
199
209
|
|
|
200
|
-
## API Reference
|
|
210
|
+
## 📋 API Reference
|
|
201
211
|
|
|
202
212
|
### POST `/api/download`
|
|
203
213
|
|
|
@@ -232,45 +242,60 @@ sudo systemctl start ytp-dl-api
|
|
|
232
242
|
|
|
233
243
|
---
|
|
234
244
|
|
|
235
|
-
## VPS Deployment
|
|
245
|
+
## 🖥️ VPS Deployment
|
|
236
246
|
|
|
237
247
|
**What it does:**
|
|
238
248
|
|
|
239
|
-
* Installs Python and
|
|
240
|
-
* Installs Deno system-wide (required by yt-dlp for modern YouTube extraction)
|
|
241
|
-
* Creates
|
|
242
|
-
* Installs `ytp-dl==0.6.
|
|
243
|
-
* Sets up
|
|
244
|
-
* Configures Gunicorn with gevent workers
|
|
249
|
+
* ✅ Installs Python, FFmpeg, and Mullvad CLI
|
|
250
|
+
* ✅ Installs Deno system-wide (required by yt-dlp for modern YouTube extraction)
|
|
251
|
+
* ✅ Creates virtualenv at `/opt/yt-dlp-mullvad/venv`
|
|
252
|
+
* ✅ Installs `ytp-dl==0.6.3` + `yt-dlp[default]` into the virtualenv
|
|
253
|
+
* ✅ Sets up systemd service on port 5000
|
|
254
|
+
* ✅ Configures Gunicorn with gevent workers
|
|
245
255
|
|
|
246
256
|
```bash
|
|
247
257
|
#!/usr/bin/env bash
|
|
248
|
-
# VPS_Installation.sh - Minimal Ubuntu 24.04/25.04 setup for ytp-dl API
|
|
258
|
+
# VPS_Installation.sh - Minimal Ubuntu 24.04/25.04 setup for ytp-dl API + Mullvad
|
|
249
259
|
#
|
|
250
260
|
# What this does:
|
|
251
|
-
# - Installs Python
|
|
261
|
+
# - Installs Python, ffmpeg, Mullvad CLI
|
|
252
262
|
# - Installs Deno system-wide (JS runtime required for modern YouTube extraction via yt-dlp)
|
|
253
|
-
# - Creates a virtualenv at /opt/
|
|
254
|
-
# - Installs ytp-dl==0.6.
|
|
263
|
+
# - Creates a virtualenv at /opt/yt-dlp-mullvad/venv
|
|
264
|
+
# - Installs ytp-dl==0.6.3 + yt-dlp[default] + gunicorn + gevent in that venv
|
|
255
265
|
# - Creates a simple systemd service ytp-dl-api.service on port 5000
|
|
266
|
+
#
|
|
267
|
+
# Mullvad connect/disconnect is handled per-job by downloader.py.
|
|
256
268
|
|
|
257
269
|
set -euo pipefail
|
|
258
270
|
|
|
259
271
|
### --- Tunables -------------------------------------------------------------
|
|
260
|
-
PORT="${PORT:-5000}"
|
|
261
|
-
APP_DIR="${APP_DIR:-/opt/
|
|
262
|
-
VENV_DIR="${VENV_DIR:-${APP_DIR}/venv}"
|
|
272
|
+
PORT="${PORT:-5000}" # API listen port
|
|
273
|
+
APP_DIR="${APP_DIR:-/opt/yt-dlp-mullvad}" # app/venv root
|
|
274
|
+
VENV_DIR="${VENV_DIR:-${APP_DIR}/venv}" # python venv
|
|
263
275
|
|
|
264
|
-
|
|
276
|
+
MV_ACCOUNT="${MV_ACCOUNT:-}" # Mullvad account (put number after -)
|
|
277
|
+
YTPDL_MAX_CONCURRENT="${YTPDL_MAX_CONCURRENT:-1}" # API concurrency cap
|
|
278
|
+
YTPDL_MULLVAD_LOCATION="${YTPDL_MULLVAD_LOCATION:-us}" # default Mullvad relay hint
|
|
265
279
|
### -------------------------------------------------------------------------
|
|
266
280
|
|
|
267
281
|
[[ "${EUID}" -eq 0 ]] || { echo "Please run as root"; exit 1; }
|
|
268
282
|
export DEBIAN_FRONTEND=noninteractive
|
|
269
283
|
|
|
270
|
-
echo "==> 1) Base packages"
|
|
284
|
+
echo "==> 1) Base packages & Mullvad CLI"
|
|
271
285
|
apt-get update
|
|
272
|
-
apt-get install -yq --no-install-recommends
|
|
273
|
-
|
|
286
|
+
apt-get install -yq --no-install-recommends python3-venv python3-pip curl ffmpeg ca-certificates unzip
|
|
287
|
+
|
|
288
|
+
if ! command -v mullvad >/dev/null 2>&1; then
|
|
289
|
+
curl -fsSLo /tmp/mullvad.deb https://mullvad.net/download/app/deb/latest/
|
|
290
|
+
apt-get install -y /tmp/mullvad.deb
|
|
291
|
+
fi
|
|
292
|
+
|
|
293
|
+
if [[ -n "${MV_ACCOUNT}" ]]; then
|
|
294
|
+
echo "Logging into Mullvad account (if not already logged in)..."
|
|
295
|
+
mullvad account login "${MV_ACCOUNT}" || true
|
|
296
|
+
fi
|
|
297
|
+
|
|
298
|
+
mullvad status || true
|
|
274
299
|
|
|
275
300
|
echo "==> 1.5) Install Deno (system-wide, for yt-dlp YouTube extraction)"
|
|
276
301
|
# Install into /usr/local/bin/deno so systemd PATH can see it.
|
|
@@ -285,19 +310,20 @@ mkdir -p "${APP_DIR}"
|
|
|
285
310
|
python3 -m venv "${VENV_DIR}"
|
|
286
311
|
source "${VENV_DIR}/bin/activate"
|
|
287
312
|
pip install --upgrade pip
|
|
288
|
-
pip install "ytp-dl==0.6.
|
|
313
|
+
pip install "ytp-dl==0.6.3" "yt-dlp[default]" gunicorn gevent
|
|
289
314
|
deactivate
|
|
290
315
|
|
|
291
316
|
echo "==> 3) API environment file (/etc/default/ytp-dl-api)"
|
|
292
317
|
tee /etc/default/ytp-dl-api >/dev/null <<EOF
|
|
293
318
|
YTPDL_MAX_CONCURRENT=${YTPDL_MAX_CONCURRENT}
|
|
319
|
+
YTPDL_MULLVAD_LOCATION=${YTPDL_MULLVAD_LOCATION}
|
|
294
320
|
YTPDL_VENV=${VENV_DIR}
|
|
295
321
|
EOF
|
|
296
322
|
|
|
297
323
|
echo "==> 4) Gunicorn systemd service (ytp-dl-api.service on :${PORT})"
|
|
298
324
|
tee /etc/systemd/system/ytp-dl-api.service >/dev/null <<EOF
|
|
299
325
|
[Unit]
|
|
300
|
-
Description=Gunicorn for ytp-dl API (minimal)
|
|
326
|
+
Description=Gunicorn for ytp-dl Mullvad API (minimal)
|
|
301
327
|
After=network-online.target
|
|
302
328
|
Wants=network-online.target
|
|
303
329
|
|
|
@@ -308,9 +334,7 @@ EnvironmentFile=/etc/default/ytp-dl-api
|
|
|
308
334
|
Environment=VIRTUAL_ENV=${VENV_DIR}
|
|
309
335
|
Environment=PATH=${VENV_DIR}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
|
|
310
336
|
|
|
311
|
-
ExecStart=${VENV_DIR}/bin/gunicorn -k gevent -w 1
|
|
312
|
-
--worker-connections 200 --timeout 0 --graceful-timeout 15 --keep-alive 20 \
|
|
313
|
-
--bind 0.0.0.0:${PORT} scripts.api:app
|
|
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
|
|
314
338
|
|
|
315
339
|
Restart=always
|
|
316
340
|
RestartSec=3
|
|
@@ -0,0 +1,8 @@
|
|
|
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,,
|
ytp_dl-0.6.2.dist-info/RECORD
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
scripts/__init__.py,sha256=EbAplfCcyLD3Q_9sxemm6owCc5_UJv53vmlxy810p2s,152
|
|
2
|
-
scripts/api.py,sha256=K-4iwgj6W4Gx8y3mzBe_CP4Ua9Ky9quESOqcw6BpNv0,3713
|
|
3
|
-
scripts/downloader.py,sha256=NGW1TmPmjHCTlN1uUNYwhSBm1c7UotkF8Pn9Q0dxqLk,9227
|
|
4
|
-
ytp_dl-0.6.2.dist-info/METADATA,sha256=7U33Y4moFmjs1xivWbR3_xGe5imTbGFlecFx6BI63os,9754
|
|
5
|
-
ytp_dl-0.6.2.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
6
|
-
ytp_dl-0.6.2.dist-info/entry_points.txt,sha256=QqjqZZAEt3Y7RGrleqZ312sjjboUpbMLdo7qFxuCH30,48
|
|
7
|
-
ytp_dl-0.6.2.dist-info/top_level.txt,sha256=rmzd5mewlrJy4sT608KPib7sM7edoY75AeqJeY3SPB4,8
|
|
8
|
-
ytp_dl-0.6.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|