diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2188d0a..e714779 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,171 +1,122 @@
-### Changelog
+# Changelog
-All notable changes to this project will be documented in this file. Dates are displayed in UTC.
+All notable changes to the hardened Spooty branch are documented here.
-#### [2.4.2](https://github.com/Raiper34/spooty/compare/2.4.1...2.4.2)
+This project follows a practical chronological changelog rather than autogenerated dependency noise.
-- fix(spotify): use data.next for playlist pagination instead of filtered track count [`#52`](https://github.com/Raiper34/spooty/issues/52) [`#35`](https://github.com/Raiper34/spooty/issues/35)
+---
-#### [2.4.1](https://github.com/Raiper34/spooty/compare/2.4.0...2.4.1)
+# Unreleased
-> 7 February 2026
+## Added
-- fix(docker): fix docker node version [`a14f5ea`](https://github.com/Raiper34/spooty/commit/a14f5eab6805acdf4755dbd8f37a289a0d675c12)
+### Spotify OAuth Integration
-#### [2.4.0](https://github.com/Raiper34/spooty/compare/2.3.4...2.4.0)
+- Added Spotify OAuth login flow
+- Added `/api/spotify/login`
+- Added `/api/spotify/callback`
+- Added `/api/spotify/status`
+- Added persistent Spotify user access token storage
+- Added authenticated Spotify playlist retrieval
+- Added OAuth frontend bootstrap flow
-> 7 February 2026
+### Large Playlist Support
-- feat(files): add possibility to set the audio quality of downloaded files [`#39`](https://github.com/Raiper34/spooty/issues/39)
-- fix(downloading): fix downloading songs from browser with special characters [`#41`](https://github.com/Raiper34/spooty/issues/41)
-- fix(downloadin): fix ejs runtime problem [`#38`](https://github.com/Raiper34/spooty/issues/38)
+- Fixed Spotify playlist truncation at 100 tracks
+- Added full Spotify pagination support
+- Verified support for playlists exceeding 300 tracks
+- Improved playlist queue handling stability
-#### [2.3.4](https://github.com/Raiper34/spooty/compare/2.3.3...2.3.4)
+### Queue Hardening
-> 4 February 2026
+- Added configurable YouTube search pacing
+- Added configurable YouTube download pacing
+- Added sequential search queue behavior
+- Reduced aggressive request bursts toward YouTube
+- Improved queue logging visibility
-- fix(ytdl): Upgrade ytdl package (automated) [`ef32347`](https://github.com/Raiper34/spooty/commit/ef32347903d7caa2cc72c9c54341195b2ac0bb96)
+### Security
-#### [2.3.3](https://github.com/Raiper34/spooty/compare/2.3.2...2.3.3)
+- Added token-based frontend access mode
+- Added external secret handling recommendations
+- Added isolated Spotify credential storage
+- Improved Docker credential handling documentation
-> 30 January 2026
+### Frontend
-- fix(ytdl): Upgrade ytdl package (automated) [`cc552d1`](https://github.com/Raiper34/spooty/commit/cc552d1def309a3633aa67ca7f3659eb44ef47b2)
+- Added Spotify login integration
+- Added auth token bootstrap from URL
+- Improved playlist rendering behavior
+- Improved large playlist progress tracking
-#### [2.3.2](https://github.com/Raiper34/spooty/compare/2.3.1...2.3.2)
+### Docker
-> 26 January 2026
+- Improved hardened Docker runtime flow
+- Added explicit environment variable examples
+- Added safer default pacing recommendations
+- Improved deployment documentation
-- fix(ytdl): Upgrade ytdl package (automated) [`531730b`](https://github.com/Raiper34/spooty/commit/531730bc57f9ab1339277ad995f37611a9f55aa8)
+---
-#### [2.3.1](https://github.com/Raiper34/spooty/compare/2.3.0...2.3.1)
+# 2.4.2
-> 23 January 2026
+## Fixed
-- fix(ytdl): Upgrade ytdl package (automated) [`08b83bf`](https://github.com/Raiper34/spooty/commit/08b83bf9de70999bddb47cd1d64d63d1ab550d88)
-- docs(readme): change redirect URI [`8162a2a`](https://github.com/Raiper34/spooty/commit/8162a2a21f762310f857dfc42cad53831769ed66)
+- Fixed Spotify playlist pagination using `data.next`
+- Fixed incomplete playlist retrieval behavior
-#### [2.3.0](https://github.com/Raiper34/spooty/compare/2.2.1...2.3.0)
+---
-> 29 December 2025
+# 2.4.1
-- feat(track): allow download of individual track and fix album art [`#29`](https://github.com/Raiper34/spooty/issues/29)
-- docs(readme): remove demo image and gif from readme [`a2ac579`](https://github.com/Raiper34/spooty/commit/a2ac579854a6cbc95729d434c514fb2309f7a3bb)
+## Fixed
-#### [2.2.1](https://github.com/Raiper34/spooty/compare/2.2.0...2.2.1)
+- Fixed Docker Node.js version compatibility
-> 15 November 2025
+---
-- fix(downloading): migrate from @distube/ytdl-core to ytdlp-nodejs yt downloading library [`5795f7c`](https://github.com/Raiper34/spooty/commit/5795f7cc178ab4d8a8d3d8a7e94f33743ff6e9e0)
-- fix(docker): add python3 dependency into docker image for yt download library [`46d7c66`](https://github.com/Raiper34/spooty/commit/46d7c6699bc8698896b22837e8507885677a71bd)
-- docs(readme): fix yt cookies section heading [`285578f`](https://github.com/Raiper34/spooty/commit/285578ff1ed1d7fe14d7ddd0d95a4b8079276e4e)
-- docs(readme): add docker versio badge into readme [`4be3743`](https://github.com/Raiper34/spooty/commit/4be3743f7b5a8019bd6ed0d2248f23a254a82603)
+# 2.4.0
-#### [2.2.0](https://github.com/Raiper34/spooty/compare/2.1.1...2.2.0)
+## Added
-> 17 October 2025
+- Added configurable audio quality selection
-- feat(spotify): integrate Spotify API for playlist metadata and track retrieval [`de55e42`](https://github.com/Raiper34/spooty/commit/de55e42fcd246d86e8d3156d8e0ab4898b5755d6)
-- feat(youtube): use youtube cookies to bypass limitation & add timeout between each downloads [`4c05178`](https://github.com/Raiper34/spooty/commit/4c051782fd8808a808fe49fe1509ec0b0db9d05e)
+## Fixed
-#### [2.1.1](https://github.com/Raiper34/spooty/compare/2.1.0...2.1.1)
+- Fixed special character download issues
+- Fixed EJS runtime issue
-> 13 August 2025
+---
-- feat(gui): add version to gui header [`#25`](https://github.com/Raiper34/spooty/issues/25)
-- fix(track): fix track missing artist and title tags [`b93993c`](https://github.com/Raiper34/spooty/commit/b93993c6cf034dccad3df08485d2126614874743)
-- style(gui): change gui primary to match spotify primary color [`5068fa8`](https://github.com/Raiper34/spooty/commit/5068fa8a4b724ef4a1060dc4d83cc80afee08bad)
+# 2.3.0
-#### [2.1.0](https://github.com/Raiper34/spooty/compare/2.0.11...2.1.0)
+## Added
-> 12 August 2025
+- Added individual track downloads
+- Added cover art support
-- feat(track): add track cover art into downloaded file [`#24`](https://github.com/Raiper34/spooty/issues/24)
+---
-#### [2.0.11](https://github.com/Raiper34/spooty/compare/2.0.10...2.0.11)
+# 2.2.0
-> 14 June 2025
+## Added
-- fix(ytdl): Upgrade ytdl package (automated) [`fe79302`](https://github.com/Raiper34/spooty/commit/fe79302c2a747093df39405e42c06c08957acc3c)
+- Added Spotify API integration
+- Added YouTube cookie support
+- Added download timeout handling
-#### [2.0.10](https://github.com/Raiper34/spooty/compare/2.0.9...2.0.10)
+---
-> 4 June 2025
+# 2.0.0
-- fix(ytdl): Upgrade ytdl package (automated) [`f74a4d2`](https://github.com/Raiper34/spooty/commit/f74a4d2ed9da50e29f4e44b0027b2891c7473ec6)
+## Added
-#### [2.0.9](https://github.com/Raiper34/spooty/compare/2.0.8...2.0.9)
+- Introduced queue-based backend architecture
-> 8 May 2025
+---
-- fix(ytdl): Upgrade ytdl package (automated) [`cc6a201`](https://github.com/Raiper34/spooty/commit/cc6a20196d474152eecaeb9862edfb3505ed3106)
+# 1.0.0
-#### [2.0.8](https://github.com/Raiper34/spooty/compare/2.0.7...2.0.8)
+## Added
-> 27 April 2025
-
-- docs(readme): remove docs, netlify and docsify, keep only readme docs for now [`bc9482c`](https://github.com/Raiper34/spooty/commit/bc9482c6cb9a47aa0d96fa34866546ec552d1e3c)
-- fix(ytdl): Upgrade ytdl package (automated) [`17b875e`](https://github.com/Raiper34/spooty/commit/17b875e2825cb739767565be5d86aba3a48a9208)
-
-#### [2.0.7](https://github.com/Raiper34/spooty/compare/2.0.6...2.0.7)
-
-> 5 April 2025
-
-- fix(ytdl): Upgrade ytdl package (automated) [`9653db5`](https://github.com/Raiper34/spooty/commit/9653db54ea48e93526f2124b11b8012d987b7bcc)
-
-#### [2.0.6](https://github.com/Raiper34/spooty/compare/2.0.5...2.0.6)
-
-> 1 April 2025
-
-- ci(netlify): remove netlify cli from deps [`e4cde85`](https://github.com/Raiper34/spooty/commit/e4cde8585ba0dfc422cec89360612947ebe92ff1)
-- fix(ytdl): Upgrade ytdl package (automated) [`5f30f31`](https://github.com/Raiper34/spooty/commit/5f30f3116f34ceb48d00e3abd5c606e8f9b7caba)
-- feat(names): strip special characters from file and folder names [`6f39c1e`](https://github.com/Raiper34/spooty/commit/6f39c1e5dc4b8f87917a18d2a23cec6b5cd9ca60)
-- ci(github-actions): fix github actions update ytdl script [`04d8987`](https://github.com/Raiper34/spooty/commit/04d8987aa51a8887bf1b8b41e8680c2db9530bef)
-
-#### [2.0.5](https://github.com/Raiper34/spooty/compare/2.0.4...2.0.5)
-
-> 20 March 2025
-
-- ci(docker): fix docker buildx in release-it for github actions [`db65823`](https://github.com/Raiper34/spooty/commit/db65823ce0794280cb373a4381e24ad704a4db56)
-- fix(ytdl): Upgrade ytdl package (automated) [`16c57aa`](https://github.com/Raiper34/spooty/commit/16c57aaefd599f04b0a88723d55751f4ba27383a)
-
-#### [2.0.4](https://github.com/Raiper34/spooty/compare/2.0.3...2.0.4)
-
-> 15 March 2025
-
-- fix(ytdl): Upgrade ytdl package (automated) [`b960dc8`](https://github.com/Raiper34/spooty/commit/b960dc88db2a44c5865cb5c6f5964e4b4ffedc3f)
-
-#### [2.0.3](https://github.com/Raiper34/spooty/compare/2.0.2...2.0.3)
-
-> 15 March 2025
-
-- fix(ytdl): Upgrade ytdl package (automated) [`844d026`](https://github.com/Raiper34/spooty/commit/844d02668512cb8446f27f5629412fb5331b1298)
-
-#### [2.0.2](https://github.com/Raiper34/spooty/compare/2.0.1...2.0.2)
-
-> 15 March 2025
-
-- fix(ytdl): Upgrade ytdl package (automated) [`cf559eb`](https://github.com/Raiper34/spooty/commit/cf559eb41a2eb84cd29d4a732d68885f4ebcf348)
-
-#### [2.0.1](https://github.com/Raiper34/spooty/compare/2.0.0...2.0.1)
-
-> 23 February 2025
-
-- build(ytdl): Upgrade ytdl package (automated) [`#20`](https://github.com/Raiper34/spooty/pull/20)
-- refactor(backend): queue system presented [`#5`](https://github.com/Raiper34/spooty/issues/5)
-- fix(utdl): upgrade ytdl package [`66b55b3`](https://github.com/Raiper34/spooty/commit/66b55b39432f2da71bd401fd65af6c0a207dd6c2)
-- ci(github-actions): add automatic ytdl update into github actions [`2ca165b`](https://github.com/Raiper34/spooty/commit/2ca165b59a76147439d4d00055b84102cf7a7317)
-- ci(github-actions): add automatic ytdl update into github actions [`9baf3b9`](https://github.com/Raiper34/spooty/commit/9baf3b9eb7b093029300614bc9c1a913a73fb003)
-- ci(github-actions): add automatic ytdl update into github actions [`b2fbb01`](https://github.com/Raiper34/spooty/commit/b2fbb01a7415c4027aa50dd2a1773d0b1e0dde3a)
-- docs(website): change default environment variable for REDIS_RUN var [`f539a22`](https://github.com/Raiper34/spooty/commit/f539a227575569ce5641f5dfec6fcf50598a491d)
-- ci(github-actions): add automatic ytdl update into github actions [`cbe2c9f`](https://github.com/Raiper34/spooty/commit/cbe2c9fbdc8322881b6ae3fd90586b7656d155ec)
-- ci(github-actions): add automatic ytdl update into github actions [`a55ebf6`](https://github.com/Raiper34/spooty/commit/a55ebf659974137f96a465b74a8a0c8b649b3399)
-- ci(github-actions): add automatic ytdl update into github actions [`66516e6`](https://github.com/Raiper34/spooty/commit/66516e6916af1b1e29df13d870f775f5c7ce43c2)
-
-
-
-### 2.0.0
-- refactor(backend): queue system presented
-
-### 1.0.0
-- initial release
\ No newline at end of file
+- Initial release
diff --git a/README.md b/README.md
index b0543d5..92bb5bb 100644
--- a/README.md
+++ b/README.md
@@ -1,157 +1,370 @@
-[](https://hub.docker.com/r/raiper34/spooty)
-[](https://hub.docker.com/r/raiper34/spooty)
-
-[](https://hub.docker.com/r/raiper34/spooty)
-[](https://github.com/Raiper34/spooty)
-[](https://github.com/Raiper34/spooty)
+# Spooty Hardened
-
-# Spooty - selfhosted Spotify downloader
-Spooty is a self-hosted Spotify downloader.
-It allows download track/playlist/album from the Spotify url.
-It can also subscribe to a playlist or author page and download new songs upon release.
-Spooty basically downloads nothing from Spotify, it only gets information from spotify and then finds relevant and downloadeds music on Youtube.
-The project is based on NestJS and Angular.
+Self-hosted Spotify playlist and track downloader built with NestJS + Angular.
-> [!IMPORTANT]
-> Please do not use this tool for piracy! Download only music you own rights! Use this tool only on your responsibility.
+Spooty does not download audio from Spotify itself.
+It retrieves metadata from Spotify and locates matching audio on YouTube.
-### Content
-- [🚀 Installation](#-installation)
- - [Spotify App Configuration](#spotify-app-configuration)
- - [Docker](#docker)
- - [Docker command](#docker-command)
- - [Docker compose](#docker-compose)
- - [Build from source](#build-from-source)
- - [Process](#requirements)
- - [Requirements](#process)
- - [Environment variables](#environment-variables)
- - [YouTube cookies](#youtube-cookies)
-- [⚖️ License](#-license)
+This hardened branch focuses on:
-## 🚀 Installation
-Recommended and the easiest way how to start to use of Spooty is using docker.
+- Large playlist support (>100 tracks)
+- Spotify OAuth login flow
+- Better YouTube pacing and throttling resistance
+- Improved Docker deployment
+- Safer credential handling
+- More resilient cover-art embedding
+- Better queue stability
-### Spotify App Configuration
+---
-To fully use Spooty, you need to create an application in the Spotify Developer Dashboard:
+# Features
-1. Go to [Spotify Developer Dashboard](https://developer.spotify.com/dashboard)
-2. Sign in with your Spotify account
-3. Create a new application
-4. Note your `Client ID` and `Client Secret`
-5. Configure the redirect URI to `http://127.0.0.1:3000/api/callback` (or the corresponding URL of your instance)
+- Download Spotify playlists
+- Download individual Spotify tracks
+- Playlist auto-subscription support
+- Automatic YouTube matching
+- MP3 tagging and embedded cover art
+- Docker deployment
+- Queue-based download system
+- Spotify OAuth integration
+- Large playlist pagination support
+- Download pacing controls
+- YouTube cookie support
-These credentials will be used by Spooty to access the Spotify API.
+---
-### Docker
+# Important Notice
-Just run docker command or use docker compose configuration.
-For detailed configuration, see available [environment variables](#environment-variables).
+Use this software responsibly.
-#### Docker command
-```shell
-docker run -d -p 3000:3000 \
- -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_SECRET=your_client_secret \
- raiper34/spooty:latest
-```
+Only download music you legally own or are permitted to access.
-#### Docker compose
-```yaml
+The maintainers are not responsible for misuse.
+
+---
+
+# Supported URLs
+
+- Spotify playlists
+- Spotify tracks
+
+Example:
+
+ https://open.spotify.com/playlist/...
+ https://open.spotify.com/track/...
+
+---
+
+# Quick Start (Recommended)
+
+## 1. Create Spotify Developer App
+
+Go to:
+
+ https://developer.spotify.com/dashboard
+
+Create an application.
+
+Add this Redirect URI:
+
+ http://127.0.0.1:3000/api/spotify/callback
+
+Copy:
+
+- Client ID
+- Client Secret
+
+---
+
+## 2. Store Credentials Outside Repository
+
+Create a secure env file:
+
+~~~bash
+sudo mkdir -p /etc/tokens
+
+sudo tee /etc/tokens/spotify.env > /dev/null <<'EOF'
+SPOTIFY_CLIENT_ID=your_client_id
+SPOTIFY_CLIENT_SECRET=your_client_secret
+EOF
+
+sudo chown root:$USER /etc/tokens/spotify.env
+sudo chmod 640 /etc/tokens/spotify.env
+~~~
+
+Never commit this file.
+
+---
+
+# Docker Run
+
+~~~bash
+docker run --rm -p 3000:3000 \
+ --env-file /etc/tokens/spotify.env \
+ -e SPOTIFY_REDIRECT_URI='http://127.0.0.1:3000/api/spotify/callback' \
+ -e AUTH_ENABLED=true \
+ -e SPOOTY_AUTH_TOKEN=change_this_token \
+ -e YT_SEARCH_DELAY_MS=7000 \
+ -e YT_DOWNLOADS_PER_MINUTE=3 \
+ -v "$PWD/downloads:/spooty/backend/downloads" \
+ spootyfy-hardened:local
+~~~
+
+Open:
+
+ http://127.0.0.1:3000/?token=change_this_token
+
+Then:
+
+1. Click "Connect Spotify"
+2. Login to Spotify
+3. Approve access
+4. Paste playlist URL
+5. Download
+
+---
+
+# Docker Compose
+
+~~~yaml
services:
spooty:
- image: raiper34/spooty:latest
+ image: spootyfy-hardened:local
container_name: spooty
restart: unless-stopped
+
ports:
- "3000:3000"
- volumes:
- - /path/to/downloads:/spooty/backend/downloads
- - /path/to/cookies.txt:/spooty/config/cookies.txt
+
+ env_file:
+ - /etc/tokens/spotify.env
+
environment:
- - SPOTIFY_CLIENT_ID=your_client_id
- - SPOTIFY_CLIENT_SECRET=your_client_secret
- # Configure other environment variables if needed
-```
+ SPOTIFY_REDIRECT_URI: "http://127.0.0.1:3000/api/spotify/callback"
+ AUTH_ENABLED: "true"
+ SPOOTY_AUTH_TOKEN: "change_this_token"
-### Build from source
+ YT_SEARCH_DELAY_MS: "7000"
+ YT_DOWNLOADS_PER_MINUTE: "3"
-Spooty can be also build from source files on your own.
+ volumes:
+ - ./downloads:/spooty/backend/downloads
+~~~
-#### Requirements
-- Node v20.20.0 (it is recommended to use `nvm` node version manager to install proper version of node)
-- Redis in memory cache
-- Ffmpeg
+---
+
+# Build From Source
+
+## Requirements
+
+- Node.js 20.20.0
+- Docker
+- ffmpeg
- Python3
+- Redis
-#### Process
-- install Node v20.20.0 using `nvm install` and use that node version `nvm use`
-- from project root install all dependencies using `npm install`
-- copy `.env.default` as `.env` in `src/backend` folder and modify desired environment properties (see [environment variables](#environment-variables))
-- add your Spotify application credentials to the `.env` file:
- ```
- SPOTIFY_CLIENT_ID=your_client_id
- SPOTIFY_CLIENT_SECRET=your_client_secret
- ```
-- build source files `npm run build`
- - built project will be stored in `dist` folder
-- start server `npm run start`
+---
-### Environment variables
+## Build
-Some behaviour and settings of Spooty can be configured using environment variables and `.env` file.
+~~~bash
+npm install
+npm run build
+~~~
- Name | Default | Description |
--------------------------|---------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
- 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 |
- DOWNLOADS_PATH | `./downloads` (relative to backend) | Path where downaloded files will be stored |
- FORMAT | `mp3` | Format of downloaded files ('aac', 'flac', 'mp3', 'm4a', 'opus', 'vorbis', 'wav', 'alac') |
- QUALITY | undefined | Audio quality (0-9 VBR or specific bitrate) of downloaded files |
- PORT | 3000 | Port of Spooty server |
- REDIS_PORT | 6379 | Port of Redis server |
- REDIS_HOST | localhost | Host of Redis server |
- RUN_REDIS | false | Whenever Redis server should be started from backend (recommended for Docker environment) |
- 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) |
- YT_DOWNLOADS_PER_MINUTE | 3 | Set the maximum number of YouTube downloads started per minute |
- 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). |
+---
-### YouTube cookies
+## Run
-YouTube may block or throttle downloads without authentication cookies. Spooty supports two ways to provide them — use the one that fits your setup.
+~~~bash
+npm run start
+~~~
-#### `YT_COOKIES` — browser-based cookies (non-Docker)
+---
-Set `YT_COOKIES` to the name of your browser and yt-dlp will automatically read cookies directly from it.
-Supported values: `chrome`, `firefox`, `edge`, `safari`, `brave`, `opera`, `chromium`.
+# Environment Variables
-```
-YT_COOKIES=chrome
-```
+| Variable | Default | Description |
+|---|---|---|
+| DB_PATH | `./config/db.sqlite` | SQLite database path |
+| DOWNLOADS_PATH | `./downloads` | Download directory |
+| FORMAT | `mp3` | Output format |
+| REDIS_HOST | `localhost` | Redis host |
+| REDIS_PORT | `6379` | Redis port |
+| SPOTIFY_CLIENT_ID | unset | Spotify client ID |
+| SPOTIFY_CLIENT_SECRET | unset | Spotify client secret |
+| SPOTIFY_REDIRECT_URI | unset | OAuth callback URI |
+| AUTH_ENABLED | `false` | Enable frontend token auth |
+| SPOOTY_AUTH_TOKEN | unset | Frontend auth token |
+| YT_SEARCH_DELAY_MS | `5000` | Delay between YouTube searches |
+| YT_DOWNLOADS_PER_MINUTE | `3` | Download pacing |
+| YT_COOKIES | unset | Browser cookie extraction |
+| YT_COOKIES_FILE | unset | Netscape cookies.txt path |
-> [!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)
+# YouTube Cookies
-Export your YouTube cookies as a Netscape `cookies.txt` file and provide its path. This is the recommended approach for Docker deployments.
+YouTube may throttle or block requests without cookies.
-**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`.
+Two supported methods exist.
-**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.
+## Browser Cookies (Native Install Only)
-> [!NOTE]
-> `YT_COOKIES` takes priority over `YT_COOKIES_FILE` if both are set.
+~~~bash
+YT_COOKIES=firefox
+~~~
-# ⚖️ License
-[MIT](https://choosealicense.com/licenses/mit/)
+Supported:
+
+- firefox
+- chrome
+- chromium
+- edge
+- brave
+- opera
+
+This does NOT work reliably in Docker.
+
+---
+
+## Cookies File (Recommended)
+
+Export a Netscape-format `cookies.txt`.
+
+Mount into container:
+
+~~~bash
+-v /path/to/cookies.txt:/spooty/config/cookies.txt
+~~~
+
+Then:
+
+~~~bash
+-e YT_COOKIES_FILE=/spooty/config/cookies.txt
+~~~
+
+---
+
+# Large Playlist Support
+
+The hardened branch fixes several Spotify API limitations:
+
+- Proper playlist pagination
+- >100 track playlists
+- OAuth-authenticated playlist access
+- Queue stability improvements
+
+Verified working with playlists containing 300+ tracks.
+
+---
+
+# Queue Pacing
+
+Aggressive YouTube access can trigger:
+
+- HTTP 302 loops
+- CAPTCHA
+- temporary throttling
+- incomplete downloads
+
+Recommended safe pacing:
+
+~~~bash
+-e YT_SEARCH_DELAY_MS=7000
+-e YT_DOWNLOADS_PER_MINUTE=3
+~~~
+
+---
+
+# Security Notes
+
+Never commit:
+
+- Spotify secrets
+- OAuth tokens
+- cookies.txt
+- downloaded music
+- local databases
+
+Recommended `.gitignore` additions:
+
+~~~gitignore
+downloads/
+config/
+*.sqlite
+cookies.txt
+.env
+.env.local
+~~~
+
+---
+
+# Hardened Branch Changelog
+
+## Unreleased Hardened Changes
+
+### Spotify
+
+- Added Spotify OAuth login flow
+- Added persistent Spotify user token storage
+- Added authenticated playlist retrieval
+- Fixed large playlist pagination
+- Fixed playlist truncation at 100 tracks
+- Added playlist retrieval debugging
+- Added OAuth status endpoint
+
+### YouTube
+
+- Added configurable search pacing
+- Added configurable download pacing
+- Reduced aggressive YouTube request bursts
+- Improved queue throttling behavior
+
+### Backend
+
+- Added safer queue pacing logic
+- Added explicit search processor throttling
+- Improved Docker runtime stability
+- Improved logging around Spotify retrieval
+- Added auth bootstrap flow support
+
+### Frontend
+
+- Added Spotify login integration
+- Added frontend auth token bootstrap
+- Fixed playlist progress handling for large playlists
+- Improved playlist rendering stability
+
+### Security
+
+- Moved secrets to external env files
+- Improved Docker secret handling recommendations
+- Added security documentation
+- Prevented accidental secret inclusion in repository
+
+---
+
+# Development Notes
+
+Recommended local test run:
+
+~~~bash
+docker run --rm -p 3000:3000 \
+ --env-file /etc/tokens/spotify.env \
+ -e SPOTIFY_REDIRECT_URI='http://127.0.0.1:3000/api/spotify/callback' \
+ -e AUTH_ENABLED=true \
+ -e SPOOTY_AUTH_TOKEN=test-token \
+ -e YT_SEARCH_DELAY_MS=7000 \
+ -e YT_DOWNLOADS_PER_MINUTE=3 \
+ -v "$PWD/downloads:/spooty/backend/downloads" \
+ spootyfy-hardened:local
+~~~
+
+---
+
+# License
+
+MIT
diff --git a/src/backend/src/environmentEnum.ts b/src/backend/src/environmentEnum.ts
index 8208771..61caf30 100644
--- a/src/backend/src/environmentEnum.ts
+++ b/src/backend/src/environmentEnum.ts
@@ -12,4 +12,6 @@ export enum EnvironmentEnum {
SPOOTY_AUTH_TOKEN = 'SPOOTY_AUTH_TOKEN',
CORS_ORIGIN = 'CORS_ORIGIN',
NODE_ENV = 'NODE_ENV',
+ SPOTIFY_REDIRECT_URI = 'SPOTIFY_REDIRECT_URI',
+ SPOTIFY_TOKEN_PATH = 'SPOTIFY_TOKEN_PATH',
}
diff --git a/src/backend/src/shared/auth.guard.ts b/src/backend/src/shared/auth.guard.ts
index 50fd671..9e7f9c8 100644
--- a/src/backend/src/shared/auth.guard.ts
+++ b/src/backend/src/shared/auth.guard.ts
@@ -11,7 +11,12 @@ export class AuthGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const request = context.switchToHttp().getRequest
Self-hosted spotify downloader
+ diff --git a/src/frontend/src/app/app.component.ts b/src/frontend/src/app/app.component.ts index 8a9d0d7..95e7b3c 100644 --- a/src/frontend/src/app/app.component.ts +++ b/src/frontend/src/app/app.component.ts @@ -5,6 +5,7 @@ import {PlaylistService, PlaylistStatusEnum} from "./services/playlist.service"; import {PlaylistBoxComponent} from "./components/playlist-box/playlist-box.component"; import {VersionService} from "./services/version.service"; import {map} from "rxjs"; +import {HttpClient} from "@angular/common/http"; @Component({ selector: 'app-root', @@ -25,12 +26,15 @@ export class AppComponent { playlists$ = this.playlistService.all$.pipe(map(items => items.filter(item => !item.isTrack))); songs$ = this.playlistService.all$.pipe(map(items => items.filter(item => item.isTrack))); version = this.versionService.getVersion(); + spotifyConnected = false; constructor( private readonly playlistService: PlaylistService, private readonly versionService: VersionService, + private readonly http: HttpClient, ) { this.bootstrapAuthTokenFromUrl(); + this.checkSpotifyStatus(); this.fetchPlaylists(); } @@ -47,6 +51,16 @@ export class AppComponent { window.history.replaceState({}, document.title, url.toString()); } + checkSpotifyStatus(): void { + this.http + .get<{ connected: boolean }>('/api/spotify/status') + .subscribe((status) => (this.spotifyConnected = status.connected)); + } + + connectSpotify(): void { + window.location.href = '/api/spotify/login'; + } + fetchPlaylists(): void { this.playlistService.fetch(); }