gamdl 2.9.2__tar.gz → 3.0__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.
- {gamdl-2.9.2 → gamdl-3.0}/PKG-INFO +114 -76
- {gamdl-2.9.2 → gamdl-3.0}/README.md +111 -75
- gamdl-3.0/gamdl/__init__.py +1 -0
- gamdl-3.0/gamdl/api/__init__.py +3 -0
- gamdl-3.0/gamdl/api/apple_music.py +603 -0
- gamdl-3.0/gamdl/api/constants.py +34 -0
- gamdl-3.0/gamdl/api/exceptions.py +25 -0
- gamdl-3.0/gamdl/api/itunes.py +147 -0
- {gamdl-2.9.2 → gamdl-3.0}/gamdl/cli/cli.py +143 -141
- {gamdl-2.9.2 → gamdl-3.0}/gamdl/cli/cli_config.py +172 -148
- gamdl-3.0/gamdl/cli/database.py +48 -0
- gamdl-3.0/gamdl/cli/interactive_prompts.py +232 -0
- {gamdl-2.9.2 → gamdl-3.0}/gamdl/cli/utils.py +29 -25
- gamdl-3.0/gamdl/downloader/__init__.py +9 -0
- {gamdl-2.9.2 → gamdl-3.0}/gamdl/downloader/amdecrypt.py +152 -20
- gamdl-2.9.2/gamdl/downloader/downloader_base.py → gamdl-3.0/gamdl/downloader/base.py +72 -131
- gamdl-3.0/gamdl/downloader/constants.py +3 -0
- gamdl-3.0/gamdl/downloader/downloader.py +269 -0
- gamdl-3.0/gamdl/downloader/enums.py +16 -0
- gamdl-3.0/gamdl/downloader/exceptions.py +25 -0
- gamdl-3.0/gamdl/downloader/music_video.py +213 -0
- gamdl-3.0/gamdl/downloader/song.py +181 -0
- gamdl-3.0/gamdl/downloader/types.py +15 -0
- gamdl-3.0/gamdl/downloader/uploaded_video.py +65 -0
- gamdl-3.0/gamdl/interface/__init__.py +8 -0
- gamdl-3.0/gamdl/interface/base.py +309 -0
- {gamdl-2.9.2 → gamdl-3.0}/gamdl/interface/constants.py +36 -0
- {gamdl-2.9.2 → gamdl-3.0}/gamdl/interface/enums.py +19 -0
- gamdl-3.0/gamdl/interface/exceptions.py +42 -0
- gamdl-3.0/gamdl/interface/interface.py +608 -0
- gamdl-3.0/gamdl/interface/music_video.py +426 -0
- gamdl-2.9.2/gamdl/interface/interface_song.py → gamdl-3.0/gamdl/interface/song.py +137 -116
- {gamdl-2.9.2 → gamdl-3.0}/gamdl/interface/types.py +39 -3
- gamdl-3.0/gamdl/interface/uploaded_video.py +126 -0
- gamdl-3.0/gamdl/interface/wvd.py +3 -0
- {gamdl-2.9.2 → gamdl-3.0}/gamdl/utils.py +0 -43
- {gamdl-2.9.2 → gamdl-3.0}/gamdl.egg-info/PKG-INFO +114 -76
- {gamdl-2.9.2 → gamdl-3.0}/gamdl.egg-info/SOURCES.txt +15 -11
- {gamdl-2.9.2 → gamdl-3.0}/gamdl.egg-info/requires.txt +2 -0
- {gamdl-2.9.2 → gamdl-3.0}/pyproject.toml +9 -1
- gamdl-2.9.2/gamdl/__init__.py +0 -1
- gamdl-2.9.2/gamdl/api/__init__.py +0 -2
- gamdl-2.9.2/gamdl/api/apple_music_api.py +0 -467
- gamdl-2.9.2/gamdl/api/constants.py +0 -170
- gamdl-2.9.2/gamdl/api/exceptions.py +0 -7
- gamdl-2.9.2/gamdl/api/itunes_api.py +0 -86
- gamdl-2.9.2/gamdl/downloader/__init__.py +0 -8
- gamdl-2.9.2/gamdl/downloader/constants.py +0 -46
- gamdl-2.9.2/gamdl/downloader/downloader.py +0 -617
- gamdl-2.9.2/gamdl/downloader/downloader_music_video.py +0 -285
- gamdl-2.9.2/gamdl/downloader/downloader_song.py +0 -248
- gamdl-2.9.2/gamdl/downloader/downloader_uploaded_video.py +0 -107
- gamdl-2.9.2/gamdl/downloader/enums.py +0 -38
- gamdl-2.9.2/gamdl/downloader/exceptions.py +0 -31
- gamdl-2.9.2/gamdl/downloader/hardcoded_wvd.py +0 -3
- gamdl-2.9.2/gamdl/downloader/types.py +0 -44
- gamdl-2.9.2/gamdl/interface/__init__.py +0 -6
- gamdl-2.9.2/gamdl/interface/interface.py +0 -161
- gamdl-2.9.2/gamdl/interface/interface_music_video.py +0 -380
- gamdl-2.9.2/gamdl/interface/interface_uploaded_video.py +0 -86
- {gamdl-2.9.2 → gamdl-3.0}/LICENSE +0 -0
- {gamdl-2.9.2 → gamdl-3.0}/gamdl/__main__.py +0 -0
- {gamdl-2.9.2 → gamdl-3.0}/gamdl/cli/__init__.py +0 -0
- {gamdl-2.9.2 → gamdl-3.0}/gamdl/cli/config_file.py +0 -0
- {gamdl-2.9.2 → gamdl-3.0}/gamdl/cli/constants.py +0 -0
- {gamdl-2.9.2 → gamdl-3.0}/gamdl.egg-info/dependency_links.txt +0 -0
- {gamdl-2.9.2 → gamdl-3.0}/gamdl.egg-info/entry_points.txt +0 -0
- {gamdl-2.9.2 → gamdl-3.0}/gamdl.egg-info/top_level.txt +0 -0
- {gamdl-2.9.2 → gamdl-3.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gamdl
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0
|
|
4
4
|
Summary: A command-line app for downloading Apple Music songs, music videos and post videos.
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Project-URL: Repository, https://github.com/glomatico/gamdl
|
|
@@ -12,11 +12,13 @@ Requires-Dist: click>=8.3.0
|
|
|
12
12
|
Requires-Dist: colorama>=0.4.6
|
|
13
13
|
Requires-Dist: dataclass-click>=1.0.4
|
|
14
14
|
Requires-Dist: httpx>=0.28.1
|
|
15
|
+
Requires-Dist: httpx-retries>=0.4.6
|
|
15
16
|
Requires-Dist: inquirerpy>=0.3.4
|
|
16
17
|
Requires-Dist: m3u8>=6.0.0
|
|
17
18
|
Requires-Dist: mutagen>=1.47.0
|
|
18
19
|
Requires-Dist: pillow>=12.0.0
|
|
19
20
|
Requires-Dist: pywidevine>=1.8.0
|
|
21
|
+
Requires-Dist: structlog>=25.5.0
|
|
20
22
|
Requires-Dist: yt-dlp>=2025.10.22
|
|
21
23
|
Dynamic: license-file
|
|
22
24
|
|
|
@@ -49,28 +51,43 @@ A command-line app for downloading Apple Music songs, music videos and post vide
|
|
|
49
51
|
- **Firefox**: [Export Cookies](https://addons.mozilla.org/addon/export-cookies-txt)
|
|
50
52
|
- **Chromium**: [Get cookies.txt LOCALLY](https://chromewebstore.google.com/detail/get-cookiestxt-locally/cclelndahbckbenkjhflpdbgdldlbecc)
|
|
51
53
|
|
|
52
|
-
###
|
|
54
|
+
### Dependencies
|
|
53
55
|
|
|
54
|
-
Add these tools to your system PATH for
|
|
56
|
+
Add these tools to your system PATH or specify their paths via command-line arguments or the config file. The tools needed depend on which audio quality, video format, and download mode you want. Use the table below to find the required tools for your use case:
|
|
55
57
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
| Use Case | Configuration | Required Tools |
|
|
59
|
+
|---|---|---|
|
|
60
|
+
| **Songs in Legacy Codecs** | `song_codec_priority: aac-legacy\|aac-he-legacy` | None |
|
|
61
|
+
| **Songs in Non Legacy Codecs** | `song_codec_priority: aac\|aac-he\|aac-binaural\|aac-downmix\|aac-he-binaural\|aac-he-downmix\|atmos\|ac3`<br/>`use_wrapper: true` | Wrapper |
|
|
62
|
+
| **Music Videos** | `music_video_remux_mode: ffmpeg` | FFmpeg<br/>mp4decrypt |
|
|
63
|
+
| | `music_video_remux_mode: mp4box` | MP4Box<br/>mp4decrypt |
|
|
64
|
+
| **Faster Downloads** | `download_mode: nm3u8dlre` | N_m3u8DL-RE |
|
|
65
|
+
|
|
66
|
+
#### Tool Reference
|
|
67
|
+
|
|
68
|
+
| Tool | Download | Purpose |
|
|
69
|
+
|---|---|---|
|
|
70
|
+
| **FFmpeg** | [Windows](https://github.com/AnimMouse/ffmpeg-stable-autobuild/releases) / [Linux](https://johnvansickle.com/ffmpeg/) | Required for music video remuxing with FFmpeg mode |
|
|
71
|
+
| **MP4Box** | [Download](https://gpac.io/downloads/gpac-nightly-builds/) | Alternative for music video remuxing |
|
|
72
|
+
| **mp4decrypt** | [Download](https://www.bento4.com/downloads/) | Decrypts MP4 files when used with MP4Box |
|
|
73
|
+
| **N_m3u8DL-RE** | [Download](https://github.com/nilaoda/N_m3u8DL-RE/releases/latest) | Faster download alternative |
|
|
74
|
+
| **Wrapper** | [Download](https://github.com/WorldObservationLog/wrapper) | For downloading songs in ALAC and other experimental codecs |
|
|
61
75
|
|
|
62
76
|
## 📦 Installation
|
|
63
77
|
|
|
64
|
-
**Install Gamdl via pip:**
|
|
78
|
+
1. **Install Gamdl via pip:**
|
|
65
79
|
|
|
66
|
-
```bash
|
|
67
|
-
pip install gamdl
|
|
68
|
-
```
|
|
80
|
+
```bash
|
|
81
|
+
pip install gamdl
|
|
82
|
+
```
|
|
69
83
|
|
|
70
|
-
**
|
|
84
|
+
2. **Set up the cookies file:**
|
|
85
|
+
- Place the cookies file in the working directory as `cookies.txt`, or
|
|
86
|
+
- Specify the path using `--cookies-path` or in the config file
|
|
71
87
|
|
|
72
|
-
|
|
73
|
-
|
|
88
|
+
3. **Optional: Set up tools** (only if you need the functionality)
|
|
89
|
+
|
|
90
|
+
See the [Dependencies](#dependencies) section to determine which tools you need based on your use case, then follow the [Tool Reference](#tool-reference) for download and installation instructions.
|
|
74
91
|
|
|
75
92
|
## 🚀 Usage
|
|
76
93
|
|
|
@@ -138,54 +155,56 @@ The file is created automatically on first run. Command-line arguments override
|
|
|
138
155
|
| `--log-level` | Logging level | `INFO` |
|
|
139
156
|
| `--log-file` | Log file path | - |
|
|
140
157
|
| `--no-exceptions` | Don't print exceptions | `false` |
|
|
158
|
+
| `--artist-auto-select` | Automatically select artist content to download (artist URLs) | - |
|
|
159
|
+
| `--database-path` | Path to the SQLite database file for registering downloaded media | - |
|
|
141
160
|
| `--no-config-file`, `-n` | Don't use a config file | `false` |
|
|
142
161
|
| **Apple Music Options** | | |
|
|
143
162
|
| `--cookies-path`, `-c` | Cookies file path | `./cookies.txt` |
|
|
144
163
|
| `--wrapper-account-url` | Wrapper account URL | `http://127.0.0.1:30020` |
|
|
145
164
|
| `--language`, `-l` | Metadata language | `en-US` |
|
|
146
165
|
| **Output Options** | | |
|
|
166
|
+
| `--cover-format` | Cover format | `jpg` |
|
|
167
|
+
| `--cover-size` | Cover size in pixels | `1200` |
|
|
168
|
+
| `--wvd-path` | .wvd file path | - |
|
|
169
|
+
| **Song Options** | | |
|
|
170
|
+
| `--synced-lyrics-format` | Synced lyrics format | `lrc` |
|
|
171
|
+
| `--song-codec-priority` | Comma-separated codec priority | `aac-legacy` |
|
|
172
|
+
| `--use-album-date` | Use album release date for songs | `false` |
|
|
173
|
+
| `--no-synced-lyrics` | Don't download synced lyrics | `false` |
|
|
174
|
+
| `--synced-lyrics-only` | Download only synced lyrics | `false` |
|
|
175
|
+
| **Music Video Options** | | |
|
|
176
|
+
| `--music-video-resolution` | Max music video resolution | `1080p` |
|
|
177
|
+
| `--music-video-codec-priority` | Comma-separated codec priority | `h264,h265` |
|
|
178
|
+
| `--music-video-remux-mode` | Remux mode | `ffmpeg` |
|
|
179
|
+
| `--music-video-remux-format` | Music video remux format | `m4v` |
|
|
180
|
+
| **Post Video Options** | | |
|
|
181
|
+
| `--uploaded-video-quality` | Post video quality | `best` |
|
|
182
|
+
| **Download & Path Options** | | |
|
|
147
183
|
| `--output-path`, `-o` | Output directory path | `./Apple Music` |
|
|
148
184
|
| `--temp-path` | Temporary directory path | `.` |
|
|
149
|
-
| `--wvd-path` | .wvd file path | - |
|
|
150
|
-
| `--overwrite` | Overwrite existing files | `false` |
|
|
151
|
-
| `--save-cover`, `-s` | Save cover as separate file | `false` |
|
|
152
|
-
| `--save-playlist` | Save M3U8 playlist file | `false` |
|
|
153
|
-
| **Download Options** | | |
|
|
154
|
-
| `--artist-auto-select` | Automatically select artist content to download (artist URLs) | - |
|
|
155
185
|
| `--nm3u8dlre-path` | N_m3u8DL-RE executable path | `N_m3u8DL-RE` |
|
|
156
186
|
| `--mp4decrypt-path` | mp4decrypt executable path | `mp4decrypt` |
|
|
157
187
|
| `--ffmpeg-path` | FFmpeg executable path | `ffmpeg` |
|
|
158
188
|
| `--mp4box-path` | MP4Box executable path | `MP4Box` |
|
|
159
|
-
| `--use-wrapper` | Use wrapper
|
|
189
|
+
| `--use-wrapper` | Use wrapper for decrypting songs | `false` |
|
|
160
190
|
| `--wrapper-decrypt-ip` | Wrapper decryption server IP | `127.0.0.1:10020` |
|
|
161
191
|
| `--download-mode` | Download mode | `ytdlp` |
|
|
162
|
-
| `--cover-format` | Cover format | `jpg` |
|
|
163
192
|
| **Template Options** | | |
|
|
164
193
|
| `--album-folder-template` | Album folder template | `{album_artist}/{album}` |
|
|
165
194
|
| `--compilation-folder-template` | Compilation folder template | `Compilations/{album}` |
|
|
166
195
|
| `--no-album-folder-template` | No album folder template | `{artist}/Unknown Album` |
|
|
196
|
+
| `--playlist-folder-template` | Playlist folder template | `Playlists/{playlist_artist}/{playlist_title}` |
|
|
167
197
|
| `--single-disc-file-template` | Single disc file template | `{track:02d} {title}` |
|
|
168
198
|
| `--multi-disc-file-template` | Multi disc file template | `{disc}-{track:02d} {title}` |
|
|
169
199
|
| `--no-album-file-template` | No album file template | `{title}` |
|
|
170
200
|
| `--playlist-file-template` | Playlist file template | `Playlists/{playlist_artist}/{playlist_title}` |
|
|
171
201
|
| `--date-tag-template` | Date tag template | `%Y-%m-%dT%H:%M:%SZ` |
|
|
172
202
|
| `--exclude-tags` | Comma-separated tags to exclude | - |
|
|
173
|
-
| `--cover-size` | Cover size in pixels | `1200` |
|
|
174
203
|
| `--truncate` | Max filename length | - |
|
|
175
|
-
| **
|
|
176
|
-
| `--
|
|
177
|
-
| `--
|
|
178
|
-
| `--
|
|
179
|
-
| `--synced-lyrics-only` | Download only synced lyrics | `false` |
|
|
180
|
-
| `--use-album-date` | Use album release date for songs | `false` |
|
|
181
|
-
| `--fetch-extra-tags` | Fetch extra tags from preview (normalization and smooth playback) | `false` |
|
|
182
|
-
| **Music Video Options** | | |
|
|
183
|
-
| `--music-video-codec-priority` | Comma-separated codec priority | `h264,h265` |
|
|
184
|
-
| `--music-video-remux-mode` | Remux mode | `ffmpeg` |
|
|
185
|
-
| `--music-video-remux-format` | Music video remux format | `m4v` |
|
|
186
|
-
| `--music-video-resolution` | Max music video resolution | `1080p` |
|
|
187
|
-
| **Post Video Options** | | |
|
|
188
|
-
| `--uploaded-video-quality` | Post video quality | `best` |
|
|
204
|
+
| **File Output Options** | | |
|
|
205
|
+
| `--overwrite` | Overwrite existing files | `false` |
|
|
206
|
+
| `--save-cover`, `-s` | Save cover as separate file | `false` |
|
|
207
|
+
| `--save-playlist` | Save M3U8 playlist file | `false` |
|
|
189
208
|
|
|
190
209
|
|
|
191
210
|
### Template Variables
|
|
@@ -216,6 +235,9 @@ The file is created automatically on first run. Command-line arguments override
|
|
|
216
235
|
|
|
217
236
|
- `ytdlp`, `nm3u8dlre`
|
|
218
237
|
|
|
238
|
+
> [!NOTE]
|
|
239
|
+
> - **yt-dlp is only used as a file download library**. Media is still fetched directly from Apple Music's servers, and yt-dlp is only responsible for handling the file download process.
|
|
240
|
+
|
|
219
241
|
### Remux Mode
|
|
220
242
|
|
|
221
243
|
- `ffmpeg`
|
|
@@ -304,7 +326,7 @@ Use Gamdl as a library in your Python projects:
|
|
|
304
326
|
```python
|
|
305
327
|
import asyncio
|
|
306
328
|
|
|
307
|
-
from gamdl.api import AppleMusicApi
|
|
329
|
+
from gamdl.api import AppleMusicApi
|
|
308
330
|
from gamdl.downloader import (
|
|
309
331
|
AppleMusicBaseDownloader,
|
|
310
332
|
AppleMusicDownloader,
|
|
@@ -313,63 +335,79 @@ from gamdl.downloader import (
|
|
|
313
335
|
AppleMusicUploadedVideoDownloader,
|
|
314
336
|
)
|
|
315
337
|
from gamdl.interface import (
|
|
338
|
+
AppleMusicBaseInterface,
|
|
316
339
|
AppleMusicInterface,
|
|
317
340
|
AppleMusicMusicVideoInterface,
|
|
318
341
|
AppleMusicSongInterface,
|
|
319
342
|
AppleMusicUploadedVideoInterface,
|
|
320
343
|
)
|
|
321
344
|
|
|
345
|
+
|
|
322
346
|
async def main():
|
|
323
|
-
# Create AppleMusicApi instance
|
|
347
|
+
# Create AppleMusicApi instance from cookies
|
|
324
348
|
apple_music_api = await AppleMusicApi.create_from_netscape_cookies(
|
|
325
349
|
cookies_path="cookies.txt",
|
|
326
350
|
)
|
|
327
|
-
itunes_api = ItunesApi(
|
|
328
|
-
apple_music_api.storefront,
|
|
329
|
-
apple_music_api.language,
|
|
330
|
-
)
|
|
331
351
|
|
|
332
352
|
# Check subscription
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
# Set up base downloader and specialized downloaders
|
|
342
|
-
base_downloader = AppleMusicBaseDownloader()
|
|
343
|
-
song_downloader = AppleMusicSongDownloader(
|
|
344
|
-
base_downloader=base_downloader,
|
|
345
|
-
interface=song_interface,
|
|
353
|
+
if not apple_music_api.active_subscription:
|
|
354
|
+
print("No active Apple Music subscription")
|
|
355
|
+
return
|
|
356
|
+
|
|
357
|
+
# Create base interface
|
|
358
|
+
base_interface = await AppleMusicBaseInterface.create(
|
|
359
|
+
apple_music_api=apple_music_api,
|
|
346
360
|
)
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
361
|
+
|
|
362
|
+
# Create specialized interfaces
|
|
363
|
+
song_interface = AppleMusicSongInterface(
|
|
364
|
+
base=base_interface,
|
|
365
|
+
)
|
|
366
|
+
music_video_interface = AppleMusicMusicVideoInterface(
|
|
367
|
+
base=base_interface,
|
|
350
368
|
)
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
interface=uploaded_video_interface,
|
|
369
|
+
uploaded_video_interface = AppleMusicUploadedVideoInterface(
|
|
370
|
+
base=base_interface,
|
|
354
371
|
)
|
|
355
372
|
|
|
356
|
-
#
|
|
357
|
-
|
|
373
|
+
# Create main interface
|
|
374
|
+
interface = AppleMusicInterface(
|
|
375
|
+
song=song_interface,
|
|
376
|
+
music_video=music_video_interface,
|
|
377
|
+
uploaded_video=uploaded_video_interface,
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
# Create base downloader
|
|
381
|
+
base_downloader = AppleMusicBaseDownloader(
|
|
358
382
|
interface=interface,
|
|
359
|
-
base_downloader=base_downloader,
|
|
360
|
-
song_downloader=song_downloader,
|
|
361
|
-
music_video_downloader=music_video_downloader,
|
|
362
|
-
uploaded_video_downloader=uploaded_video_downloader,
|
|
363
383
|
)
|
|
364
384
|
|
|
365
|
-
#
|
|
385
|
+
# Create specialized downloaders
|
|
386
|
+
song_downloader = AppleMusicSongDownloader(base=base_downloader)
|
|
387
|
+
music_video_downloader = AppleMusicMusicVideoDownloader(
|
|
388
|
+
base=base_downloader,
|
|
389
|
+
)
|
|
390
|
+
uploaded_video_downloader = AppleMusicUploadedVideoDownloader(base=base_downloader)
|
|
391
|
+
|
|
392
|
+
# Create main downloader
|
|
393
|
+
downloader = AppleMusicDownloader(
|
|
394
|
+
song=song_downloader,
|
|
395
|
+
music_video=music_video_downloader,
|
|
396
|
+
uploaded_video=uploaded_video_downloader,
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
# Download from URL
|
|
366
400
|
url = "https://music.apple.com/us/album/never-gonna-give-you-up-2022-remaster/1624945511?i=1624945512"
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
401
|
+
|
|
402
|
+
download_queue = []
|
|
403
|
+
async for media in downloader.get_download_item_from_url(url):
|
|
404
|
+
download_queue.append(media)
|
|
405
|
+
|
|
406
|
+
for download_item in download_queue:
|
|
407
|
+
try:
|
|
408
|
+
await downloader.download(download_item)
|
|
409
|
+
except Exception as e:
|
|
410
|
+
print(f"Error downloading: {e}")
|
|
373
411
|
|
|
374
412
|
|
|
375
413
|
if __name__ == "__main__":
|
|
@@ -27,28 +27,43 @@ A command-line app for downloading Apple Music songs, music videos and post vide
|
|
|
27
27
|
- **Firefox**: [Export Cookies](https://addons.mozilla.org/addon/export-cookies-txt)
|
|
28
28
|
- **Chromium**: [Get cookies.txt LOCALLY](https://chromewebstore.google.com/detail/get-cookiestxt-locally/cclelndahbckbenkjhflpdbgdldlbecc)
|
|
29
29
|
|
|
30
|
-
###
|
|
30
|
+
### Dependencies
|
|
31
31
|
|
|
32
|
-
Add these tools to your system PATH for
|
|
32
|
+
Add these tools to your system PATH or specify their paths via command-line arguments or the config file. The tools needed depend on which audio quality, video format, and download mode you want. Use the table below to find the required tools for your use case:
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
| Use Case | Configuration | Required Tools |
|
|
35
|
+
|---|---|---|
|
|
36
|
+
| **Songs in Legacy Codecs** | `song_codec_priority: aac-legacy\|aac-he-legacy` | None |
|
|
37
|
+
| **Songs in Non Legacy Codecs** | `song_codec_priority: aac\|aac-he\|aac-binaural\|aac-downmix\|aac-he-binaural\|aac-he-downmix\|atmos\|ac3`<br/>`use_wrapper: true` | Wrapper |
|
|
38
|
+
| **Music Videos** | `music_video_remux_mode: ffmpeg` | FFmpeg<br/>mp4decrypt |
|
|
39
|
+
| | `music_video_remux_mode: mp4box` | MP4Box<br/>mp4decrypt |
|
|
40
|
+
| **Faster Downloads** | `download_mode: nm3u8dlre` | N_m3u8DL-RE |
|
|
41
|
+
|
|
42
|
+
#### Tool Reference
|
|
43
|
+
|
|
44
|
+
| Tool | Download | Purpose |
|
|
45
|
+
|---|---|---|
|
|
46
|
+
| **FFmpeg** | [Windows](https://github.com/AnimMouse/ffmpeg-stable-autobuild/releases) / [Linux](https://johnvansickle.com/ffmpeg/) | Required for music video remuxing with FFmpeg mode |
|
|
47
|
+
| **MP4Box** | [Download](https://gpac.io/downloads/gpac-nightly-builds/) | Alternative for music video remuxing |
|
|
48
|
+
| **mp4decrypt** | [Download](https://www.bento4.com/downloads/) | Decrypts MP4 files when used with MP4Box |
|
|
49
|
+
| **N_m3u8DL-RE** | [Download](https://github.com/nilaoda/N_m3u8DL-RE/releases/latest) | Faster download alternative |
|
|
50
|
+
| **Wrapper** | [Download](https://github.com/WorldObservationLog/wrapper) | For downloading songs in ALAC and other experimental codecs |
|
|
39
51
|
|
|
40
52
|
## 📦 Installation
|
|
41
53
|
|
|
42
|
-
**Install Gamdl via pip:**
|
|
54
|
+
1. **Install Gamdl via pip:**
|
|
43
55
|
|
|
44
|
-
```bash
|
|
45
|
-
pip install gamdl
|
|
46
|
-
```
|
|
56
|
+
```bash
|
|
57
|
+
pip install gamdl
|
|
58
|
+
```
|
|
47
59
|
|
|
48
|
-
**
|
|
60
|
+
2. **Set up the cookies file:**
|
|
61
|
+
- Place the cookies file in the working directory as `cookies.txt`, or
|
|
62
|
+
- Specify the path using `--cookies-path` or in the config file
|
|
49
63
|
|
|
50
|
-
|
|
51
|
-
|
|
64
|
+
3. **Optional: Set up tools** (only if you need the functionality)
|
|
65
|
+
|
|
66
|
+
See the [Dependencies](#dependencies) section to determine which tools you need based on your use case, then follow the [Tool Reference](#tool-reference) for download and installation instructions.
|
|
52
67
|
|
|
53
68
|
## 🚀 Usage
|
|
54
69
|
|
|
@@ -116,54 +131,56 @@ The file is created automatically on first run. Command-line arguments override
|
|
|
116
131
|
| `--log-level` | Logging level | `INFO` |
|
|
117
132
|
| `--log-file` | Log file path | - |
|
|
118
133
|
| `--no-exceptions` | Don't print exceptions | `false` |
|
|
134
|
+
| `--artist-auto-select` | Automatically select artist content to download (artist URLs) | - |
|
|
135
|
+
| `--database-path` | Path to the SQLite database file for registering downloaded media | - |
|
|
119
136
|
| `--no-config-file`, `-n` | Don't use a config file | `false` |
|
|
120
137
|
| **Apple Music Options** | | |
|
|
121
138
|
| `--cookies-path`, `-c` | Cookies file path | `./cookies.txt` |
|
|
122
139
|
| `--wrapper-account-url` | Wrapper account URL | `http://127.0.0.1:30020` |
|
|
123
140
|
| `--language`, `-l` | Metadata language | `en-US` |
|
|
124
141
|
| **Output Options** | | |
|
|
142
|
+
| `--cover-format` | Cover format | `jpg` |
|
|
143
|
+
| `--cover-size` | Cover size in pixels | `1200` |
|
|
144
|
+
| `--wvd-path` | .wvd file path | - |
|
|
145
|
+
| **Song Options** | | |
|
|
146
|
+
| `--synced-lyrics-format` | Synced lyrics format | `lrc` |
|
|
147
|
+
| `--song-codec-priority` | Comma-separated codec priority | `aac-legacy` |
|
|
148
|
+
| `--use-album-date` | Use album release date for songs | `false` |
|
|
149
|
+
| `--no-synced-lyrics` | Don't download synced lyrics | `false` |
|
|
150
|
+
| `--synced-lyrics-only` | Download only synced lyrics | `false` |
|
|
151
|
+
| **Music Video Options** | | |
|
|
152
|
+
| `--music-video-resolution` | Max music video resolution | `1080p` |
|
|
153
|
+
| `--music-video-codec-priority` | Comma-separated codec priority | `h264,h265` |
|
|
154
|
+
| `--music-video-remux-mode` | Remux mode | `ffmpeg` |
|
|
155
|
+
| `--music-video-remux-format` | Music video remux format | `m4v` |
|
|
156
|
+
| **Post Video Options** | | |
|
|
157
|
+
| `--uploaded-video-quality` | Post video quality | `best` |
|
|
158
|
+
| **Download & Path Options** | | |
|
|
125
159
|
| `--output-path`, `-o` | Output directory path | `./Apple Music` |
|
|
126
160
|
| `--temp-path` | Temporary directory path | `.` |
|
|
127
|
-
| `--wvd-path` | .wvd file path | - |
|
|
128
|
-
| `--overwrite` | Overwrite existing files | `false` |
|
|
129
|
-
| `--save-cover`, `-s` | Save cover as separate file | `false` |
|
|
130
|
-
| `--save-playlist` | Save M3U8 playlist file | `false` |
|
|
131
|
-
| **Download Options** | | |
|
|
132
|
-
| `--artist-auto-select` | Automatically select artist content to download (artist URLs) | - |
|
|
133
161
|
| `--nm3u8dlre-path` | N_m3u8DL-RE executable path | `N_m3u8DL-RE` |
|
|
134
162
|
| `--mp4decrypt-path` | mp4decrypt executable path | `mp4decrypt` |
|
|
135
163
|
| `--ffmpeg-path` | FFmpeg executable path | `ffmpeg` |
|
|
136
164
|
| `--mp4box-path` | MP4Box executable path | `MP4Box` |
|
|
137
|
-
| `--use-wrapper` | Use wrapper
|
|
165
|
+
| `--use-wrapper` | Use wrapper for decrypting songs | `false` |
|
|
138
166
|
| `--wrapper-decrypt-ip` | Wrapper decryption server IP | `127.0.0.1:10020` |
|
|
139
167
|
| `--download-mode` | Download mode | `ytdlp` |
|
|
140
|
-
| `--cover-format` | Cover format | `jpg` |
|
|
141
168
|
| **Template Options** | | |
|
|
142
169
|
| `--album-folder-template` | Album folder template | `{album_artist}/{album}` |
|
|
143
170
|
| `--compilation-folder-template` | Compilation folder template | `Compilations/{album}` |
|
|
144
171
|
| `--no-album-folder-template` | No album folder template | `{artist}/Unknown Album` |
|
|
172
|
+
| `--playlist-folder-template` | Playlist folder template | `Playlists/{playlist_artist}/{playlist_title}` |
|
|
145
173
|
| `--single-disc-file-template` | Single disc file template | `{track:02d} {title}` |
|
|
146
174
|
| `--multi-disc-file-template` | Multi disc file template | `{disc}-{track:02d} {title}` |
|
|
147
175
|
| `--no-album-file-template` | No album file template | `{title}` |
|
|
148
176
|
| `--playlist-file-template` | Playlist file template | `Playlists/{playlist_artist}/{playlist_title}` |
|
|
149
177
|
| `--date-tag-template` | Date tag template | `%Y-%m-%dT%H:%M:%SZ` |
|
|
150
178
|
| `--exclude-tags` | Comma-separated tags to exclude | - |
|
|
151
|
-
| `--cover-size` | Cover size in pixels | `1200` |
|
|
152
179
|
| `--truncate` | Max filename length | - |
|
|
153
|
-
| **
|
|
154
|
-
| `--
|
|
155
|
-
| `--
|
|
156
|
-
| `--
|
|
157
|
-
| `--synced-lyrics-only` | Download only synced lyrics | `false` |
|
|
158
|
-
| `--use-album-date` | Use album release date for songs | `false` |
|
|
159
|
-
| `--fetch-extra-tags` | Fetch extra tags from preview (normalization and smooth playback) | `false` |
|
|
160
|
-
| **Music Video Options** | | |
|
|
161
|
-
| `--music-video-codec-priority` | Comma-separated codec priority | `h264,h265` |
|
|
162
|
-
| `--music-video-remux-mode` | Remux mode | `ffmpeg` |
|
|
163
|
-
| `--music-video-remux-format` | Music video remux format | `m4v` |
|
|
164
|
-
| `--music-video-resolution` | Max music video resolution | `1080p` |
|
|
165
|
-
| **Post Video Options** | | |
|
|
166
|
-
| `--uploaded-video-quality` | Post video quality | `best` |
|
|
180
|
+
| **File Output Options** | | |
|
|
181
|
+
| `--overwrite` | Overwrite existing files | `false` |
|
|
182
|
+
| `--save-cover`, `-s` | Save cover as separate file | `false` |
|
|
183
|
+
| `--save-playlist` | Save M3U8 playlist file | `false` |
|
|
167
184
|
|
|
168
185
|
|
|
169
186
|
### Template Variables
|
|
@@ -194,6 +211,9 @@ The file is created automatically on first run. Command-line arguments override
|
|
|
194
211
|
|
|
195
212
|
- `ytdlp`, `nm3u8dlre`
|
|
196
213
|
|
|
214
|
+
> [!NOTE]
|
|
215
|
+
> - **yt-dlp is only used as a file download library**. Media is still fetched directly from Apple Music's servers, and yt-dlp is only responsible for handling the file download process.
|
|
216
|
+
|
|
197
217
|
### Remux Mode
|
|
198
218
|
|
|
199
219
|
- `ffmpeg`
|
|
@@ -282,7 +302,7 @@ Use Gamdl as a library in your Python projects:
|
|
|
282
302
|
```python
|
|
283
303
|
import asyncio
|
|
284
304
|
|
|
285
|
-
from gamdl.api import AppleMusicApi
|
|
305
|
+
from gamdl.api import AppleMusicApi
|
|
286
306
|
from gamdl.downloader import (
|
|
287
307
|
AppleMusicBaseDownloader,
|
|
288
308
|
AppleMusicDownloader,
|
|
@@ -291,63 +311,79 @@ from gamdl.downloader import (
|
|
|
291
311
|
AppleMusicUploadedVideoDownloader,
|
|
292
312
|
)
|
|
293
313
|
from gamdl.interface import (
|
|
314
|
+
AppleMusicBaseInterface,
|
|
294
315
|
AppleMusicInterface,
|
|
295
316
|
AppleMusicMusicVideoInterface,
|
|
296
317
|
AppleMusicSongInterface,
|
|
297
318
|
AppleMusicUploadedVideoInterface,
|
|
298
319
|
)
|
|
299
320
|
|
|
321
|
+
|
|
300
322
|
async def main():
|
|
301
|
-
# Create AppleMusicApi instance
|
|
323
|
+
# Create AppleMusicApi instance from cookies
|
|
302
324
|
apple_music_api = await AppleMusicApi.create_from_netscape_cookies(
|
|
303
325
|
cookies_path="cookies.txt",
|
|
304
326
|
)
|
|
305
|
-
itunes_api = ItunesApi(
|
|
306
|
-
apple_music_api.storefront,
|
|
307
|
-
apple_music_api.language,
|
|
308
|
-
)
|
|
309
327
|
|
|
310
328
|
# Check subscription
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
# Set up base downloader and specialized downloaders
|
|
320
|
-
base_downloader = AppleMusicBaseDownloader()
|
|
321
|
-
song_downloader = AppleMusicSongDownloader(
|
|
322
|
-
base_downloader=base_downloader,
|
|
323
|
-
interface=song_interface,
|
|
329
|
+
if not apple_music_api.active_subscription:
|
|
330
|
+
print("No active Apple Music subscription")
|
|
331
|
+
return
|
|
332
|
+
|
|
333
|
+
# Create base interface
|
|
334
|
+
base_interface = await AppleMusicBaseInterface.create(
|
|
335
|
+
apple_music_api=apple_music_api,
|
|
324
336
|
)
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
337
|
+
|
|
338
|
+
# Create specialized interfaces
|
|
339
|
+
song_interface = AppleMusicSongInterface(
|
|
340
|
+
base=base_interface,
|
|
341
|
+
)
|
|
342
|
+
music_video_interface = AppleMusicMusicVideoInterface(
|
|
343
|
+
base=base_interface,
|
|
328
344
|
)
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
interface=uploaded_video_interface,
|
|
345
|
+
uploaded_video_interface = AppleMusicUploadedVideoInterface(
|
|
346
|
+
base=base_interface,
|
|
332
347
|
)
|
|
333
348
|
|
|
334
|
-
#
|
|
335
|
-
|
|
349
|
+
# Create main interface
|
|
350
|
+
interface = AppleMusicInterface(
|
|
351
|
+
song=song_interface,
|
|
352
|
+
music_video=music_video_interface,
|
|
353
|
+
uploaded_video=uploaded_video_interface,
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
# Create base downloader
|
|
357
|
+
base_downloader = AppleMusicBaseDownloader(
|
|
336
358
|
interface=interface,
|
|
337
|
-
base_downloader=base_downloader,
|
|
338
|
-
song_downloader=song_downloader,
|
|
339
|
-
music_video_downloader=music_video_downloader,
|
|
340
|
-
uploaded_video_downloader=uploaded_video_downloader,
|
|
341
359
|
)
|
|
342
360
|
|
|
343
|
-
#
|
|
361
|
+
# Create specialized downloaders
|
|
362
|
+
song_downloader = AppleMusicSongDownloader(base=base_downloader)
|
|
363
|
+
music_video_downloader = AppleMusicMusicVideoDownloader(
|
|
364
|
+
base=base_downloader,
|
|
365
|
+
)
|
|
366
|
+
uploaded_video_downloader = AppleMusicUploadedVideoDownloader(base=base_downloader)
|
|
367
|
+
|
|
368
|
+
# Create main downloader
|
|
369
|
+
downloader = AppleMusicDownloader(
|
|
370
|
+
song=song_downloader,
|
|
371
|
+
music_video=music_video_downloader,
|
|
372
|
+
uploaded_video=uploaded_video_downloader,
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
# Download from URL
|
|
344
376
|
url = "https://music.apple.com/us/album/never-gonna-give-you-up-2022-remaster/1624945511?i=1624945512"
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
377
|
+
|
|
378
|
+
download_queue = []
|
|
379
|
+
async for media in downloader.get_download_item_from_url(url):
|
|
380
|
+
download_queue.append(media)
|
|
381
|
+
|
|
382
|
+
for download_item in download_queue:
|
|
383
|
+
try:
|
|
384
|
+
await downloader.download(download_item)
|
|
385
|
+
except Exception as e:
|
|
386
|
+
print(f"Error downloading: {e}")
|
|
351
387
|
|
|
352
388
|
|
|
353
389
|
if __name__ == "__main__":
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "3.0"
|