openvibers/openvibe.tools Commits
YT: optional proxy/cookies identity from the environment, clearer refusal message; deploy.sh refreshes the vendored shared package
Changes
@@@ -18,6 +18,20 @@ const fileIndex = new Map(); // id → { filePath, mime, ext, size, expirelet currentConcurrent = 0;// Ensure downloads dir++/**++ * Optional network identity for yt-dlp, set in the unit's environment (never in the repo):++ * YT_PROXY e.g. socks5://127.0.0.1:1080 — used when YouTube refuses this server's address++ * YT_COOKIES_FILE path to a Netscape cookies.txt readable by the service user++ */++function identityArgs() {++ const out = [];++ const proxy = String(process.env.YT_PROXY || '').trim();++ if (/^(https?|socks[45]h?):\/\/[^\s]+$/i.test(proxy)) out.push('--proxy', proxy);++ const cookies = String(process.env.YT_COOKIES_FILE || '').trim();++ if (cookies && fs.existsSync(cookies)) out.push('--cookies', cookies);++ return out;++}++function ensureDir() {const dir = path.resolve(config.downloadsDir);if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });@@@ -87,6 +101,7 @@ function getInfo(url) {'--dump-json','--no-warnings','--no-playlist',++ ...identityArgs(),'--skip-download',cleanUrl,];@@@ -156,7 +171,7 @@ function getAvailableFormats(info) {/** yt-dlp's last stderr line → something the person can act on (the raw line is logged). */function friendlyDownloadError(line) {const t = String(line || '');-- if (/Sign in to confirm|not a bot|cookies/i.test(t)) return 'YouTube is asking this server to prove it is not a bot — try again in a few minutes';++ if (/Sign in to confirm|not a bot|cookies/i.test(t)) return 'YouTube is refusing downloads from this server right now. This is on YouTube\'s side; please try again later.';if (/Private video|members-only|login required/i.test(t)) return 'This video is private or members-only';if (/Video unavailable|has been removed|not available/i.test(t)) return 'This video is unavailable';if (/\bage[- ]restrict|confirm your age/i.test(t)) return 'Age-restricted videos cannot be downloaded';@@@ -305,6 +320,7 @@ function startDownload(url, quality = 'best', opts = {}) {...(preset.audio ? [] : ['--merge-output-format', preset.ext || 'mp4']),'-o', outputTemplate,'--no-playlist',++ ...identityArgs(),'--no-warnings','--no-simulate','--progress',nnew file mode 100755@@@ -0,0 +1,17 @@++#!/usr/bin/env bash++# Deploy OpenVibe.Tools on the host: pull, refresh the vendored shared package inside each app's++# node_modules (npm copies file: dependencies on this host, so a pull alone leaves them stale),++# install new dependencies when a package.json changed, restart the units, check health.++set -euo pipefail++cd "$(dirname "$0")/../.."++BEFORE=$(git rev-parse HEAD); git pull -q --ff-only; AFTER=$(git rev-parse HEAD)++for app in apps/*/; do++ [ -f "$app/package.json" ] || continue++ if git diff --name-only "$BEFORE" "$AFTER" -- "$app/package.json" | grep -q . || [ ! -d "$app/node_modules" ]; then (cd "$app" && npm install --omit=dev --no-audit --no-fund --silent); fi++ d="$app/node_modules/openvibe-shared"; [ -L "$d" ] || { mkdir -p "$d"; rsync -a --delete --exclude node_modules vendor/openvibe-shared/ "$d/"; }++done++UNITS=$(systemctl list-unit-files 'openvibe-tools*' --no-legend | awk '{print $1}')++sudo systemctl restart $UNITS++sleep 5++for u in $UNITS; do printf '%-34s %s\n' "$u" "$(systemctl is-active "$u")"; done++curl -fsS -o /dev/null -H 'Host: openvibe.tools' http://127.0.0.1:4001/api/health && echo "gateway healthy ($BEFORE → $AFTER)"
@@ -18,6 +18,20 @@ const fileIndex = new Map(); // id → { filePath, mime, ext, size, expire
@@ -18,6 +18,20 @@ const fileIndex = new Map(); // id → { filePath, mime, ext, size, expire
let currentConcurrent = 0;
let currentConcurrent = 0;
// Ensure downloads dir
// Ensure downloads dir
+/**
+ * Optional network identity for yt-dlp, set in the unit's environment (never in the repo):
+ * YT_PROXY e.g. socks5://127.0.0.1:1080 — used when YouTube refuses this server's address
+ * YT_COOKIES_FILE path to a Netscape cookies.txt readable by the service user
+ */
+function identityArgs() {
+ const out = [];
+ const proxy = String(process.env.YT_PROXY || '').trim();
+ if (/^(https?|socks[45]h?):\/\/[^\s]+$/i.test(proxy)) out.push('--proxy', proxy);
+ const cookies = String(process.env.YT_COOKIES_FILE || '').trim();
+ if (cookies && fs.existsSync(cookies)) out.push('--cookies', cookies);
+ return out;
+}
+
function ensureDir() {
function ensureDir() {
const dir = path.resolve(config.downloadsDir);
const dir = path.resolve(config.downloadsDir);
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
@@ -87,6 +101,7 @@ function getInfo(url) {
@@ -87,6 +101,7 @@ function getInfo(url) {
'--dump-json',
'--dump-json',
'--no-warnings',
'--no-warnings',
'--no-playlist',
'--no-playlist',
+ ...identityArgs(),
'--skip-download',
'--skip-download',
cleanUrl,
cleanUrl,
];
];
@@ -156,7 +171,7 @@ function getAvailableFormats(info) {
@@ -156,7 +171,7 @@ function getAvailableFormats(info) {
/** yt-dlp's last stderr line → something the person can act on (the raw line is logged). */
/** yt-dlp's last stderr line → something the person can act on (the raw line is logged). */
function friendlyDownloadError(line) {
function friendlyDownloadError(line) {
const t = String(line || '');
const t = String(line || '');
- if (/Sign in to confirm|not a bot|cookies/i.test(t)) return 'YouTube is asking this server to prove it is not a bot — try again in a few minutes';
+ if (/Sign in to confirm|not a bot|cookies/i.test(t)) return 'YouTube is refusing downloads from this server right now. This is on YouTube\'s side; please try again later.';
if (/Private video|members-only|login required/i.test(t)) return 'This video is private or members-only';
if (/Private video|members-only|login required/i.test(t)) return 'This video is private or members-only';
if (/Video unavailable|has been removed|not available/i.test(t)) return 'This video is unavailable';
if (/Video unavailable|has been removed|not available/i.test(t)) return 'This video is unavailable';
if (/\bage[- ]restrict|confirm your age/i.test(t)) return 'Age-restricted videos cannot be downloaded';
if (/\bage[- ]restrict|confirm your age/i.test(t)) return 'Age-restricted videos cannot be downloaded';
@@ -305,6 +320,7 @@ function startDownload(url, quality = 'best', opts = {}) {
@@ -305,6 +320,7 @@ function startDownload(url, quality = 'best', opts = {}) {
...(preset.audio ? [] : ['--merge-output-format', preset.ext || 'mp4']),
...(preset.audio ? [] : ['--merge-output-format', preset.ext || 'mp4']),
'-o', outputTemplate,
'-o', outputTemplate,
'--no-playlist',
'--no-playlist',
+ ...identityArgs(),
'--no-warnings',
'--no-warnings',
'--no-simulate',
'--no-simulate',
'--progress',
'--progress',
new file mode 100755
new file mode 100755
@@ -0,0 +1,17 @@
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+# Deploy OpenVibe.Tools on the host: pull, refresh the vendored shared package inside each app's
+# node_modules (npm copies file: dependencies on this host, so a pull alone leaves them stale),
+# install new dependencies when a package.json changed, restart the units, check health.
+set -euo pipefail
+cd "$(dirname "$0")/../.."
+BEFORE=$(git rev-parse HEAD); git pull -q --ff-only; AFTER=$(git rev-parse HEAD)
+for app in apps/*/; do
+ [ -f "$app/package.json" ] || continue
+ if git diff --name-only "$BEFORE" "$AFTER" -- "$app/package.json" | grep -q . || [ ! -d "$app/node_modules" ]; then (cd "$app" && npm install --omit=dev --no-audit --no-fund --silent); fi
+ d="$app/node_modules/openvibe-shared"; [ -L "$d" ] || { mkdir -p "$d"; rsync -a --delete --exclude node_modules vendor/openvibe-shared/ "$d/"; }
+done
+UNITS=$(systemctl list-unit-files 'openvibe-tools*' --no-legend | awk '{print $1}')
+sudo systemctl restart $UNITS
+sleep 5
+for u in $UNITS; do printf '%-34s %s\n' "$u" "$(systemctl is-active "$u")"; done
+curl -fsS -o /dev/null -H 'Host: openvibe.tools' http://127.0.0.1:4001/api/health && echo "gateway healthy ($BEFORE → $AFTER)"