feat: add YT_COOKIES_FILE support for Netscape cookies.txt

Previously, the only way to pass YouTube cookies was via the YT_COOKIES
env var, which was incorrectly documented as a raw cookie string
(name=value; ...) but internally mapped to yt-dlp's
--cookies-from-browser, which expects a browser name (e.g. chrome,
firefox). This made the feature non-functional as documented and
unusable in Docker environments where no browser is present.

This change introduces a second option, YT_COOKIES_FILE, which accepts
a path to a Netscape-format cookies.txt file and maps to yt-dlp's
--cookies flag. This is the recommended approach for Docker deployments,
where users can export cookies via a browser extension and bind mount
the file into the container. The file is only passed to yt-dlp if it
actually exists on disk, so the default path (./config/cookies.txt)
does not cause errors when no file is provided.

YT_COOKIES retains priority over YT_COOKIES_FILE if both are set.

README is updated to clarify the correct usage of both options,
fix the misleading cookie string instructions, add a dedicated
YouTube cookies section to the table of contents, and include the
cookies.txt bind mount in the Docker usage examples.

Resolves #40, resolves #48
This commit is contained in:
Raiper34
2026-05-03 21:35:51 +00:00
parent 5747c879ed
commit 153f502546
4 changed files with 77 additions and 25 deletions
+2 -1
View File
@@ -12,5 +12,6 @@ REDIS_RUN=false
SPOTIFY_CLIENT_ID=your_client_id
SPOTIFY_CLIENT_SECRET=your_client_secret
YT_COOKIES="AEC=xxx; APISID=xxx; APISID=xxx; HSID=xxx; HSID=xxx; LOGIN_INFO=xxx; NID=xxx; PREF=xxx; SAPISID=xxx; SAPISID=Nxxx; SEARCH_SAMESITE=xxx; SID=xxx; SID=xxx; SIDCC=xxx; SIDCC=xxx; SSID=xxx; SSID=xxx; STRP=xxx; VISITOR_PRIVACY_METADATA=xxx; YSC=xxx"
YT_COOKIES=
YT_COOKIES_FILE=./config/cookies.txt
YT_DOWNLOADS_PER_MINUTE=3
+2
View File
@@ -6,4 +6,6 @@ export enum EnvironmentEnum {
REDIS_PORT = 'REDIS_PORT',
REDIS_HOST = 'REDIS_HOST',
REDIS_RUN = 'REDIS_RUN',
YT_COOKIES = 'YT_COOKIES',
YT_COOKIES_FILE = 'YT_COOKIES_FILE',
}
+23 -1
View File
@@ -5,6 +5,7 @@ import { TrackService } from '../track/track.service';
import { ConfigService } from '@nestjs/config';
import { YtDlp } from 'ytdlp-nodejs';
import * as yts from 'yt-search';
import * as fs from 'fs';
const NodeID3 = require('node-id3');
const HEADERS = {
@@ -25,6 +26,27 @@ export class YoutubeService {
return url;
}
private getCookiesOptions(): {
cookiesFromBrowser?: string;
cookies?: string;
} {
const cookiesBrowser = this.configService.get<string>(
EnvironmentEnum.YT_COOKIES,
);
if (cookiesBrowser) {
this.logger.debug(`Using cookies from browser: ${cookiesBrowser}`);
return { cookiesFromBrowser: cookiesBrowser };
}
const cookiesFile = this.configService.get<string>(
EnvironmentEnum.YT_COOKIES_FILE,
);
if (cookiesFile && fs.existsSync(cookiesFile)) {
this.logger.debug(`Using cookies file: ${cookiesFile}`);
return { cookies: cookiesFile };
}
return {};
}
async downloadAndFormat(track: TrackEntity, output: string): Promise<void> {
this.logger.debug(
`Downloading ${track.artist} - ${track.name} (${track.youtubeUrl}) from YT`,
@@ -39,7 +61,7 @@ export class YoutubeService {
this.configService.get<'m4a'>(EnvironmentEnum.FORMAT),
{
output,
cookiesFromBrowser: this.configService.get<string>('YT_COOKIES'),
...this.getCookiesOptions(),
headers: HEADERS,
jsRuntime: 'node',
audioQuality: this.configService.get<string>('QUALITY'),