spotifysaverdn 0.7.5.post1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. spotifysaverdn-0.7.5.post1/LICENSE +8 -0
  2. spotifysaverdn-0.7.5.post1/PKG-INFO +556 -0
  3. spotifysaverdn-0.7.5.post1/README.md +507 -0
  4. spotifysaverdn-0.7.5.post1/pyproject.toml +143 -0
  5. spotifysaverdn-0.7.5.post1/spotifysaver/__init__.py +46 -0
  6. spotifysaverdn-0.7.5.post1/spotifysaver/__main__.py +8 -0
  7. spotifysaverdn-0.7.5.post1/spotifysaver/api/README.md +365 -0
  8. spotifysaverdn-0.7.5.post1/spotifysaver/api/__init__.py +5 -0
  9. spotifysaverdn-0.7.5.post1/spotifysaver/api/app.py +291 -0
  10. spotifysaverdn-0.7.5.post1/spotifysaver/api/config.py +105 -0
  11. spotifysaverdn-0.7.5.post1/spotifysaver/api/examples.py +268 -0
  12. spotifysaverdn-0.7.5.post1/spotifysaver/api/main.py +21 -0
  13. spotifysaverdn-0.7.5.post1/spotifysaver/api/routers/__init__.py +6 -0
  14. spotifysaverdn-0.7.5.post1/spotifysaver/api/routers/authentication.py +137 -0
  15. spotifysaverdn-0.7.5.post1/spotifysaver/api/routers/backups.py +316 -0
  16. spotifysaverdn-0.7.5.post1/spotifysaver/api/routers/channel_watcher.py +124 -0
  17. spotifysaverdn-0.7.5.post1/spotifysaver/api/routers/download.py +1522 -0
  18. spotifysaverdn-0.7.5.post1/spotifysaver/api/routers/jellyfin.py +59 -0
  19. spotifysaverdn-0.7.5.post1/spotifysaver/api/routers/notifications.py +35 -0
  20. spotifysaverdn-0.7.5.post1/spotifysaver/api/routers/spotify_settings.py +223 -0
  21. spotifysaverdn-0.7.5.post1/spotifysaver/api/routers/storage.py +32 -0
  22. spotifysaverdn-0.7.5.post1/spotifysaver/api/routers/user_preferences.py +129 -0
  23. spotifysaverdn-0.7.5.post1/spotifysaver/api/schemas.py +273 -0
  24. spotifysaverdn-0.7.5.post1/spotifysaver/api/services/__init__.py +31 -0
  25. spotifysaverdn-0.7.5.post1/spotifysaver/api/services/channel_watcher.py +1462 -0
  26. spotifysaverdn-0.7.5.post1/spotifysaver/api/services/download_directory_store.py +148 -0
  27. spotifysaverdn-0.7.5.post1/spotifysaver/api/services/download_limits_store.py +79 -0
  28. spotifysaverdn-0.7.5.post1/spotifysaver/api/services/download_service.py +508 -0
  29. spotifysaverdn-0.7.5.post1/spotifysaver/api/services/jellyfin_library_scan.py +406 -0
  30. spotifysaverdn-0.7.5.post1/spotifysaver/api/services/notification_hub.py +69 -0
  31. spotifysaverdn-0.7.5.post1/spotifysaver/api/services/security_store.py +318 -0
  32. spotifysaverdn-0.7.5.post1/spotifysaver/api/services/storage_protection.py +243 -0
  33. spotifysaverdn-0.7.5.post1/spotifysaver/api/services/video_download_service.py +425 -0
  34. spotifysaverdn-0.7.5.post1/spotifysaver/api/services/youtube_confirmation_store.py +77 -0
  35. spotifysaverdn-0.7.5.post1/spotifysaver/cli/__init__.py +5 -0
  36. spotifysaverdn-0.7.5.post1/spotifysaver/cli/cli.py +36 -0
  37. spotifysaverdn-0.7.5.post1/spotifysaver/cli/commands/__init__.py +11 -0
  38. spotifysaverdn-0.7.5.post1/spotifysaver/cli/commands/download/__init__.py +5 -0
  39. spotifysaverdn-0.7.5.post1/spotifysaver/cli/commands/download/album.py +161 -0
  40. spotifysaverdn-0.7.5.post1/spotifysaver/cli/commands/download/download.py +128 -0
  41. spotifysaverdn-0.7.5.post1/spotifysaver/cli/commands/download/playlist.py +123 -0
  42. spotifysaverdn-0.7.5.post1/spotifysaver/cli/commands/download/track.py +109 -0
  43. spotifysaverdn-0.7.5.post1/spotifysaver/cli/commands/init.py +76 -0
  44. spotifysaverdn-0.7.5.post1/spotifysaver/cli/commands/inspect/__init__.py +7 -0
  45. spotifysaverdn-0.7.5.post1/spotifysaver/cli/commands/inspect/album_info.py +38 -0
  46. spotifysaverdn-0.7.5.post1/spotifysaver/cli/commands/inspect/inspect.py +52 -0
  47. spotifysaverdn-0.7.5.post1/spotifysaver/cli/commands/inspect/playlist_info.py +36 -0
  48. spotifysaverdn-0.7.5.post1/spotifysaver/cli/commands/inspect/track_info.py +30 -0
  49. spotifysaverdn-0.7.5.post1/spotifysaver/cli/commands/log/__init__.py +5 -0
  50. spotifysaverdn-0.7.5.post1/spotifysaver/cli/commands/log/log.py +68 -0
  51. spotifysaverdn-0.7.5.post1/spotifysaver/cli/commands/version.py +19 -0
  52. spotifysaverdn-0.7.5.post1/spotifysaver/config/__init__.py +3 -0
  53. spotifysaverdn-0.7.5.post1/spotifysaver/config/setting_environment.py +81 -0
  54. spotifysaverdn-0.7.5.post1/spotifysaver/download_limiter.py +46 -0
  55. spotifysaverdn-0.7.5.post1/spotifysaver/downloader/__init__.py +13 -0
  56. spotifysaverdn-0.7.5.post1/spotifysaver/downloader/image_downloader.py +61 -0
  57. spotifysaverdn-0.7.5.post1/spotifysaver/downloader/soundcloud_downloader.py +350 -0
  58. spotifysaverdn-0.7.5.post1/spotifysaver/downloader/trim_silence.py +187 -0
  59. spotifysaverdn-0.7.5.post1/spotifysaver/downloader/youtube_downloader.py +766 -0
  60. spotifysaverdn-0.7.5.post1/spotifysaver/downloader/youtube_downloader_for_cli.py +1204 -0
  61. spotifysaverdn-0.7.5.post1/spotifysaver/enums/__init__.py +4 -0
  62. spotifysaverdn-0.7.5.post1/spotifysaver/enums/audio_formats_enum.py +8 -0
  63. spotifysaverdn-0.7.5.post1/spotifysaver/enums/bitrates_enum.py +9 -0
  64. spotifysaverdn-0.7.5.post1/spotifysaver/metadata/__init__.py +9 -0
  65. spotifysaverdn-0.7.5.post1/spotifysaver/metadata/music_file_metadata.py +179 -0
  66. spotifysaverdn-0.7.5.post1/spotifysaver/metadata/nfo_generator.py +163 -0
  67. spotifysaverdn-0.7.5.post1/spotifysaver/models/__init__.py +8 -0
  68. spotifysaverdn-0.7.5.post1/spotifysaver/models/album.py +42 -0
  69. spotifysaverdn-0.7.5.post1/spotifysaver/models/artist.py +44 -0
  70. spotifysaverdn-0.7.5.post1/spotifysaver/models/playlist.py +41 -0
  71. spotifysaverdn-0.7.5.post1/spotifysaver/models/track.py +90 -0
  72. spotifysaverdn-0.7.5.post1/spotifysaver/services/__init__.py +9 -0
  73. spotifysaverdn-0.7.5.post1/spotifysaver/services/audiodb_parser.py +212 -0
  74. spotifysaverdn-0.7.5.post1/spotifysaver/services/errors/errors.py +83 -0
  75. spotifysaverdn-0.7.5.post1/spotifysaver/services/lrclib_api.py +92 -0
  76. spotifysaverdn-0.7.5.post1/spotifysaver/services/schemas/__init__.py +3 -0
  77. spotifysaverdn-0.7.5.post1/spotifysaver/services/schemas/album_audiodb_schema.py +67 -0
  78. spotifysaverdn-0.7.5.post1/spotifysaver/services/schemas/artist_audiodb_schema.py +63 -0
  79. spotifysaverdn-0.7.5.post1/spotifysaver/services/schemas/track_audiodb_schema.py +39 -0
  80. spotifysaverdn-0.7.5.post1/spotifysaver/services/score_match_calculator.py +251 -0
  81. spotifysaverdn-0.7.5.post1/spotifysaver/services/spotify_api.py +496 -0
  82. spotifysaverdn-0.7.5.post1/spotifysaver/services/the_audio_db_service.py +214 -0
  83. spotifysaverdn-0.7.5.post1/spotifysaver/services/youtube_api.py +318 -0
  84. spotifysaverdn-0.7.5.post1/spotifysaver/spotlog/__init__.py +7 -0
  85. spotifysaverdn-0.7.5.post1/spotifysaver/spotlog/log_config.py +78 -0
  86. spotifysaverdn-0.7.5.post1/spotifysaver/spotlog/logger.py +19 -0
  87. spotifysaverdn-0.7.5.post1/spotifysaver/spotlog/ydd_logger.py +25 -0
  88. spotifysaverdn-0.7.5.post1/spotifysaver/ui/README.md +86 -0
  89. spotifysaverdn-0.7.5.post1/spotifysaver/ui/index.html +806 -0
  90. spotifysaverdn-0.7.5.post1/spotifysaver/ui/static/css/styles.css +1757 -0
  91. spotifysaverdn-0.7.5.post1/spotifysaver/ui/static/js/api-client.js +738 -0
  92. spotifysaverdn-0.7.5.post1/spotifysaver/ui/static/js/app.js +1931 -0
  93. spotifysaverdn-0.7.5.post1/spotifysaver/ui/static/js/channel-watcher-manager.js +398 -0
  94. spotifysaverdn-0.7.5.post1/spotifysaver/ui/static/js/download-manager.js +765 -0
  95. spotifysaverdn-0.7.5.post1/spotifysaver/ui/static/js/state-manager.js +75 -0
  96. spotifysaverdn-0.7.5.post1/spotifysaver/ui/static/js/ui-manager.js +871 -0
