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:
@@ -26,6 +26,7 @@ The project is based on NestJS and Angular.
|
|||||||
- [Process](#requirements)
|
- [Process](#requirements)
|
||||||
- [Requirements](#process)
|
- [Requirements](#process)
|
||||||
- [Environment variables](#environment-variables)
|
- [Environment variables](#environment-variables)
|
||||||
|
- [YouTube cookies](#youtube-cookies)
|
||||||
- [⚖️ License](#-license)
|
- [⚖️ License](#-license)
|
||||||
|
|
||||||
## 🚀 Installation
|
## 🚀 Installation
|
||||||
@@ -52,6 +53,7 @@ For detailed configuration, see available [environment variables](#environment-v
|
|||||||
```shell
|
```shell
|
||||||
docker run -d -p 3000:3000 \
|
docker run -d -p 3000:3000 \
|
||||||
-v /path/to/downloads:/spooty/backend/downloads \
|
-v /path/to/downloads:/spooty/backend/downloads \
|
||||||
|
-v /path/to/cookies.txt:/spooty/config/cookies.txt \
|
||||||
-e SPOTIFY_CLIENT_ID=your_client_id \
|
-e SPOTIFY_CLIENT_ID=your_client_id \
|
||||||
-e SPOTIFY_CLIENT_SECRET=your_client_secret \
|
-e SPOTIFY_CLIENT_SECRET=your_client_secret \
|
||||||
raiper34/spooty:latest
|
raiper34/spooty:latest
|
||||||
@@ -68,6 +70,7 @@ services:
|
|||||||
- "3000:3000"
|
- "3000:3000"
|
||||||
volumes:
|
volumes:
|
||||||
- /path/to/downloads:/spooty/backend/downloads
|
- /path/to/downloads:/spooty/backend/downloads
|
||||||
|
- /path/to/cookies.txt:/spooty/config/cookies.txt
|
||||||
environment:
|
environment:
|
||||||
- SPOTIFY_CLIENT_ID=your_client_id
|
- SPOTIFY_CLIENT_ID=your_client_id
|
||||||
- SPOTIFY_CLIENT_SECRET=your_client_secret
|
- SPOTIFY_CLIENT_SECRET=your_client_secret
|
||||||
@@ -102,7 +105,7 @@ Spooty can be also build from source files on your own.
|
|||||||
Some behaviour and settings of Spooty can be configured using environment variables and `.env` file.
|
Some behaviour and settings of Spooty can be configured using environment variables and `.env` file.
|
||||||
|
|
||||||
Name | Default | Description |
|
Name | Default | Description |
|
||||||
----------------------|---------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------|
|
-------------------------|---------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
DB_PATH | `./config/db.sqlite` (relative to backend) | Path where Spooty database will be stored |
|
DB_PATH | `./config/db.sqlite` (relative to backend) | Path where Spooty database will be stored |
|
||||||
FE_PATH | `../frontend/browser` (relative to backend) | Path to frontend part of application |
|
FE_PATH | `../frontend/browser` (relative to backend) | Path to frontend part of application |
|
||||||
DOWNLOADS_PATH | `./downloads` (relative to backend) | Path where downaloded files will be stored |
|
DOWNLOADS_PATH | `./downloads` (relative to backend) | Path where downaloded files will be stored |
|
||||||
@@ -115,16 +118,40 @@ Some behaviour and settings of Spooty can be configured using environment variab
|
|||||||
SPOTIFY_CLIENT_ID | your_client_id | Client ID of your Spotify application (required) |
|
SPOTIFY_CLIENT_ID | your_client_id | Client ID of your Spotify application (required) |
|
||||||
SPOTIFY_CLIENT_SECRET | your_client_secret | Client Secret of your Spotify application (required) |
|
SPOTIFY_CLIENT_SECRET | your_client_secret | Client Secret of your Spotify application (required) |
|
||||||
YT_DOWNLOADS_PER_MINUTE | 3 | Set the maximum number of YouTube downloads started per minute |
|
YT_DOWNLOADS_PER_MINUTE | 3 | Set the maximum number of YouTube downloads started per minute |
|
||||||
YT_COOKIES | | Allows you to pass your YouTube cookies to bypass some download restrictions. See [below](#how-to-get-your-youtube-cookies) for instructions. |
|
YT_COOKIES | | Browser name to automatically extract YouTube cookies from (e.g. `chrome`, `firefox`). Only works when running Spooty natively (not in Docker). See [below](#yt_cookies---browser-based-cookies-non-docker). |
|
||||||
|
YT_COOKIES_FILE | `./config/cookies.txt` | Path to a Netscape-format `cookies.txt` file. Recommended for Docker deployments. See [below](#yt_cookies_file---cookies-file-recommended-for-docker). |
|
||||||
|
|
||||||
### How to get your YouTube cookies (using browser dev tools):
|
### YouTube cookies
|
||||||
1. Go to https://www.youtube.com and log in if needed.
|
|
||||||
2. Open the browser developer tools (F12 or right click > Inspect).
|
YouTube may block or throttle downloads without authentication cookies. Spooty supports two ways to provide them — use the one that fits your setup.
|
||||||
3. Go to the "Application" tab (in Chrome) or "Storage" (in Firefox).
|
|
||||||
4. In the left menu, find "Cookies" and select https://www.youtube.com.
|
#### `YT_COOKIES` — browser-based cookies (non-Docker)
|
||||||
5. Copy all the cookies (name=value) and join them with a semicolon and a space, like:
|
|
||||||
VISITOR_INFO1_LIVE=xxxx; YSC=xxxx; SID=xxxx; ...
|
Set `YT_COOKIES` to the name of your browser and yt-dlp will automatically read cookies directly from it.
|
||||||
6. Paste this string into the YT_COOKIES environment variable (in your .env or Docker config).
|
Supported values: `chrome`, `firefox`, `edge`, `safari`, `brave`, `opera`, `chromium`.
|
||||||
|
|
||||||
|
```
|
||||||
|
YT_COOKIES=chrome
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> This only works when Spooty runs on the same machine as your browser (i.e. not in Docker, where no browser is present).
|
||||||
|
|
||||||
|
#### `YT_COOKIES_FILE` — cookies file (recommended for Docker)
|
||||||
|
|
||||||
|
Export your YouTube cookies as a Netscape `cookies.txt` file and provide its path. This is the recommended approach for Docker deployments.
|
||||||
|
|
||||||
|
**How to get your `cookies.txt` file:**
|
||||||
|
1. Install a browser extension that exports cookies in Netscape format, e.g. [Get cookies.txt LOCALLY](https://chrome.google.com/webstore/detail/get-cookiestxt-locally/cclelndahbckbenkjhflpdbgdldlbecc) for Chrome or [cookies.txt](https://addons.mozilla.org/en-US/firefox/addon/cookies-txt/) for Firefox.
|
||||||
|
2. Go to https://www.youtube.com and log in.
|
||||||
|
3. Use the extension to export cookies for `youtube.com` and save the file as `cookies.txt`.
|
||||||
|
|
||||||
|
**Docker usage:**
|
||||||
|
|
||||||
|
Bind mount the `cookies.txt` file into the container and set `YT_COOKIES_FILE` to its path inside the container. See the [Environment variables](#environment-variables) section for details.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> `YT_COOKIES` takes priority over `YT_COOKIES_FILE` if both are set.
|
||||||
|
|
||||||
# ⚖️ License
|
# ⚖️ License
|
||||||
[MIT](https://choosealicense.com/licenses/mit/)
|
[MIT](https://choosealicense.com/licenses/mit/)
|
||||||
|
|||||||
@@ -12,5 +12,6 @@ REDIS_RUN=false
|
|||||||
SPOTIFY_CLIENT_ID=your_client_id
|
SPOTIFY_CLIENT_ID=your_client_id
|
||||||
SPOTIFY_CLIENT_SECRET=your_client_secret
|
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
|
YT_DOWNLOADS_PER_MINUTE=3
|
||||||
@@ -6,4 +6,6 @@ export enum EnvironmentEnum {
|
|||||||
REDIS_PORT = 'REDIS_PORT',
|
REDIS_PORT = 'REDIS_PORT',
|
||||||
REDIS_HOST = 'REDIS_HOST',
|
REDIS_HOST = 'REDIS_HOST',
|
||||||
REDIS_RUN = 'REDIS_RUN',
|
REDIS_RUN = 'REDIS_RUN',
|
||||||
|
YT_COOKIES = 'YT_COOKIES',
|
||||||
|
YT_COOKIES_FILE = 'YT_COOKIES_FILE',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { TrackService } from '../track/track.service';
|
|||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from '@nestjs/config';
|
||||||
import { YtDlp } from 'ytdlp-nodejs';
|
import { YtDlp } from 'ytdlp-nodejs';
|
||||||
import * as yts from 'yt-search';
|
import * as yts from 'yt-search';
|
||||||
|
import * as fs from 'fs';
|
||||||
const NodeID3 = require('node-id3');
|
const NodeID3 = require('node-id3');
|
||||||
|
|
||||||
const HEADERS = {
|
const HEADERS = {
|
||||||
@@ -25,6 +26,27 @@ export class YoutubeService {
|
|||||||
return url;
|
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> {
|
async downloadAndFormat(track: TrackEntity, output: string): Promise<void> {
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
`Downloading ${track.artist} - ${track.name} (${track.youtubeUrl}) from YT`,
|
`Downloading ${track.artist} - ${track.name} (${track.youtubeUrl}) from YT`,
|
||||||
@@ -39,7 +61,7 @@ export class YoutubeService {
|
|||||||
this.configService.get<'m4a'>(EnvironmentEnum.FORMAT),
|
this.configService.get<'m4a'>(EnvironmentEnum.FORMAT),
|
||||||
{
|
{
|
||||||
output,
|
output,
|
||||||
cookiesFromBrowser: this.configService.get<string>('YT_COOKIES'),
|
...this.getCookiesOptions(),
|
||||||
headers: HEADERS,
|
headers: HEADERS,
|
||||||
jsRuntime: 'node',
|
jsRuntime: 'node',
|
||||||
audioQuality: this.configService.get<string>('QUALITY'),
|
audioQuality: this.configService.get<string>('QUALITY'),
|
||||||
|
|||||||
Reference in New Issue
Block a user