Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a7f433988 | ||
|
|
84c5c36672 |
@@ -1,5 +1,5 @@
|
|||||||
import { type ChildProcess, spawn } from "node:child_process";
|
import { type ChildProcess, spawn } from "node:child_process";
|
||||||
import { chmodSync, mkdtempSync, rmSync } from "node:fs";
|
import { chmodSync, existsSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||||
import { tmpdir } from "node:os";
|
import { tmpdir } from "node:os";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { PassThrough, type Readable } from "node:stream";
|
import { PassThrough, type Readable } from "node:stream";
|
||||||
@@ -217,6 +217,48 @@ function buildNotInstalledError(): Error {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the yt-dlp --cookies args. YouTube blocks anonymous embeds with a
|
||||||
|
* "Sign in to confirm you're not a bot" 403 unless yt-dlp is given a logged-
|
||||||
|
* in account's cookies. The path is configurable via GMW_YT_COOKIES_PATH
|
||||||
|
* (default: the BWS-provided file the deploy writes to /etc/.../ytcookies.txt).
|
||||||
|
* If the file doesn't exist we pass nothing and fall back to anon (YouTube
|
||||||
|
* may 403 — screen share will fail gracefully, not crash).
|
||||||
|
*/
|
||||||
|
var _cachedCookiePath: string | null = null;
|
||||||
|
function buildCookieArgs(): string[] {
|
||||||
|
// Single source of truth: BWS injects the account cookies via env
|
||||||
|
// (gmw_yt_downloader_cookies → GMW_YT_DOWNLOADER_COOKIES by bws-exec).
|
||||||
|
// We materialize them to a temp Netscape file because yt-dlp --cookies
|
||||||
|
// only accepts a file path, not stdin, and multiline env values are not
|
||||||
|
// reliable to pass directly on the spawn argv. Falls back to the on-disk
|
||||||
|
// file at GMW_YT_COOKIES_PATH (or /etc/gmw-discord-gateway/ytcookies.txt)
|
||||||
|
// which the Nix deploy writes from BWS once at start.
|
||||||
|
if (_cachedCookiePath) return ["--cookies", _cachedCookiePath];
|
||||||
|
const envCookies = process.env.GMW_YT_DOWNLOADER_COOKIES?.trim();
|
||||||
|
if (envCookies && envCookies.includes("LOGIN_INFO")) {
|
||||||
|
const fdPath = join(tmpdir(), `gmw-ytcookies.${process.pid}.txt`);
|
||||||
|
writeFileSync(fdPath, envCookies);
|
||||||
|
try { chmodSync(fdPath, 0o600); } catch { /* best-effort */ }
|
||||||
|
_cachedCookiePath = fdPath;
|
||||||
|
logger.info({ cookiePath: fdPath, source: "GMW_YT_DOWNLOADER_COOKIES env" }, "Using YouTube cookies (from BWS env)");
|
||||||
|
return ["--cookies", fdPath];
|
||||||
|
}
|
||||||
|
const cookiePath =
|
||||||
|
process.env.GMW_YT_COOKIES_PATH ?? "/etc/gmw-discord-gateway/ytcookies.txt";
|
||||||
|
try {
|
||||||
|
if (cookiePath && existsSync(cookiePath)) {
|
||||||
|
_cachedCookiePath = cookiePath;
|
||||||
|
logger.info({ cookiePath, source: "on-disk file" }, "Using YouTube cookies for yt-dlp");
|
||||||
|
return ["--cookies", cookiePath];
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
/* ignore — fallback to anon */
|
||||||
|
}
|
||||||
|
logger.warn("No YouTube cookies available; yt-dlp will use anonymous (YouTube may 403)");
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Public API
|
// Public API
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -241,6 +283,7 @@ export function resolveMediaUrl(
|
|||||||
): Promise<MediaSourceResolution> {
|
): Promise<MediaSourceResolution> {
|
||||||
return new Promise<MediaSourceResolution>((resolve, reject) => {
|
return new Promise<MediaSourceResolution>((resolve, reject) => {
|
||||||
const format = options?.quality ?? "bestaudio";
|
const format = options?.quality ?? "bestaudio";
|
||||||
|
const cookieArgs = buildCookieArgs();
|
||||||
const args = [
|
const args = [
|
||||||
"-f",
|
"-f",
|
||||||
format,
|
format,
|
||||||
@@ -248,6 +291,7 @@ export function resolveMediaUrl(
|
|||||||
"-",
|
"-",
|
||||||
"--no-progress",
|
"--no-progress",
|
||||||
"--no-warnings",
|
"--no-warnings",
|
||||||
|
...cookieArgs,
|
||||||
"--print",
|
"--print",
|
||||||
"before_dl:title",
|
"before_dl:title",
|
||||||
"--print",
|
"--print",
|
||||||
@@ -402,6 +446,7 @@ export function getDirectScreenInput(url: string): Promise<Readable> {
|
|||||||
const tmpDir = mkdtempSync(join(tmpdir(), "gmw-ytdlp-"));
|
const tmpDir = mkdtempSync(join(tmpdir(), "gmw-ytdlp-"));
|
||||||
chmodSync(tmpDir, 0o1777);
|
chmodSync(tmpDir, 0o1777);
|
||||||
|
|
||||||
|
const cookieArgs = buildCookieArgs();
|
||||||
const args = [
|
const args = [
|
||||||
"-f",
|
"-f",
|
||||||
"bestvideo[protocol^=http]+bestaudio[protocol^=http]/best[protocol^=http]/best",
|
"bestvideo[protocol^=http]+bestaudio[protocol^=http]/best[protocol^=http]/best",
|
||||||
@@ -410,6 +455,7 @@ export function getDirectScreenInput(url: string): Promise<Readable> {
|
|||||||
"--no-playlist",
|
"--no-playlist",
|
||||||
"--no-warnings",
|
"--no-warnings",
|
||||||
"--no-progress",
|
"--no-progress",
|
||||||
|
...cookieArgs,
|
||||||
"-P",
|
"-P",
|
||||||
tmpDir,
|
tmpDir,
|
||||||
url,
|
url,
|
||||||
|
|||||||
Reference in new issue
Block a user