@@ -0,0 +1,8 @@
1
+ Copyright (c) 2025 Gabriel Baute
2
+ Copyright (c) 2026 Danilo Nishimura
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5
+
6
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7
+
8
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,556 @@
1
+ Metadata-Version: 2.4
2
+ Name: spotifysaverdn
3
+ Version: 0.7.5.post1
4
+ Summary: Download audio and video from YouTube Music to your media server.
5
+ License: MIT
6
+ License-File: LICENSE
7
+ Keywords: spotify,youtube,youtube-music,soundcloud,music-downloader,jellyfin,metadata,cli-tool,audio-conversion,playlist-downloader
8
+ Author: Gabriel Baute
9
+ Author-email: gabrielbaute@gmail.com
10
+ Maintainer: Gabriel Baute
11
+ Maintainer-email: gabrielbaute@gmail.com
12
+ Requires-Python: >=3.10,<4.0
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Environment :: Web Environment
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: Intended Audience :: End Users/Desktop
18
+ Classifier: License :: OSI Approved :: MIT License
19
+ Classifier: Natural Language :: English
20
+ Classifier: Operating System :: OS Independent
21
+ Classifier: Programming Language :: Python :: 3
22
+ Classifier: Programming Language :: Python :: 3.10
23
+ Classifier: Programming Language :: Python :: 3.11
24
+ Classifier: Programming Language :: Python :: 3.12
25
+ Classifier: Programming Language :: Python :: 3.13
26
+ Classifier: Programming Language :: Python :: 3.14
27
+ Classifier: Programming Language :: Python :: 3.15
28
+ Classifier: Topic :: Multimedia :: Sound/Audio
29
+ Classifier: Topic :: Multimedia :: Sound/Audio :: Conversion
30
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
31
+ Classifier: Topic :: Utilities
32
+ Provides-Extra: docs
33
+ Requires-Dist: click (>=8.1.8,<9.0.0)
34
+ Requires-Dist: fastapi (>=0.115.14,<0.116.0)
35
+ Requires-Dist: mutagen (>=1.47.0,<2.0.0)
36
+ Requires-Dist: pydantic (>=2.11.9,<3.0.0)
37
+ Requires-Dist: python-dotenv (>=1.1.1,<2.0.0)
38
+ Requires-Dist: requests (>=2.32.5,<3.0.0)
39
+ Requires-Dist: spotipy (>=2.25.1,<3.0.0)
40
+ Requires-Dist: tzdata (>=2025.2,<2026.0)
41
+ Requires-Dist: uvicorn[standard] (>=0.34.0,<0.35.0)
42
+ Requires-Dist: yt-dlp (>=2026.8.19,<2027.0.0)
43
+ Requires-Dist: ytmusicapi (>=1.10.3,<2.0.0)
44
+ Project-URL: Documentation, https://deepwiki.com/danilonishi/spotify-saver
45
+ Project-URL: Homepage, https://github.com/danilonishi/spotify-saver
46
+ Project-URL: Repository, https://github.com/danilonishi/spotify-saver
47
+ Description-Content-Type: text/markdown
48
+
49
+ # SpotifySaver 🎵✨
50
+
51
+ [![Python](https://img.shields.io/badge/Python-3.10%2B-blue?logo=python&logoColor=white)](https://www.python.org/)
52
+ [![PyPI Version](https://img.shields.io/pypi/v/spotifysaverdn?color=blue&logo=pypi&logoColor=white)](https://pypi.org/project/spotifysaverdn/)
53
+ [![Docker](https://img.shields.io/badge/Docker-GHCR-blue?logo=docker&logoColor=white)](https://github.com/danilonishi/spotify-saver/pkgs/container/spotify-saver)
54
+ [![GitHub Container Registry](https://img.shields.io/badge/ghcr.io-Auto--Build-green?logo=github)](https://github.com/danilonishi/spotify-saver/pkgs/container/spotify-saver)
55
+ [![FFmpeg](https://img.shields.io/badge/FFmpeg-Required-orange?logo=ffmpeg&logoColor=white)](https://ffmpeg.org/)
56
+ [![yt-dlp](https://img.shields.io/badge/yt--dlp-2026.8.19%2B-red)](https://github.com/yt-dlp/yt-dlp)
57
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
58
+ [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
59
+
60
+ [![Spotify](https://img.shields.io/badge/Spotify-API-1ED760?logo=spotify)](https://developer.spotify.com/)
61
+ [![YouTube](https://img.shields.io/badge/Youtube-API-C00000?logo=youtube)](https://youtube.com/)
62
+ [![YouTube Music](https://img.shields.io/badge/YouTube_Music-API-yellow)](https://ytmusicapi.readthedocs.io/)
63
+ [![SoundCloud](https://img.shields.io/badge/Soundcloud-API-FF5500?logo=soundcloud)](https://soundcloud.com)
64
+
65
+ > ⚠️This repository is a fork from gabrielbaute's SpotifySaver. It's being developed with quality of life features I missed when using his tool. You can easily see the changes made by clicking [here](https://github.com/gabrielbaute/spotify-saver/compare/main...danilonishi:spotify-saver:main).
66
+
67
+ All-in-one tool for downloading content for Jellyfin or similar media servers.
68
+
69
+ The app allows you to download content for your media server. It can connect to Spotify, Soundcloud and YouTube Music APIs, retrieve information, locate the media, download and extract the audio and/or video data and generate a Jellyfin-compatible .nfo file for libraries.
70
+
71
+ ## Changes in this Fork
72
+
73
+ <p align="center">
74
+ <img src="screenshot.jpg" alt="SpotifySaver web interface" />
75
+ </p>
76
+
77
+ This fork packs many more features compared to the baseline:
78
+
79
+ - **Server-managed download queue:** Downloads continue if you refresh or close the browser.
80
+ - **Access control:** Protected UI and API to avoid unauthorized access.
81
+ - **Video support:** Downloads videos with configurable format, resolution and quality and generates NFO metadata.
82
+ - **SoundCloud support:** Direct downloads when download option is available.
83
+ - **Silence Trimming:** Option to automatically trim silence at the start and end of downloaded audio files.
84
+ - **Playlist support:** Download playlists from Spotify and YouTube.
85
+ - **Multiple parallel Downloads:** Useful when downloading large playlists or albums.
86
+ - **Channel Watcher:** Schedule channel checks and automatically download new uploads.
87
+ - **Jellyfin library scans:** Automatically triggers Jellyfin media scan after downloads are completed.
88
+ - **[Browser extension support](https://github.com/danilonishi/spotifyu-saver-browser-extension):** Trigger downloads from Spotify, YouTube, and SoundCloud pages, or watch channels.
89
+ - **Simplified download folders:** Browse and select download folders in the web interface.
90
+ - **UI improvements:** Dark mode and customizable interface.
91
+ - **Backup and restore:** Easier to migrate the server or share configurations.
92
+ - **Storage safeguards:** Set a minimum free space to avoid completely filling the storage.
93
+ - **Improved visibility:** Downloads are reported by the server to all connected clients.
94
+ - **Large Spotify playlists:** Large playlists were capped due to Spotify list being paginated. This is fixed here.
95
+ - **Overwrite control:** Keep existing files by default (skipping download), or choose to redownload and overwrite.
96
+ - **Notifications:** Browser and toaster notifications for events are available.
97
+ - **Download queue controls:** Pause/resume the queue and retry failed downloads.
98
+ - **Channel Watcher controls and filters:** Filter out uploads by keywords, duration, date, Shorts, and livestreams.
99
+
100
+
101
+ ## 🌟 Features
102
+
103
+ | | Feature | Description |
104
+ |--------|--------------------------|--------------------------------------------|
105
+ ||**General**||
106
+ | ✅ | Organization | Automatic folder structure: Artist/Album/Track or Channel/Video|
107
+ | ✅ | Metadata | Generate Jellyfin-compatible `.nfo` metadata for audio and video|
108
+ | ✅ | Audio Formats | M4A, MP3 and OPUS|
109
+ | ✅ | Audio Bitrates | 96, 128, 192 and 256 kbps|
110
+ | ✅ | Video Formats | MP4, MKV and WebM|
111
+ | ✅ | Video Resolution | 360p, 480p, 720p, 1080p, 1440p and 2460p (4K)|
112
+ ||**Youtube/Youtube Music**||
113
+ | ✅ | Download | Download audio or video|
114
+ | ✅ | Playlists / Album Collections | Download and generate playlist file |
115
+ | ✅ | Channel Watcher | Watch channel and automatically downloads new content |
116
+ | ✅ | video with chapter markers | Not supported|
117
+ | ❌ | Lyrics | Not supported|
118
+ | ❌ | Captions | Planned|
119
+ ||**Spotify**||
120
+ | ✅ | Tracks | Fetch metadata from Spotify and audio track from Youtube Music|
121
+ | ✅ | Playlists | Download tracks and saves playlist file|
122
+ | ✅ | Lyrics | Synchronized lyrics (.lrc) from LRC Lib|
123
+ | ❌ | Artist Watcher | Planned |
124
+ ||**Soundcloud**||
125
+ | ✅ | Tracks | Direct download when available, Youtube Music as fallback|
126
+ | ❌ | Playlists | Planned |
127
+ | ❌ | Lyrics | Not supported|
128
+ | ❌ | Artist Watcher | Planned |
129
+ ||**Browser Extension**||
130
+ | ✅ | API | Adapts interface when download is requested by browser extension|
131
+ ||**Settings**||
132
+ | ✅ | UI | Control panel visibility|
133
+ | ✅ | Notifications | Enable browser and toaster notifications for events|
134
+ | ✅ | Storage | Define minimum free space for downloads|
135
+ | ✅ | Download limiter | Set number of simultaneous downloads and bandwidth|
136
+ | ✅ | Confirmation | Option to confirm queued downloads before they start downloading|
137
+ | ✅ | Backup & Restore | Generates `.json` file with server and client settings|
138
+ | ✅ | Jellyfin | Option to trigger library scan after all downloads are finished|
139
+ | ✅ | Spotify | Set up credentials to access the Spotify API|
140
+ | ✅ | Security | Password management|
141
+
142
+
143
+ # Installation
144
+
145
+ ### Requirements
146
+ - Python 3.10+
147
+ - FFmpeg
148
+ - [Spotify Developer Account](https://developer.spotify.com/dashboard/) (only for Spotify URL downloads)
149
+
150
+ ##
151
+
152
+ You can install using one of the three methods listed below.
153
+
154
+ ### Method 1: Docker (recommended)
155
+ See [Docker support with auto-builds](#-docker-support)
156
+
157
+ ### Method 2: Poetry (through Git)
158
+ ```bash
159
+ git clone https://github.com/danilonishi/spotify-saver.git
160
+ cd spotify-saver
161
+ poetry install
162
+ ```
163
+ Then proceed to [First-time sign-in](#first-time-sign-in)
164
+
165
+ ### Method 3: Pip (through Git)
166
+ ```bash
167
+ pip install git+https://github.com/danilonishi/spotify-saver.git
168
+ ```
169
+ Then proceed to [First-time sign-in](#first-time-sign-in)
170
+
171
+ ## 🐳 Docker Support
172
+
173
+ Docker images are published to
174
+ [GitHub Container Registry](https://github.com/danilonishi/spotify-saver/pkgs/container/spotify-saver)
175
+ from pushes to `main` and version tags (`v*`). `latest` tracks `main`; version
176
+ tags are also available for pinned deployments. The first successful workflow
177
+ run creates the package. Set its visibility to **Public** in GitHub Packages
178
+ if servers should be able to pull images without authenticating. Until the
179
+ first publish workflow has succeeded, use the local-build override below.
180
+
181
+ ### Install with Docker Compose
182
+
183
+ Requirements: Docker Engine/Desktop with the Compose plugin. Clone this
184
+ repository, then create the host folders and environment file:
185
+
186
+ In **Windows PowerShell**, run:
187
+
188
+ ```powershell
189
+ New-Item -ItemType Directory -Force data\music, data\videos, data\config
190
+ Copy-Item .env.docker.example .env
191
+ ```
192
+
193
+ On **Linux/macOS**, run the equivalent commands in a terminal using Bash:
194
+
195
+ ```sh
196
+ mkdir -p data/music data/videos data/config
197
+ cp .env.docker.example .env
198
+ ```
199
+
200
+ The container runs as user ID `1000` and needs write access to its config
201
+ folder. If you created the repository or its data folders with `sudo`, fix
202
+ ownership before starting the container:
203
+
204
+ ```sh
205
+ sudo chown -R 1000:1000 data/config
206
+ ```
207
+
208
+ If you set `CONFIG_DIR` in `.env` to a different host folder, use that folder
209
+ instead of `data/config`.
210
+
211
+ Before starting SpotifySaver, edit `.env` and set `MUSIC_DIR` and `VIDEO_DIR`
212
+ to the existing media folders on your server. For example, if your media
213
+ folders are `/media/Music` and `/media/Videos`, set:
214
+
215
+ ```dotenv
216
+ MUSIC_DIR=/media/Music
217
+ VIDEO_DIR=/media/Videos
218
+ ```
219
+
220
+ These are host paths, not the `/music` and `/videos` paths used inside the
221
+ container. Leave `CONFIG_DIR=./data/config` unless you want to store the app's
222
+ configuration elsewhere. On Linux, you can edit the file with `nano .env` from
223
+ the repository folder.
224
+
225
+ Direct YouTube downloads and the Channel Watcher do not need Spotify
226
+ credentials. Spotify URL-based downloads can be configured from **Settings →
227
+ Spotify Account** after signing in. The app asks Spotify to authorize in your
228
+ browser and receives the callback on the server; this works with a headless
229
+ Docker deployment.
230
+
231
+ Start the published image:
232
+
233
+ ```sh
234
+ docker compose pull
235
+ docker compose up -d
236
+ ```
237
+
238
+ ## First-time sign-in
239
+
240
+ On first launch, SpotifySaver writes a one-time administrator setup token to
241
+ the server logs. From the directory containing the Compose files, run:
242
+
243
+ ```sh
244
+ docker compose logs --tail=100 spotifysaver
245
+ ```
246
+
247
+ Find the line beginning `First-run administrator setup token` and enter its
248
+ token in the setup page. If the container is restarting, use this same command
249
+ to inspect the startup error in the logs.
250
+
251
+ Open SpotifySaver in your browser, enter that token, and create an administrator
252
+ password of at least 12 characters. The token is generated again after a server
253
+ restart until the initial password has been set, and becomes invalid once it is
254
+ used. Do not share the logs while they contain the token. After setup, sign in
255
+ with the password you created. Login attempts are rate-limited.
256
+
257
+ The admin password, sessions, and Spotify app credentials are persisted in
258
+ `CONFIG_DIR` (the `data/config` folder by default); the database is readable
259
+ only by the container account on Linux. The Spotify OAuth token cache is stored
260
+ in the same config directory with restricted file permissions. Back up this
261
+ directory and protect access to it.
262
+
263
+
264
+ ## Configure Spotify
265
+
266
+ 1. Create an app in the [Spotify Developer Dashboard](https://developer.spotify.com/dashboard/).
267
+ 2. In SpotifySaver, open **Settings → Spotify Account**. Copy the displayed
268
+ OAuth redirect URL into the app's allowed Redirect URIs in the Spotify
269
+ Developer Dashboard. It must match exactly; for a Caddy deployment it uses
270
+ the public HTTPS hostname.
271
+ 3. Enter the app's Client ID and Client Secret and save them.
272
+ 4. Select **Connect to Spotify** and approve access in Spotify.
273
+
274
+ The Client Secret is stored on the server and is never returned to the browser
275
+ after saving. The server-side security database is protected by filesystem
276
+ permissions, not encrypted; anyone with access to the server's config files or
277
+ Docker administrator access can read it. Keep those host files private.
278
+
279
+ To build and run the checked-out source instead of pulling GHCR, add the build
280
+ override:
281
+
282
+ ```sh
283
+ docker compose -f docker-compose.yml -f docker-compose.build.yml up -d --build
284
+ ```
285
+
286
+ ### CLI
287
+
288
+ If you're unable to browse to the settings view, run this command to save configuration to `~/.spotify-saver/.env`:
289
+
290
+ ```bash
291
+ spotifysaver init
292
+ ```
293
+ The app also checks for a `.env` file in the project directory as a fallback.
294
+ Supported environment variables include:
295
+
296
+ | Variable | Description | Default Value |
297
+ |---------------------------|--------------------------------------------|-----------------------------------|
298
+ | `SPOTIFY_CLIENT_ID` | ID of the Spotify app you created | - |
299
+ | `SPOTIFY_CLIENT_SECRET` | Secret key generated for your Spotify app | - |
300
+ | `SPOTIFY_REDIRECT_URI` | Spotify app redirect URI | `http://127.0.0.1:8888/callback` |
301
+ | `SPOTIFYSAVER_OUTPUT_DIR` | Audio download directory | `Music` |
302
+ | `SPOTIFYSAVER_VIDEO_DIR` | Video download directory | `Videos` |
303
+ | `YTDLP_COOKIES_PATH` | YouTube cookies file path (optional; for Docker, use a path inside the container such as `/config/cookies.txt`) | - |
304
+ | `SPOTIFY_AUTH_CACHE_PATH` | Spotify authorization cache path | `~/.spotify-saver/.spotify-cache` |
305
+ | `SPOTIFY_SCOPES` | Spotify permissions requested | `playlist-read-private playlist-read-collaborative` |
306
+ | `LOG_LEVEL` | Logging level | `info` |
307
+
308
+ The `init` wizard also writes `API_PORT` and `API_HOST`, but the standalone
309
+ server currently uses port `8000` and host `0.0.0.0`. For an example
310
+ configuration file, see [`.env.example`](.env.example).
311
+
312
+ # Updating
313
+
314
+ ## Git/Poetry
315
+
316
+ For a source checkout installed with Poetry, update the checkout and reinstall:
317
+
318
+ ```sh
319
+ git pull
320
+ poetry install
321
+ ```
322
+
323
+ ## Git/Pip
324
+
325
+ If installed from Git with pip, upgrade from the repository:
326
+
327
+ ```sh
328
+ python -m pip install --upgrade "git+https://github.com/danilonishi/spotify-saver.git"
329
+ ```
330
+
331
+ Restart the SpotifySaver server after updating.
332
+
333
+ ## Docker
334
+
335
+ For Docker, update to the latest published `main` image by running `git pull`
336
+ for the Compose files, then pull and start the service.
337
+
338
+ If Caddy runs in Docker, include the Caddy override on **both** commands so the container stays attached to Caddy's shared network:
339
+
340
+ ```sh
341
+ docker compose -f docker-compose.yml -f docker-compose.caddy.yml pull
342
+ docker compose -f docker-compose.yml -f docker-compose.caddy.yml up -d
343
+ ```
344
+
345
+ If you do **not** use the Docker Caddy override (for example, Caddy runs on the
346
+ host), use the base Compose file:
347
+
348
+ ```sh
349
+ docker compose pull
350
+ docker compose up -d
351
+ ```
352
+
353
+ Docker images are published as latest when changes are pushed to main. To publish a versioned release image, push a new Git tag using the vX.Y.Z format (for example, v0.7.6). The workflow publishes versioned and major/minor tags. To pin your deployment to that release, set IMAGE_TAG=0.7.6 in .env instead of using latest.
354
+
355
+ Open `http://127.0.0.1:8000`. The API is also available at
356
+ `http://127.0.0.1:8000/docs`. The port is bound to loopback only; for access
357
+ over HTTPS or from other devices, configure a reverse proxy rather than
358
+ exposing the application port directly. See the optional
359
+ [Caddy reverse proxy guide](CADDY.md).
360
+
361
+ The UI's folder selectors operate on container paths. Audio downloads are
362
+ stored under `/music`, video and watched-channel downloads under `/videos`,
363
+ and both paths map to `data/music` and `data/videos` on the host by default.
364
+ You can set `MUSIC_DIR`, `VIDEO_DIR`, and `CONFIG_DIR` in `.env` to change the
365
+ host-side folders. Keep the config directory: it stores the watched-channel
366
+ database, channel download archives, cookies, and other home-directory app
367
+ state. Back up these folders before migrating or reinstalling:
368
+
369
+ ```text
370
+ data/music/
371
+ data/videos/
372
+ data/config/
373
+ ```
374
+
375
+ On Linux, make sure the mounted directories are writable by the container's
376
+ non-root user (UID 1000). For example, if these are dedicated directories:
377
+
378
+ ```sh
379
+ sudo chown -R 1000:1000 data/music data/videos data/config
380
+ ```
381
+
382
+ Useful maintenance commands for a deployment without the Docker Caddy override:
383
+
384
+ ```sh
385
+ docker compose logs -f spotifysaver
386
+ git pull
387
+ docker compose pull
388
+ docker compose up -d
389
+ docker compose down
390
+ ```
391
+
392
+ For a Docker Caddy deployment, add
393
+ `-f docker-compose.yml -f docker-compose.caddy.yml` to each `docker compose`
394
+ command instead. Omitting the override can detach SpotifySaver from Caddy's
395
+ network when Compose recreates the container.
396
+
397
+ `docker compose down` stops and removes the container but leaves the bind
398
+ mounted data folders and named log volume intact. Do not use
399
+ `docker compose down -v` unless you intend to remove the named log volume as
400
+ well.
401
+
402
+ ## Cookies
403
+
404
+ Cookies are disabled by default. **Only configure `YTDLP_COOKIES_PATH` if a video
405
+ requires authentication**, and use a fresh export: stale or incompatible YouTube
406
+ cookies can cause downloads to fail with HTTP 403. For Docker Compose, save the
407
+ cookies file under the configured `CONFIG_DIR` on the host (by default,
408
+ `./data/config/cookies.txt`) and set this in `.env`:
409
+
410
+ ```dotenv
411
+ YTDLP_COOKIES_PATH=/config/cookies.txt
412
+ ```
413
+
414
+ To disable cookies, remove or empty `YTDLP_COOKIES_PATH` in `.env`, then
415
+ recreate the container with `docker compose up -d --force-recreate spotifysaver`.
416
+ To back up or replace the file first, use the host path, for example:
417
+
418
+ ```bash
419
+ cp ./data/config/cookies.txt ./data/config/cookies.txt.backup
420
+ ```
421
+
422
+ Do not share the cookie file; it contains session credentials. If the cookie
423
+ file is not present in your configured `CONFIG_DIR`, leave the setting disabled.
424
+
425
+ # 📚 Documentation
426
+
427
+ The **documentation for using the API**, on the other hand, can be found in this same repository here: [API Documentation](API_IMPLEMENTATION_SUMMARY.md)
428
+
429
+ ## 💻 Using the CLI
430
+
431
+ ### Available Commands
432
+
433
+ | Command | Description | Example |
434
+ |----------------------|--------------------------------------------|--------------------------------------------|
435
+ | `init` | Configure environment variables | `spotifysaver init"` |
436
+ | `download [URL]` | Download Spotify tracks/albums/playlists or YouTube albums/playlists | `spotifysaver download "URL"` |
437
+ | `inspect` | Shows Spotify metadata (album, playlist) | `spotifysaver inspect "URL_SPOTIFY"` |
438
+ | `show-log` | Shows the application log | `spotifysaver show-log` |
439
+ | `version` | Shows the installed version | `spotifysaver version` |
440
+
441
+ ### Download Options
442
+
443
+ | Option | Description | Accepted Values ​​|
444
+ |-------------------|-------------------------------------------------------|-------------------------|
445
+ | `--lyrics` | Download synchronized lyrics (.lrc) | Flag (no value) |
446
+ | `--output DIR` | Output directory | Valid path |
447
+ | `--format FORMAT` | Audio format | `m4a` (default), `mp3`, `opus` |
448
+ | `--bitrate KBPS` | Audio bitrate | `96`, `128` , `192`, `256`(default) |
449
+ | `--cover/--no-cover` | Save Spotify cover or embed YouTube thumbnail | Flag (no value) |
450
+ | `--nfo` | Generates a .nfo metadata file in the JellyFin format | Flag (no value) |
451
+ | `--explain` | Show score breakdown for each track without downloading (for error analysis) | Flag (no value) |
452
+ | `--dry-run` | Simulate download without saving files | Flag (no value) |
453
+
454
+ ### show-log Options
455
+
456
+ | Option | Description | Accepted Values ​​ |
457
+ |-------------|-----------------------------------------|-------------------------------|
458
+ | `--lines` | Number of log lines to display | `--lines 25` --> `int` |
459
+ | `--level` | Filter by log level | INFO, WARNING, DEBUG, ERROR |
460
+ | `--path` | Displays the location of the log file | Flag (no value) |
461
+
462
+ ## 💡 Usage Examples
463
+ ```bash
464
+ # Set spotifysaver configuration
465
+ spotifysaver init
466
+
467
+ # Download album with synchronized lyrics
468
+ spotifysaver download "https://open.spotify.com/album/..." --lyrics
469
+
470
+ # Download album with metadata file; the cover is saved as cover.jpg by default
471
+ spotifysaver download "https://open.spotify.com/album/..." --nfo
472
+
473
+ # Download song in MP3 format
474
+ spotifysaver download "https://open.spotify.com/track/..." --format mp3
475
+
476
+ # Download a YouTube Music album or playlist with YouTube-provided tags
477
+ spotifysaver download "https://music.youtube.com/playlist?list=..."
478
+ ```
479
+
480
+ The CLI accepts Spotify tracks, albums, and playlists, and YouTube playlists or album collections. Direct YouTube video URLs are supported by the web interface and API, but not by the CLI. Spotify playlist tracks are saved under their canonical artist/album folders, with playlist files kept separately in a `Playlists/` directory beside the configured output directory. Existing files are skipped by default; set the web interface's overwrite option or the API's `overwrite_existing` field to replace them.
481
+
482
+ ## Usage with API
483
+
484
+ To use the API, you need to have the API server running. You can start it with the following command:
485
+
486
+ ```bash
487
+ # Start the API server
488
+ spotifysaver-api
489
+ ```
490
+
491
+ The server will run at `http://127.0.0.1:8000` by default. The API accepts Spotify track, album, and playlist URLs, as well as direct YouTube video and collection URLs. Active downloads can be cancelled with `POST /api/v1/download/{task_id}/cancel`. You can find the [API documentation here](API_IMPLEMENTATION_SUMMARY.md), which describes the technical aspects and usage in detail.
492
+
493
+ ## 🖥️ Web Interface (UI)
494
+
495
+ Start the API server with web interface
496
+ ```bash
497
+ spotifysaver-api
498
+ ```
499
+
500
+ This will start the API server with an integrated web interface.
501
+ You can access at `http://127.0.0.1:8000` from the same machine or replace by your target ip or reverse-proxy address.
502
+
503
+ **Default Access:** (replace by your)
504
+ - Web Interface & API: `http://{address}:8000`
505
+ - API Documentation: `http://{address}:8000/docs`
506
+
507
+ The web interface and API **require an administrator password**. On first startup,
508
+ the server logs a one-time setup token; enter it in the browser to create the
509
+ password. Store and protect the server's configuration directory because it
510
+ contains the password hash, Spotify credentials, and authorization tokens.
511
+ Use **HTTPS** for remote access.
512
+
513
+ In **Settings → Backup & Restore**, download a JSON configuration backup before
514
+ migrating or reinstalling. The file includes watched channel URLs and server
515
+ paths, so keep it private. Restoring replaces the included settings on the
516
+ current instance. Passwords, Jellyfin API keys, Spotify client secrets, and
517
+ Spotify authorization tokens, active downloads, per-video watcher history, and
518
+ activity logs are not transferred; re-enter secrets and reconnect Spotify on
519
+ the destination if needed. Review media folder paths after restoring because
520
+ host and container paths can differ.
521
+
522
+ ## 📂 Output Structure
523
+ ```
524
+ .
525
+ ├── Music/
526
+ │ ├── Artist/
527
+ │ │ └── Album (Year)/
528
+ │ │ ├── 01 - Song.mp3
529
+ │ │ ├── 01 - Song.lrc
530
+ │ │ ├── album.nfo
531
+ │ │ └── cover.jpg
532
+ │ └── YouTube/
533
+ │ └── Collection Name/
534
+ │ └── 01 - Video Title.mp3
535
+ ├── Videos/
536
+ │ └── Channel Name/
537
+ │ └── Video Title.mp4
538
+ └── Playlists/
539
+ └── Playlist Name/
540
+ └── Playlist Name.m3u
541
+ ```
542
+ Videos are saved under a folder named for the YouTube channel or uploader within
543
+ the selected video folder (default: `Videos`). Playlist video filenames include
544
+ their position in the playlist, such as `01 - Video Title.mp4`.
545
+
546
+ ## 🤝 Contributions
547
+ 1. Fork the project
548
+ 2. Create your branch (`git checkout -b feature/new-feature`)
549
+ 3. Commit your changes (`git commit -m 'Add awesome feature'`)
550
+ 4. Push to the branch (`git push origin feature/new-feature`)
551
+ 5. Open a Pull Request
552
+
553
+ ## 📄 License
554
+
555
+ MIT © [TGabriel Baute](https://github.com/gabrielbaute)
556
+