ytp-dl 0.6.2__tar.gz → 0.6.4__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.
- {ytp_dl-0.6.2 → ytp_dl-0.6.4}/PKG-INFO +77 -48
- {ytp_dl-0.6.2 → ytp_dl-0.6.4}/README.md +77 -47
- {ytp_dl-0.6.2 → ytp_dl-0.6.4}/scripts/api.py +38 -8
- {ytp_dl-0.6.2 → ytp_dl-0.6.4}/scripts/downloader.py +102 -48
- {ytp_dl-0.6.2 → ytp_dl-0.6.4}/setup.py +1 -1
- {ytp_dl-0.6.2 → ytp_dl-0.6.4}/ytp_dl.egg-info/PKG-INFO +77 -48
- {ytp_dl-0.6.2 → ytp_dl-0.6.4}/ytp_dl.egg-info/requires.txt +0 -1
- {ytp_dl-0.6.2 → ytp_dl-0.6.4}/scripts/__init__.py +0 -0
- {ytp_dl-0.6.2 → ytp_dl-0.6.4}/setup.cfg +0 -0
- {ytp_dl-0.6.2 → ytp_dl-0.6.4}/ytp_dl.egg-info/SOURCES.txt +0 -0
- {ytp_dl-0.6.2 → ytp_dl-0.6.4}/ytp_dl.egg-info/dependency_links.txt +0 -0
- {ytp_dl-0.6.2 → ytp_dl-0.6.4}/ytp_dl.egg-info/entry_points.txt +0 -0
- {ytp_dl-0.6.2 → ytp_dl-0.6.4}/ytp_dl.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: ytp-dl
|
|
3
|
-
Version: 0.6.
|
|
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
|
|
@@ -34,35 +33,37 @@ Dynamic: summary
|
|
|
34
33
|
|
|
35
34
|
# ytp-dl
|
|
36
35
|
|
|
37
|
-
> A lightweight YouTube downloader with
|
|
36
|
+
> A lightweight YouTube downloader with Mullvad VPN integration and 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 automatically routes downloads through Mullvad VPN via an HTTP API.
|
|
45
44
|
|
|
46
45
|
---
|
|
47
46
|
|
|
48
|
-
## Features
|
|
47
|
+
## ✨ Features
|
|
49
48
|
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
49
|
+
* 🔒 **Privacy First** — Automatically connects/disconnects Mullvad VPN per download
|
|
50
|
+
* 🎥 **Smart Quality Selection** — Prefers 1080p H.264 + AAC (no transcoding needed)
|
|
51
|
+
* 🎵 **Audio Downloads** — Extract audio as MP3
|
|
52
|
+
* 🚀 **HTTP API** — Simple Flask-based API with concurrency controls
|
|
53
|
+
* ⚡ **VPS Ready** — Includes automated installer script for Ubuntu
|
|
54
54
|
|
|
55
55
|
---
|
|
56
56
|
|
|
57
|
-
## Installation
|
|
57
|
+
## 📦 Installation
|
|
58
58
|
|
|
59
59
|
```bash
|
|
60
|
-
pip install ytp-dl==0.6.
|
|
60
|
+
pip install ytp-dl==0.6.4 yt-dlp[default]
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
**Requirements:**
|
|
64
64
|
|
|
65
65
|
* Linux operating system (tested on Ubuntu 24.04/25.04)
|
|
66
|
+
* [Mullvad CLI](https://mullvad.net/en/download/vpn/linux) installed and configured
|
|
66
67
|
* FFmpeg (for handling audio + video)
|
|
67
68
|
* Deno (system-wide, required by yt-dlp for modern YouTube extraction)
|
|
68
69
|
* Python 3.8+
|
|
@@ -73,7 +74,7 @@ Notes:
|
|
|
73
74
|
|
|
74
75
|
---
|
|
75
76
|
|
|
76
|
-
## Using Your VPS
|
|
77
|
+
## 🎯 Using Your VPS
|
|
77
78
|
|
|
78
79
|
Once your VPS is running, you can download videos using simple HTTP requests:
|
|
79
80
|
|
|
@@ -138,26 +139,34 @@ else:
|
|
|
138
139
|
|
|
139
140
|
---
|
|
140
141
|
|
|
141
|
-
## Configuration
|
|
142
|
+
## ⚙️ Configuration
|
|
142
143
|
|
|
143
144
|
### Installation Script Variables
|
|
144
145
|
|
|
145
146
|
These environment variables configure the VPS installation (they can be overridden when running the script):
|
|
146
147
|
|
|
147
|
-
| Variable
|
|
148
|
-
|
|
|
149
|
-
| `PORT`
|
|
150
|
-
| `APP_DIR`
|
|
151
|
-
| `
|
|
148
|
+
| Variable | Description | Default |
|
|
149
|
+
| ------------------------ | --------------------------------------- | --------------------- |
|
|
150
|
+
| `PORT` | API server port | `5000` |
|
|
151
|
+
| `APP_DIR` | Installation directory | `/opt/yt-dlp-mullvad` |
|
|
152
|
+
| `MV_ACCOUNT` | Mullvad account number | your mullvad id |
|
|
153
|
+
| `YTPDL_MAX_CONCURRENT` | Max simultaneous downloads (API cap) | `1` |
|
|
154
|
+
| `YTPDL_MULLVAD_LOCATION` | Mullvad relay location code (e.g. `us`) | `us` |
|
|
155
|
+
|
|
156
|
+
Notes:
|
|
157
|
+
|
|
158
|
+
* If `MV_ACCOUNT` is set, the installer attempts `mullvad account login <MV_ACCOUNT>` once.
|
|
159
|
+
* If `MV_ACCOUNT` is left empty, the script skips login and assumes Mullvad is already configured.
|
|
152
160
|
|
|
153
161
|
### Runtime Environment Variables
|
|
154
162
|
|
|
155
163
|
After installation, these variables control the API behavior. They are set in `/etc/default/ytp-dl-api` and can be edited manually:
|
|
156
164
|
|
|
157
|
-
| Variable
|
|
158
|
-
|
|
|
159
|
-
| `YTPDL_MAX_CONCURRENT`
|
|
160
|
-
| `
|
|
165
|
+
| Variable | Description | Default |
|
|
166
|
+
| ------------------------ | ----------------------------- | -------------------------- |
|
|
167
|
+
| `YTPDL_MAX_CONCURRENT` | Maximum concurrent downloads | `1` |
|
|
168
|
+
| `YTPDL_MULLVAD_LOCATION` | Mullvad relay location code | `us` |
|
|
169
|
+
| `YTPDL_VENV` | Path to virtualenv for ytp-dl | `/opt/yt-dlp-mullvad/venv` |
|
|
161
170
|
|
|
162
171
|
To change configuration after installation:
|
|
163
172
|
|
|
@@ -168,7 +177,7 @@ sudo systemctl restart ytp-dl-api
|
|
|
168
177
|
|
|
169
178
|
---
|
|
170
179
|
|
|
171
|
-
## Managing Your VPS Service
|
|
180
|
+
## 🔧 Managing Your VPS Service
|
|
172
181
|
|
|
173
182
|
### View Service Status
|
|
174
183
|
|
|
@@ -197,7 +206,7 @@ sudo systemctl start ytp-dl-api
|
|
|
197
206
|
|
|
198
207
|
---
|
|
199
208
|
|
|
200
|
-
## API Reference
|
|
209
|
+
## 📋 API Reference
|
|
201
210
|
|
|
202
211
|
### POST `/api/download`
|
|
203
212
|
|
|
@@ -232,45 +241,66 @@ sudo systemctl start ytp-dl-api
|
|
|
232
241
|
|
|
233
242
|
---
|
|
234
243
|
|
|
235
|
-
## VPS Deployment
|
|
244
|
+
## 🖥️ VPS Deployment
|
|
236
245
|
|
|
237
246
|
**What it does:**
|
|
238
247
|
|
|
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
|
|
248
|
+
* ✅ Installs Python, FFmpeg, and Mullvad CLI
|
|
249
|
+
* ✅ Installs Deno system-wide (required by yt-dlp for modern YouTube extraction)
|
|
250
|
+
* ✅ Creates virtualenv at `/opt/yt-dlp-mullvad/venv`
|
|
251
|
+
* ✅ Installs `ytp-dl==0.6.4` + `yt-dlp[default]` into the virtualenv
|
|
252
|
+
* ✅ Sets up systemd service on port 5000
|
|
253
|
+
* ✅ Configures Gunicorn with gthread (threaded) workers
|
|
245
254
|
|
|
246
255
|
```bash
|
|
247
256
|
#!/usr/bin/env bash
|
|
248
|
-
# VPS_Installation.sh - Minimal Ubuntu 24.04/25.04 setup for ytp-dl API
|
|
257
|
+
# VPS_Installation.sh - Minimal Ubuntu 24.04/25.04 setup for ytp-dl API + Mullvad
|
|
249
258
|
#
|
|
250
259
|
# What this does:
|
|
251
|
-
# - Installs Python
|
|
260
|
+
# - Installs Python, ffmpeg, Mullvad CLI
|
|
252
261
|
# - 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.
|
|
262
|
+
# - Creates a virtualenv at /opt/yt-dlp-mullvad/venv
|
|
263
|
+
# - Installs ytp-dl==0.6.4 + yt-dlp[default] + gunicorn + gevent in that venv
|
|
255
264
|
# - Creates a simple systemd service ytp-dl-api.service on port 5000
|
|
265
|
+
#
|
|
266
|
+
# Mullvad connect/disconnect is handled per-job by downloader.py.
|
|
256
267
|
|
|
257
268
|
set -euo pipefail
|
|
258
269
|
|
|
259
270
|
### --- Tunables -------------------------------------------------------------
|
|
260
|
-
PORT="${PORT:-5000}"
|
|
261
|
-
APP_DIR="${APP_DIR:-/opt/
|
|
262
|
-
VENV_DIR="${VENV_DIR:-${APP_DIR}/venv}"
|
|
263
|
-
|
|
264
|
-
|
|
271
|
+
PORT="${PORT:-5000}" # API listen port
|
|
272
|
+
APP_DIR="${APP_DIR:-/opt/yt-dlp-mullvad}" # app/venv root
|
|
273
|
+
VENV_DIR="${VENV_DIR:-${APP_DIR}/venv}" # python venv
|
|
274
|
+
|
|
275
|
+
MV_ACCOUNT="${MV_ACCOUNT:-}" # Mullvad account (put number after -)
|
|
276
|
+
YTPDL_MAX_CONCURRENT="${YTPDL_MAX_CONCURRENT:-1}" # API concurrency cap
|
|
277
|
+
YTPDL_MULLVAD_LOCATION="${YTPDL_MULLVAD_LOCATION:-us}" # default Mullvad relay hint
|
|
278
|
+
GUNICORN_THREADS="${GUNICORN_THREADS:-4}" # keep API responsive on tiny VPS
|
|
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
|
|
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
|
|
274
304
|
|
|
275
305
|
echo "==> 1.5) Install Deno (system-wide, for yt-dlp YouTube extraction)"
|
|
276
306
|
# Install into /usr/local/bin/deno so systemd PATH can see it.
|
|
@@ -285,19 +315,20 @@ mkdir -p "${APP_DIR}"
|
|
|
285
315
|
python3 -m venv "${VENV_DIR}"
|
|
286
316
|
source "${VENV_DIR}/bin/activate"
|
|
287
317
|
pip install --upgrade pip
|
|
288
|
-
pip install "ytp-dl==0.6.
|
|
318
|
+
pip install "ytp-dl==0.6.4" "yt-dlp[default]" gunicorn
|
|
289
319
|
deactivate
|
|
290
320
|
|
|
291
321
|
echo "==> 3) API environment file (/etc/default/ytp-dl-api)"
|
|
292
322
|
tee /etc/default/ytp-dl-api >/dev/null <<EOF
|
|
293
323
|
YTPDL_MAX_CONCURRENT=${YTPDL_MAX_CONCURRENT}
|
|
324
|
+
YTPDL_MULLVAD_LOCATION=${YTPDL_MULLVAD_LOCATION}
|
|
294
325
|
YTPDL_VENV=${VENV_DIR}
|
|
295
326
|
EOF
|
|
296
327
|
|
|
297
328
|
echo "==> 4) Gunicorn systemd service (ytp-dl-api.service on :${PORT})"
|
|
298
329
|
tee /etc/systemd/system/ytp-dl-api.service >/dev/null <<EOF
|
|
299
330
|
[Unit]
|
|
300
|
-
Description=Gunicorn for ytp-dl API (minimal)
|
|
331
|
+
Description=Gunicorn for ytp-dl Mullvad API (minimal)
|
|
301
332
|
After=network-online.target
|
|
302
333
|
Wants=network-online.target
|
|
303
334
|
|
|
@@ -308,9 +339,7 @@ EnvironmentFile=/etc/default/ytp-dl-api
|
|
|
308
339
|
Environment=VIRTUAL_ENV=${VENV_DIR}
|
|
309
340
|
Environment=PATH=${VENV_DIR}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
|
|
310
341
|
|
|
311
|
-
ExecStart=${VENV_DIR}/bin/gunicorn -k
|
|
312
|
-
--worker-connections 200 --timeout 0 --graceful-timeout 15 --keep-alive 20 \
|
|
313
|
-
--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
|
|
314
343
|
|
|
315
344
|
Restart=always
|
|
316
345
|
RestartSec=3
|
|
@@ -1,34 +1,36 @@
|
|
|
1
1
|
# ytp-dl
|
|
2
2
|
|
|
3
|
-
> A lightweight YouTube downloader with
|
|
3
|
+
> A lightweight YouTube downloader with Mullvad VPN integration and HTTP API
|
|
4
4
|
|
|
5
5
|
[](https://pypi.org/project/ytp-dl/)
|
|
6
6
|
[](https://pypi.org/project/ytp-dl/)
|
|
7
7
|
[](https://pypi.org/project/ytp-dl/)
|
|
8
8
|
[](https://pypi.org/project/ytp-dl/)
|
|
9
9
|
|
|
10
|
-
**ytp-dl** is a privacy-focused YouTube downloader that
|
|
10
|
+
**ytp-dl** is a privacy-focused YouTube downloader that automatically routes downloads through Mullvad VPN via an HTTP API.
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
-
## Features
|
|
14
|
+
## ✨ Features
|
|
15
15
|
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
16
|
+
* 🔒 **Privacy First** — Automatically connects/disconnects Mullvad VPN per download
|
|
17
|
+
* 🎥 **Smart Quality Selection** — Prefers 1080p H.264 + AAC (no transcoding needed)
|
|
18
|
+
* 🎵 **Audio Downloads** — Extract audio as MP3
|
|
19
|
+
* 🚀 **HTTP API** — Simple Flask-based API with concurrency controls
|
|
20
|
+
* ⚡ **VPS Ready** — Includes automated installer script for Ubuntu
|
|
20
21
|
|
|
21
22
|
---
|
|
22
23
|
|
|
23
|
-
## Installation
|
|
24
|
+
## 📦 Installation
|
|
24
25
|
|
|
25
26
|
```bash
|
|
26
|
-
pip install ytp-dl==0.6.
|
|
27
|
+
pip install ytp-dl==0.6.4 yt-dlp[default]
|
|
27
28
|
```
|
|
28
29
|
|
|
29
30
|
**Requirements:**
|
|
30
31
|
|
|
31
32
|
* Linux operating system (tested on Ubuntu 24.04/25.04)
|
|
33
|
+
* [Mullvad CLI](https://mullvad.net/en/download/vpn/linux) installed and configured
|
|
32
34
|
* FFmpeg (for handling audio + video)
|
|
33
35
|
* Deno (system-wide, required by yt-dlp for modern YouTube extraction)
|
|
34
36
|
* Python 3.8+
|
|
@@ -39,7 +41,7 @@ Notes:
|
|
|
39
41
|
|
|
40
42
|
---
|
|
41
43
|
|
|
42
|
-
## Using Your VPS
|
|
44
|
+
## 🎯 Using Your VPS
|
|
43
45
|
|
|
44
46
|
Once your VPS is running, you can download videos using simple HTTP requests:
|
|
45
47
|
|
|
@@ -104,26 +106,34 @@ else:
|
|
|
104
106
|
|
|
105
107
|
---
|
|
106
108
|
|
|
107
|
-
## Configuration
|
|
109
|
+
## ⚙️ Configuration
|
|
108
110
|
|
|
109
111
|
### Installation Script Variables
|
|
110
112
|
|
|
111
113
|
These environment variables configure the VPS installation (they can be overridden when running the script):
|
|
112
114
|
|
|
113
|
-
| Variable
|
|
114
|
-
|
|
|
115
|
-
| `PORT`
|
|
116
|
-
| `APP_DIR`
|
|
117
|
-
| `
|
|
115
|
+
| Variable | Description | Default |
|
|
116
|
+
| ------------------------ | --------------------------------------- | --------------------- |
|
|
117
|
+
| `PORT` | API server port | `5000` |
|
|
118
|
+
| `APP_DIR` | Installation directory | `/opt/yt-dlp-mullvad` |
|
|
119
|
+
| `MV_ACCOUNT` | Mullvad account number | your mullvad id |
|
|
120
|
+
| `YTPDL_MAX_CONCURRENT` | Max simultaneous downloads (API cap) | `1` |
|
|
121
|
+
| `YTPDL_MULLVAD_LOCATION` | Mullvad relay location code (e.g. `us`) | `us` |
|
|
122
|
+
|
|
123
|
+
Notes:
|
|
124
|
+
|
|
125
|
+
* If `MV_ACCOUNT` is set, the installer attempts `mullvad account login <MV_ACCOUNT>` once.
|
|
126
|
+
* If `MV_ACCOUNT` is left empty, the script skips login and assumes Mullvad is already configured.
|
|
118
127
|
|
|
119
128
|
### Runtime Environment Variables
|
|
120
129
|
|
|
121
130
|
After installation, these variables control the API behavior. They are set in `/etc/default/ytp-dl-api` and can be edited manually:
|
|
122
131
|
|
|
123
|
-
| Variable
|
|
124
|
-
|
|
|
125
|
-
| `YTPDL_MAX_CONCURRENT`
|
|
126
|
-
| `
|
|
132
|
+
| Variable | Description | Default |
|
|
133
|
+
| ------------------------ | ----------------------------- | -------------------------- |
|
|
134
|
+
| `YTPDL_MAX_CONCURRENT` | Maximum concurrent downloads | `1` |
|
|
135
|
+
| `YTPDL_MULLVAD_LOCATION` | Mullvad relay location code | `us` |
|
|
136
|
+
| `YTPDL_VENV` | Path to virtualenv for ytp-dl | `/opt/yt-dlp-mullvad/venv` |
|
|
127
137
|
|
|
128
138
|
To change configuration after installation:
|
|
129
139
|
|
|
@@ -134,7 +144,7 @@ sudo systemctl restart ytp-dl-api
|
|
|
134
144
|
|
|
135
145
|
---
|
|
136
146
|
|
|
137
|
-
## Managing Your VPS Service
|
|
147
|
+
## 🔧 Managing Your VPS Service
|
|
138
148
|
|
|
139
149
|
### View Service Status
|
|
140
150
|
|
|
@@ -163,7 +173,7 @@ sudo systemctl start ytp-dl-api
|
|
|
163
173
|
|
|
164
174
|
---
|
|
165
175
|
|
|
166
|
-
## API Reference
|
|
176
|
+
## 📋 API Reference
|
|
167
177
|
|
|
168
178
|
### POST `/api/download`
|
|
169
179
|
|
|
@@ -198,45 +208,66 @@ sudo systemctl start ytp-dl-api
|
|
|
198
208
|
|
|
199
209
|
---
|
|
200
210
|
|
|
201
|
-
## VPS Deployment
|
|
211
|
+
## 🖥️ VPS Deployment
|
|
202
212
|
|
|
203
213
|
**What it does:**
|
|
204
214
|
|
|
205
|
-
* Installs Python and
|
|
206
|
-
* Installs Deno system-wide (required by yt-dlp for modern YouTube extraction)
|
|
207
|
-
* Creates
|
|
208
|
-
* Installs `ytp-dl==0.6.
|
|
209
|
-
* Sets up
|
|
210
|
-
* Configures Gunicorn with
|
|
215
|
+
* ✅ Installs Python, FFmpeg, and Mullvad CLI
|
|
216
|
+
* ✅ Installs Deno system-wide (required by yt-dlp for modern YouTube extraction)
|
|
217
|
+
* ✅ Creates virtualenv at `/opt/yt-dlp-mullvad/venv`
|
|
218
|
+
* ✅ Installs `ytp-dl==0.6.4` + `yt-dlp[default]` into the virtualenv
|
|
219
|
+
* ✅ Sets up systemd service on port 5000
|
|
220
|
+
* ✅ Configures Gunicorn with gthread (threaded) workers
|
|
211
221
|
|
|
212
222
|
```bash
|
|
213
223
|
#!/usr/bin/env bash
|
|
214
|
-
# VPS_Installation.sh - Minimal Ubuntu 24.04/25.04 setup for ytp-dl API
|
|
224
|
+
# VPS_Installation.sh - Minimal Ubuntu 24.04/25.04 setup for ytp-dl API + Mullvad
|
|
215
225
|
#
|
|
216
226
|
# What this does:
|
|
217
|
-
# - Installs Python
|
|
227
|
+
# - Installs Python, ffmpeg, Mullvad CLI
|
|
218
228
|
# - Installs Deno system-wide (JS runtime required for modern YouTube extraction via yt-dlp)
|
|
219
|
-
# - Creates a virtualenv at /opt/
|
|
220
|
-
# - Installs ytp-dl==0.6.
|
|
229
|
+
# - Creates a virtualenv at /opt/yt-dlp-mullvad/venv
|
|
230
|
+
# - Installs ytp-dl==0.6.4 + yt-dlp[default] + gunicorn + gevent in that venv
|
|
221
231
|
# - Creates a simple systemd service ytp-dl-api.service on port 5000
|
|
232
|
+
#
|
|
233
|
+
# Mullvad connect/disconnect is handled per-job by downloader.py.
|
|
222
234
|
|
|
223
235
|
set -euo pipefail
|
|
224
236
|
|
|
225
237
|
### --- Tunables -------------------------------------------------------------
|
|
226
|
-
PORT="${PORT:-5000}"
|
|
227
|
-
APP_DIR="${APP_DIR:-/opt/
|
|
228
|
-
VENV_DIR="${VENV_DIR:-${APP_DIR}/venv}"
|
|
229
|
-
|
|
230
|
-
|
|
238
|
+
PORT="${PORT:-5000}" # API listen port
|
|
239
|
+
APP_DIR="${APP_DIR:-/opt/yt-dlp-mullvad}" # app/venv root
|
|
240
|
+
VENV_DIR="${VENV_DIR:-${APP_DIR}/venv}" # python venv
|
|
241
|
+
|
|
242
|
+
MV_ACCOUNT="${MV_ACCOUNT:-}" # Mullvad account (put number after -)
|
|
243
|
+
YTPDL_MAX_CONCURRENT="${YTPDL_MAX_CONCURRENT:-1}" # API concurrency cap
|
|
244
|
+
YTPDL_MULLVAD_LOCATION="${YTPDL_MULLVAD_LOCATION:-us}" # default Mullvad relay hint
|
|
245
|
+
GUNICORN_THREADS="${GUNICORN_THREADS:-4}" # keep API responsive on tiny VPS
|
|
231
246
|
### -------------------------------------------------------------------------
|
|
232
247
|
|
|
233
248
|
[[ "${EUID}" -eq 0 ]] || { echo "Please run as root"; exit 1; }
|
|
234
249
|
export DEBIAN_FRONTEND=noninteractive
|
|
235
250
|
|
|
236
|
-
echo "==> 1) Base packages"
|
|
251
|
+
echo "==> 1) Base packages & Mullvad CLI"
|
|
237
252
|
apt-get update
|
|
238
|
-
apt-get install -yq --no-install-recommends
|
|
239
|
-
|
|
253
|
+
apt-get install -yq --no-install-recommends python3-venv python3-pip curl ffmpeg ca-certificates unzip
|
|
254
|
+
|
|
255
|
+
if ! command -v mullvad >/dev/null 2>&1; then
|
|
256
|
+
curl -fsSLo /tmp/mullvad.deb https://mullvad.net/download/app/deb/latest/
|
|
257
|
+
apt-get install -y /tmp/mullvad.deb
|
|
258
|
+
fi
|
|
259
|
+
|
|
260
|
+
if [[ -n "${MV_ACCOUNT}" ]]; then
|
|
261
|
+
echo "Logging into Mullvad account (if not already logged in)..."
|
|
262
|
+
mullvad account login "${MV_ACCOUNT}" || true
|
|
263
|
+
fi
|
|
264
|
+
|
|
265
|
+
mullvad status || true
|
|
266
|
+
|
|
267
|
+
# Keep the public API reachable even if Mullvad disconnects between jobs.
|
|
268
|
+
# (Lockdown mode can block all traffic while disconnected.)
|
|
269
|
+
mullvad lockdown-mode set off || true
|
|
270
|
+
mullvad lan set allow || true
|
|
240
271
|
|
|
241
272
|
echo "==> 1.5) Install Deno (system-wide, for yt-dlp YouTube extraction)"
|
|
242
273
|
# Install into /usr/local/bin/deno so systemd PATH can see it.
|
|
@@ -251,19 +282,20 @@ mkdir -p "${APP_DIR}"
|
|
|
251
282
|
python3 -m venv "${VENV_DIR}"
|
|
252
283
|
source "${VENV_DIR}/bin/activate"
|
|
253
284
|
pip install --upgrade pip
|
|
254
|
-
pip install "ytp-dl==0.6.
|
|
285
|
+
pip install "ytp-dl==0.6.4" "yt-dlp[default]" gunicorn
|
|
255
286
|
deactivate
|
|
256
287
|
|
|
257
288
|
echo "==> 3) API environment file (/etc/default/ytp-dl-api)"
|
|
258
289
|
tee /etc/default/ytp-dl-api >/dev/null <<EOF
|
|
259
290
|
YTPDL_MAX_CONCURRENT=${YTPDL_MAX_CONCURRENT}
|
|
291
|
+
YTPDL_MULLVAD_LOCATION=${YTPDL_MULLVAD_LOCATION}
|
|
260
292
|
YTPDL_VENV=${VENV_DIR}
|
|
261
293
|
EOF
|
|
262
294
|
|
|
263
295
|
echo "==> 4) Gunicorn systemd service (ytp-dl-api.service on :${PORT})"
|
|
264
296
|
tee /etc/systemd/system/ytp-dl-api.service >/dev/null <<EOF
|
|
265
297
|
[Unit]
|
|
266
|
-
Description=Gunicorn for ytp-dl API (minimal)
|
|
298
|
+
Description=Gunicorn for ytp-dl Mullvad API (minimal)
|
|
267
299
|
After=network-online.target
|
|
268
300
|
Wants=network-online.target
|
|
269
301
|
|
|
@@ -274,9 +306,7 @@ EnvironmentFile=/etc/default/ytp-dl-api
|
|
|
274
306
|
Environment=VIRTUAL_ENV=${VENV_DIR}
|
|
275
307
|
Environment=PATH=${VENV_DIR}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
|
|
276
308
|
|
|
277
|
-
ExecStart=${VENV_DIR}/bin/gunicorn -k
|
|
278
|
-
--worker-connections 200 --timeout 0 --graceful-timeout 15 --keep-alive 20 \
|
|
279
|
-
--bind 0.0.0.0:${PORT} scripts.api:app
|
|
309
|
+
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
|
|
280
310
|
|
|
281
311
|
Restart=always
|
|
282
312
|
RestartSec=3
|
|
@@ -306,4 +336,4 @@ echo "Installation complete!"
|
|
|
306
336
|
echo "API running on port ${PORT}"
|
|
307
337
|
echo "Test from outside: curl http://YOUR_VPS_IP:${PORT}/healthz"
|
|
308
338
|
echo "========================================="
|
|
309
|
-
```
|
|
339
|
+
```
|
|
@@ -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,12 +17,17 @@ 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
29
|
STALE_JOB_TTL_S = int(os.environ.get("YTPDL_STALE_JOB_TTL_S", "3600"))
|
|
24
30
|
|
|
25
|
-
# extension is now a "mode": mp3 | mp4 | best
|
|
26
31
|
_ALLOWED_EXTENSIONS = {"mp3", "mp4", "best"}
|
|
27
32
|
|
|
28
33
|
|
|
@@ -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,12 +79,14 @@ 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)
|
|
64
86
|
url = (data.get("url") or "").strip()
|
|
65
87
|
resolution = data.get("resolution")
|
|
88
|
+
|
|
89
|
+
# extension is now a "mode": mp3 | mp4 | best
|
|
66
90
|
extension = (data.get("extension") or "mp4").strip().lower()
|
|
67
91
|
|
|
68
92
|
if not url:
|
|
@@ -88,7 +112,7 @@ def handle_download():
|
|
|
88
112
|
if not (filename and os.path.exists(filename)):
|
|
89
113
|
raise RuntimeError("Download failed")
|
|
90
114
|
|
|
91
|
-
# Release
|
|
115
|
+
# Release slot as soon as yt-dlp is done.
|
|
92
116
|
_release_once()
|
|
93
117
|
|
|
94
118
|
response = send_file(filename, as_attachment=True)
|
|
@@ -108,7 +132,11 @@ def handle_download():
|
|
|
108
132
|
if job_dir:
|
|
109
133
|
shutil.rmtree(job_dir, ignore_errors=True)
|
|
110
134
|
_release_once()
|
|
111
|
-
|
|
135
|
+
|
|
136
|
+
msg = str(e)
|
|
137
|
+
if "Mullvad not logged in" in msg:
|
|
138
|
+
return jsonify(error=msg), 503
|
|
139
|
+
return jsonify(error=f"Download failed: {msg}"), 500
|
|
112
140
|
|
|
113
141
|
except Exception as e:
|
|
114
142
|
if job_dir:
|
|
@@ -119,7 +147,9 @@ def handle_download():
|
|
|
119
147
|
|
|
120
148
|
@app.route("/healthz", methods=["GET"])
|
|
121
149
|
def healthz():
|
|
122
|
-
|
|
150
|
+
with _in_use_lock:
|
|
151
|
+
in_use = _in_use
|
|
152
|
+
return jsonify(ok=True, in_use=in_use, capacity=MAX_CONCURRENT), 200
|
|
123
153
|
|
|
124
154
|
|
|
125
155
|
def main():
|
|
@@ -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)
|
|
@@ -8,7 +8,7 @@ with open("requirements.txt", "r", encoding="utf-8") as fh:
|
|
|
8
8
|
|
|
9
9
|
setup(
|
|
10
10
|
name="ytp-dl",
|
|
11
|
-
version="0.6.
|
|
11
|
+
version="0.6.4",
|
|
12
12
|
author="dumgum82",
|
|
13
13
|
author_email="dumgum42@gmail.com",
|
|
14
14
|
description="YouTube video downloader with Mullvad VPN integration and Flask API",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: ytp-dl
|
|
3
|
-
Version: 0.6.
|
|
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
|
|
@@ -34,35 +33,37 @@ Dynamic: summary
|
|
|
34
33
|
|
|
35
34
|
# ytp-dl
|
|
36
35
|
|
|
37
|
-
> A lightweight YouTube downloader with
|
|
36
|
+
> A lightweight YouTube downloader with Mullvad VPN integration and 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 automatically routes downloads through Mullvad VPN via an HTTP API.
|
|
45
44
|
|
|
46
45
|
---
|
|
47
46
|
|
|
48
|
-
## Features
|
|
47
|
+
## ✨ Features
|
|
49
48
|
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
49
|
+
* 🔒 **Privacy First** — Automatically connects/disconnects Mullvad VPN per download
|
|
50
|
+
* 🎥 **Smart Quality Selection** — Prefers 1080p H.264 + AAC (no transcoding needed)
|
|
51
|
+
* 🎵 **Audio Downloads** — Extract audio as MP3
|
|
52
|
+
* 🚀 **HTTP API** — Simple Flask-based API with concurrency controls
|
|
53
|
+
* ⚡ **VPS Ready** — Includes automated installer script for Ubuntu
|
|
54
54
|
|
|
55
55
|
---
|
|
56
56
|
|
|
57
|
-
## Installation
|
|
57
|
+
## 📦 Installation
|
|
58
58
|
|
|
59
59
|
```bash
|
|
60
|
-
pip install ytp-dl==0.6.
|
|
60
|
+
pip install ytp-dl==0.6.4 yt-dlp[default]
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
**Requirements:**
|
|
64
64
|
|
|
65
65
|
* Linux operating system (tested on Ubuntu 24.04/25.04)
|
|
66
|
+
* [Mullvad CLI](https://mullvad.net/en/download/vpn/linux) installed and configured
|
|
66
67
|
* FFmpeg (for handling audio + video)
|
|
67
68
|
* Deno (system-wide, required by yt-dlp for modern YouTube extraction)
|
|
68
69
|
* Python 3.8+
|
|
@@ -73,7 +74,7 @@ Notes:
|
|
|
73
74
|
|
|
74
75
|
---
|
|
75
76
|
|
|
76
|
-
## Using Your VPS
|
|
77
|
+
## 🎯 Using Your VPS
|
|
77
78
|
|
|
78
79
|
Once your VPS is running, you can download videos using simple HTTP requests:
|
|
79
80
|
|
|
@@ -138,26 +139,34 @@ else:
|
|
|
138
139
|
|
|
139
140
|
---
|
|
140
141
|
|
|
141
|
-
## Configuration
|
|
142
|
+
## ⚙️ Configuration
|
|
142
143
|
|
|
143
144
|
### Installation Script Variables
|
|
144
145
|
|
|
145
146
|
These environment variables configure the VPS installation (they can be overridden when running the script):
|
|
146
147
|
|
|
147
|
-
| Variable
|
|
148
|
-
|
|
|
149
|
-
| `PORT`
|
|
150
|
-
| `APP_DIR`
|
|
151
|
-
| `
|
|
148
|
+
| Variable | Description | Default |
|
|
149
|
+
| ------------------------ | --------------------------------------- | --------------------- |
|
|
150
|
+
| `PORT` | API server port | `5000` |
|
|
151
|
+
| `APP_DIR` | Installation directory | `/opt/yt-dlp-mullvad` |
|
|
152
|
+
| `MV_ACCOUNT` | Mullvad account number | your mullvad id |
|
|
153
|
+
| `YTPDL_MAX_CONCURRENT` | Max simultaneous downloads (API cap) | `1` |
|
|
154
|
+
| `YTPDL_MULLVAD_LOCATION` | Mullvad relay location code (e.g. `us`) | `us` |
|
|
155
|
+
|
|
156
|
+
Notes:
|
|
157
|
+
|
|
158
|
+
* If `MV_ACCOUNT` is set, the installer attempts `mullvad account login <MV_ACCOUNT>` once.
|
|
159
|
+
* If `MV_ACCOUNT` is left empty, the script skips login and assumes Mullvad is already configured.
|
|
152
160
|
|
|
153
161
|
### Runtime Environment Variables
|
|
154
162
|
|
|
155
163
|
After installation, these variables control the API behavior. They are set in `/etc/default/ytp-dl-api` and can be edited manually:
|
|
156
164
|
|
|
157
|
-
| Variable
|
|
158
|
-
|
|
|
159
|
-
| `YTPDL_MAX_CONCURRENT`
|
|
160
|
-
| `
|
|
165
|
+
| Variable | Description | Default |
|
|
166
|
+
| ------------------------ | ----------------------------- | -------------------------- |
|
|
167
|
+
| `YTPDL_MAX_CONCURRENT` | Maximum concurrent downloads | `1` |
|
|
168
|
+
| `YTPDL_MULLVAD_LOCATION` | Mullvad relay location code | `us` |
|
|
169
|
+
| `YTPDL_VENV` | Path to virtualenv for ytp-dl | `/opt/yt-dlp-mullvad/venv` |
|
|
161
170
|
|
|
162
171
|
To change configuration after installation:
|
|
163
172
|
|
|
@@ -168,7 +177,7 @@ sudo systemctl restart ytp-dl-api
|
|
|
168
177
|
|
|
169
178
|
---
|
|
170
179
|
|
|
171
|
-
## Managing Your VPS Service
|
|
180
|
+
## 🔧 Managing Your VPS Service
|
|
172
181
|
|
|
173
182
|
### View Service Status
|
|
174
183
|
|
|
@@ -197,7 +206,7 @@ sudo systemctl start ytp-dl-api
|
|
|
197
206
|
|
|
198
207
|
---
|
|
199
208
|
|
|
200
|
-
## API Reference
|
|
209
|
+
## 📋 API Reference
|
|
201
210
|
|
|
202
211
|
### POST `/api/download`
|
|
203
212
|
|
|
@@ -232,45 +241,66 @@ sudo systemctl start ytp-dl-api
|
|
|
232
241
|
|
|
233
242
|
---
|
|
234
243
|
|
|
235
|
-
## VPS Deployment
|
|
244
|
+
## 🖥️ VPS Deployment
|
|
236
245
|
|
|
237
246
|
**What it does:**
|
|
238
247
|
|
|
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
|
|
248
|
+
* ✅ Installs Python, FFmpeg, and Mullvad CLI
|
|
249
|
+
* ✅ Installs Deno system-wide (required by yt-dlp for modern YouTube extraction)
|
|
250
|
+
* ✅ Creates virtualenv at `/opt/yt-dlp-mullvad/venv`
|
|
251
|
+
* ✅ Installs `ytp-dl==0.6.4` + `yt-dlp[default]` into the virtualenv
|
|
252
|
+
* ✅ Sets up systemd service on port 5000
|
|
253
|
+
* ✅ Configures Gunicorn with gthread (threaded) workers
|
|
245
254
|
|
|
246
255
|
```bash
|
|
247
256
|
#!/usr/bin/env bash
|
|
248
|
-
# VPS_Installation.sh - Minimal Ubuntu 24.04/25.04 setup for ytp-dl API
|
|
257
|
+
# VPS_Installation.sh - Minimal Ubuntu 24.04/25.04 setup for ytp-dl API + Mullvad
|
|
249
258
|
#
|
|
250
259
|
# What this does:
|
|
251
|
-
# - Installs Python
|
|
260
|
+
# - Installs Python, ffmpeg, Mullvad CLI
|
|
252
261
|
# - 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.
|
|
262
|
+
# - Creates a virtualenv at /opt/yt-dlp-mullvad/venv
|
|
263
|
+
# - Installs ytp-dl==0.6.4 + yt-dlp[default] + gunicorn + gevent in that venv
|
|
255
264
|
# - Creates a simple systemd service ytp-dl-api.service on port 5000
|
|
265
|
+
#
|
|
266
|
+
# Mullvad connect/disconnect is handled per-job by downloader.py.
|
|
256
267
|
|
|
257
268
|
set -euo pipefail
|
|
258
269
|
|
|
259
270
|
### --- Tunables -------------------------------------------------------------
|
|
260
|
-
PORT="${PORT:-5000}"
|
|
261
|
-
APP_DIR="${APP_DIR:-/opt/
|
|
262
|
-
VENV_DIR="${VENV_DIR:-${APP_DIR}/venv}"
|
|
263
|
-
|
|
264
|
-
|
|
271
|
+
PORT="${PORT:-5000}" # API listen port
|
|
272
|
+
APP_DIR="${APP_DIR:-/opt/yt-dlp-mullvad}" # app/venv root
|
|
273
|
+
VENV_DIR="${VENV_DIR:-${APP_DIR}/venv}" # python venv
|
|
274
|
+
|
|
275
|
+
MV_ACCOUNT="${MV_ACCOUNT:-}" # Mullvad account (put number after -)
|
|
276
|
+
YTPDL_MAX_CONCURRENT="${YTPDL_MAX_CONCURRENT:-1}" # API concurrency cap
|
|
277
|
+
YTPDL_MULLVAD_LOCATION="${YTPDL_MULLVAD_LOCATION:-us}" # default Mullvad relay hint
|
|
278
|
+
GUNICORN_THREADS="${GUNICORN_THREADS:-4}" # keep API responsive on tiny VPS
|
|
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
|
|
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
|
|
274
304
|
|
|
275
305
|
echo "==> 1.5) Install Deno (system-wide, for yt-dlp YouTube extraction)"
|
|
276
306
|
# Install into /usr/local/bin/deno so systemd PATH can see it.
|
|
@@ -285,19 +315,20 @@ mkdir -p "${APP_DIR}"
|
|
|
285
315
|
python3 -m venv "${VENV_DIR}"
|
|
286
316
|
source "${VENV_DIR}/bin/activate"
|
|
287
317
|
pip install --upgrade pip
|
|
288
|
-
pip install "ytp-dl==0.6.
|
|
318
|
+
pip install "ytp-dl==0.6.4" "yt-dlp[default]" gunicorn
|
|
289
319
|
deactivate
|
|
290
320
|
|
|
291
321
|
echo "==> 3) API environment file (/etc/default/ytp-dl-api)"
|
|
292
322
|
tee /etc/default/ytp-dl-api >/dev/null <<EOF
|
|
293
323
|
YTPDL_MAX_CONCURRENT=${YTPDL_MAX_CONCURRENT}
|
|
324
|
+
YTPDL_MULLVAD_LOCATION=${YTPDL_MULLVAD_LOCATION}
|
|
294
325
|
YTPDL_VENV=${VENV_DIR}
|
|
295
326
|
EOF
|
|
296
327
|
|
|
297
328
|
echo "==> 4) Gunicorn systemd service (ytp-dl-api.service on :${PORT})"
|
|
298
329
|
tee /etc/systemd/system/ytp-dl-api.service >/dev/null <<EOF
|
|
299
330
|
[Unit]
|
|
300
|
-
Description=Gunicorn for ytp-dl API (minimal)
|
|
331
|
+
Description=Gunicorn for ytp-dl Mullvad API (minimal)
|
|
301
332
|
After=network-online.target
|
|
302
333
|
Wants=network-online.target
|
|
303
334
|
|
|
@@ -308,9 +339,7 @@ EnvironmentFile=/etc/default/ytp-dl-api
|
|
|
308
339
|
Environment=VIRTUAL_ENV=${VENV_DIR}
|
|
309
340
|
Environment=PATH=${VENV_DIR}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
|
|
310
341
|
|
|
311
|
-
ExecStart=${VENV_DIR}/bin/gunicorn -k
|
|
312
|
-
--worker-connections 200 --timeout 0 --graceful-timeout 15 --keep-alive 20 \
|
|
313
|
-
--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
|
|
314
343
|
|
|
315
344
|
Restart=always
|
|
316
345
|
RestartSec=3
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|