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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: ytp-dl
3
- Version: 0.6.2
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 an HTTP API
36
+ > A lightweight YouTube downloader with Mullvad VPN integration and HTTP API
38
37
 
39
38
  [![PyPI version](https://img.shields.io/pypi/v/ytp-dl.svg)](https://pypi.org/project/ytp-dl/)
40
39
  [![Python Support](https://img.shields.io/pypi/pyversions/ytp-dl.svg)](https://pypi.org/project/ytp-dl/)
41
40
  [![License](https://img.shields.io/pypi/l/ytp-dl.svg)](https://pypi.org/project/ytp-dl/)
42
41
  [![Downloads](https://img.shields.io/pypi/dm/ytp-dl.svg)](https://pypi.org/project/ytp-dl/)
43
42
 
44
- **ytp-dl** is a privacy-focused YouTube downloader that exposes a simple HTTP API for automated downloads.
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
- * Smart quality selection (prefers 1080p H.264 + AAC when available; no transcoding needed)
51
- * Audio downloads (extract audio as MP3)
52
- * HTTP API (Flask-based API with concurrency controls)
53
- * VPS-ready (includes an automated installer script for Ubuntu)
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.2 yt-dlp[default]
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 | Description | Default |
148
- | ---------------------- | ------------------------------------ | ------------------ |
149
- | `PORT` | API server port | `5000` |
150
- | `APP_DIR` | Installation directory | `/opt/ytp-dl` |
151
- | `YTPDL_MAX_CONCURRENT` | Max simultaneous downloads (API cap) | `1` |
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 | Description | Default |
158
- | ---------------------- | ------------------------------- | --------------------- |
159
- | `YTPDL_MAX_CONCURRENT` | Maximum concurrent downloads | `1` |
160
- | `YTPDL_VENV` | Path to virtualenv for ytp-dl | `/opt/ytp-dl/venv` |
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 FFmpeg
240
- * Installs Deno system-wide (required by yt-dlp for modern YouTube extraction)
241
- * Creates a virtualenv at `/opt/ytp-dl/venv`
242
- * Installs `ytp-dl==0.6.2` + `yt-dlp[default]` into the virtualenv
243
- * Sets up a systemd service on port 5000
244
- * Configures Gunicorn with gevent workers
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 + ffmpeg
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/ytp-dl/venv
254
- # - Installs ytp-dl==0.6.2 + yt-dlp[default] + gunicorn + gevent in that venv
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}" # API listen port
261
- APP_DIR="${APP_DIR:-/opt/ytp-dl}" # app/venv root
262
- VENV_DIR="${VENV_DIR:-${APP_DIR}/venv}" # python venv
263
-
264
- YTPDL_MAX_CONCURRENT="${YTPDL_MAX_CONCURRENT:-1}" # API concurrency cap
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
- python3-venv python3-pip curl ffmpeg ca-certificates unzip
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.2" "yt-dlp[default]" gunicorn gevent
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 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
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 an HTTP API
3
+ > A lightweight YouTube downloader with Mullvad VPN integration and HTTP API
4
4
 
5
5
  [![PyPI version](https://img.shields.io/pypi/v/ytp-dl.svg)](https://pypi.org/project/ytp-dl/)
6
6
  [![Python Support](https://img.shields.io/pypi/pyversions/ytp-dl.svg)](https://pypi.org/project/ytp-dl/)
7
7
  [![License](https://img.shields.io/pypi/l/ytp-dl.svg)](https://pypi.org/project/ytp-dl/)
8
8
  [![Downloads](https://img.shields.io/pypi/dm/ytp-dl.svg)](https://pypi.org/project/ytp-dl/)
9
9
 
10
- **ytp-dl** is a privacy-focused YouTube downloader that exposes a simple HTTP API for automated downloads.
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
- * Smart quality selection (prefers 1080p H.264 + AAC when available; no transcoding needed)
17
- * Audio downloads (extract audio as MP3)
18
- * HTTP API (Flask-based API with concurrency controls)
19
- * VPS-ready (includes an automated installer script for Ubuntu)
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.2 yt-dlp[default]
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 | Description | Default |
114
- | ---------------------- | ------------------------------------ | ------------------ |
115
- | `PORT` | API server port | `5000` |
116
- | `APP_DIR` | Installation directory | `/opt/ytp-dl` |
117
- | `YTPDL_MAX_CONCURRENT` | Max simultaneous downloads (API cap) | `1` |
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 | Description | Default |
124
- | ---------------------- | ------------------------------- | --------------------- |
125
- | `YTPDL_MAX_CONCURRENT` | Maximum concurrent downloads | `1` |
126
- | `YTPDL_VENV` | Path to virtualenv for ytp-dl | `/opt/ytp-dl/venv` |
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 FFmpeg
206
- * Installs Deno system-wide (required by yt-dlp for modern YouTube extraction)
207
- * Creates a virtualenv at `/opt/ytp-dl/venv`
208
- * Installs `ytp-dl==0.6.2` + `yt-dlp[default]` into the virtualenv
209
- * Sets up a systemd service on port 5000
210
- * Configures Gunicorn with gevent workers
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 + ffmpeg
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/ytp-dl/venv
220
- # - Installs ytp-dl==0.6.2 + yt-dlp[default] + gunicorn + gevent in that venv
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}" # API listen port
227
- APP_DIR="${APP_DIR:-/opt/ytp-dl}" # app/venv root
228
- VENV_DIR="${VENV_DIR:-${APP_DIR}/venv}" # python venv
229
-
230
- YTPDL_MAX_CONCURRENT="${YTPDL_MAX_CONCURRENT:-1}" # API concurrency cap
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
- python3-venv python3-pip curl ffmpeg ca-certificates unzip
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.2" "yt-dlp[default]" gunicorn gevent
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 gevent -w 1 \
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
- _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
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 _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,12 +79,14 @@ 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)
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 semaphore as soon as yt-dlp is done.
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
- return jsonify(error=f"Download failed: {str(e)}"), 500
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
- 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
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/ytpdl/venv")
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 checks
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
- flags = [
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
- mode = (extension or "mp4").lower().strip()
312
+ require_mullvad_login()
313
+ mullvad_connect(MULLVAD_LOCATION)
314
+ if not mullvad_wait_connected():
315
+ raise RuntimeError("Mullvad connection failed")
270
316
 
271
- if mode == "mp3":
272
- return _download_with_format(
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
- if mode == "best":
283
- # Try best first (may produce webm/mkv/etc).
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=_fmt_best(cap),
325
+ fmt="bestaudio",
289
326
  merge_output_format=None,
290
- extract_mp3=False,
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
- # Default / "mp4" mode: force Apple-safe MP4 up to cap.
303
- return _download_with_format(
304
- url=url,
305
- out_dir=out_dir,
306
- fmt=_fmt_mp4_apple_safe(cap),
307
- merge_output_format="mp4",
308
- extract_mp3=False,
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.2",
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.2
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 an HTTP API
36
+ > A lightweight YouTube downloader with Mullvad VPN integration and HTTP API
38
37
 
39
38
  [![PyPI version](https://img.shields.io/pypi/v/ytp-dl.svg)](https://pypi.org/project/ytp-dl/)
40
39
  [![Python Support](https://img.shields.io/pypi/pyversions/ytp-dl.svg)](https://pypi.org/project/ytp-dl/)
41
40
  [![License](https://img.shields.io/pypi/l/ytp-dl.svg)](https://pypi.org/project/ytp-dl/)
42
41
  [![Downloads](https://img.shields.io/pypi/dm/ytp-dl.svg)](https://pypi.org/project/ytp-dl/)
43
42
 
44
- **ytp-dl** is a privacy-focused YouTube downloader that exposes a simple HTTP API for automated downloads.
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
- * Smart quality selection (prefers 1080p H.264 + AAC when available; no transcoding needed)
51
- * Audio downloads (extract audio as MP3)
52
- * HTTP API (Flask-based API with concurrency controls)
53
- * VPS-ready (includes an automated installer script for Ubuntu)
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.2 yt-dlp[default]
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 | Description | Default |
148
- | ---------------------- | ------------------------------------ | ------------------ |
149
- | `PORT` | API server port | `5000` |
150
- | `APP_DIR` | Installation directory | `/opt/ytp-dl` |
151
- | `YTPDL_MAX_CONCURRENT` | Max simultaneous downloads (API cap) | `1` |
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 | Description | Default |
158
- | ---------------------- | ------------------------------- | --------------------- |
159
- | `YTPDL_MAX_CONCURRENT` | Maximum concurrent downloads | `1` |
160
- | `YTPDL_VENV` | Path to virtualenv for ytp-dl | `/opt/ytp-dl/venv` |
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 FFmpeg
240
- * Installs Deno system-wide (required by yt-dlp for modern YouTube extraction)
241
- * Creates a virtualenv at `/opt/ytp-dl/venv`
242
- * Installs `ytp-dl==0.6.2` + `yt-dlp[default]` into the virtualenv
243
- * Sets up a systemd service on port 5000
244
- * Configures Gunicorn with gevent workers
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 + ffmpeg
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/ytp-dl/venv
254
- # - Installs ytp-dl==0.6.2 + yt-dlp[default] + gunicorn + gevent in that venv
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}" # API listen port
261
- APP_DIR="${APP_DIR:-/opt/ytp-dl}" # app/venv root
262
- VENV_DIR="${VENV_DIR:-${APP_DIR}/venv}" # python venv
263
-
264
- YTPDL_MAX_CONCURRENT="${YTPDL_MAX_CONCURRENT:-1}" # API concurrency cap
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
- python3-venv python3-pip curl ffmpeg ca-certificates unzip
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.2" "yt-dlp[default]" gunicorn gevent
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 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
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,5 +1,4 @@
1
1
  yt-dlp[default]
2
2
  flask
3
3
  requests
4
- gevent
5
4
  gunicorn
File without changes
File without changes