mirror of
https://github.com/glomatico/gamdl.git
synced 2026-06-13 12:15:18 +03:00
LRC only mode
This commit is contained in:
@@ -42,9 +42,9 @@ Some new features that I added:
|
||||
|
||||
## Usage
|
||||
```
|
||||
usage: gamdl [-h] [-u [URLS_TXT]] [-w WVD_LOCATION] [-f FINAL_PATH]
|
||||
usage: __main__.py [-h] [-u [URLS_TXT]] [-w WVD_LOCATION] [-f FINAL_PATH]
|
||||
[-t TEMP_PATH] [-c COOKIES_LOCATION] [-m] [-p] [-o] [-n]
|
||||
[-s] [-e] [-v]
|
||||
[-l] [-s] [-e] [-v]
|
||||
[url ...]
|
||||
|
||||
Download Apple Music songs/music videos/albums/playlists
|
||||
@@ -58,19 +58,23 @@ options:
|
||||
-u [URLS_TXT], --urls-txt [URLS_TXT]
|
||||
Read URLs from a text file (default: None)
|
||||
-w WVD_LOCATION, --wvd-location WVD_LOCATION
|
||||
.wvd file location (default: *.wvd)
|
||||
.wvd file location (ignored if using -l/--lrc-only)
|
||||
(default: ./*.wvd)
|
||||
-f FINAL_PATH, --final-path FINAL_PATH
|
||||
Final Path (default: Apple Music)
|
||||
Final Path (default: ./Apple Music)
|
||||
-t TEMP_PATH, --temp-path TEMP_PATH
|
||||
Temp Path (default: temp)
|
||||
Temp Path (default: ./temp)
|
||||
-c COOKIES_LOCATION, --cookies-location COOKIES_LOCATION
|
||||
Cookies location (default: cookies.txt)
|
||||
Cookies location (default: ./cookies.txt)
|
||||
-m, --disable-music-video-skip
|
||||
Disable music video skip on playlists/albums (default:
|
||||
False)
|
||||
-p, --prefer-hevc Prefer HEVC over AVC (default: False)
|
||||
-o, --overwrite Overwrite existing files (default: False)
|
||||
-n, --no-lrc Don't create .lrc file (default: False)
|
||||
-n, --no-lrc Don't create .lrc file (ignored if using -l/--lrc-
|
||||
only) (default: False)
|
||||
-l, --lrc-only Skip downloading songs and only create .lrc files
|
||||
(default: False)
|
||||
-s, --skip-cleanup Skip cleanup (default: False)
|
||||
-e, --print-exceptions
|
||||
Print execeptions (default: False)
|
||||
|
||||
+48
-35
@@ -12,91 +12,97 @@ def main():
|
||||
if not shutil.which('MP4Box'):
|
||||
raise Exception('MP4Box is not on PATH')
|
||||
parser = argparse.ArgumentParser(
|
||||
description = 'Download Apple Music songs/music videos/albums/playlists',
|
||||
formatter_class = argparse.ArgumentDefaultsHelpFormatter,
|
||||
description='Download Apple Music songs/music videos/albums/playlists',
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
)
|
||||
parser.add_argument(
|
||||
'url',
|
||||
help = 'Apple Music song/music video/album/playlist URL(s)',
|
||||
nargs = '*',
|
||||
help='Apple Music song/music video/album/playlist URL(s)',
|
||||
nargs='*',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-u',
|
||||
'--urls-txt',
|
||||
help = 'Read URLs from a text file',
|
||||
nargs = '?',
|
||||
help='Read URLs from a text file',
|
||||
nargs='?',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-w',
|
||||
'--wvd-location',
|
||||
default = '*.wvd',
|
||||
help = '.wvd file location'
|
||||
default='./*.wvd',
|
||||
help='.wvd file location (ignored if using -l/--lrc-only)',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-f',
|
||||
'--final-path',
|
||||
default = 'Apple Music',
|
||||
help = 'Final Path',
|
||||
default='./Apple Music',
|
||||
help='Final Path',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-t',
|
||||
'--temp-path',
|
||||
default = 'temp',
|
||||
help = 'Temp Path',
|
||||
default='./temp',
|
||||
help='Temp Path',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-c',
|
||||
'--cookies-location',
|
||||
default = 'cookies.txt',
|
||||
help = 'Cookies location',
|
||||
default='./cookies.txt',
|
||||
help='Cookies location',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-m',
|
||||
'--disable-music-video-skip',
|
||||
action = 'store_true',
|
||||
help = 'Disable music video skip on playlists/albums',
|
||||
action='store_true',
|
||||
help='Disable music video skip on playlists/albums',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-p',
|
||||
'--prefer-hevc',
|
||||
action = 'store_true',
|
||||
help = 'Prefer HEVC over AVC',
|
||||
action='store_true',
|
||||
help='Prefer HEVC over AVC',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-o',
|
||||
'--overwrite',
|
||||
action = 'store_true',
|
||||
help = 'Overwrite existing files',
|
||||
action='store_true',
|
||||
help='Overwrite existing files',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-n',
|
||||
'--no-lrc',
|
||||
action = 'store_true',
|
||||
help = "Don't create .lrc file",
|
||||
action='store_true',
|
||||
help="Don't create .lrc file (ignored if using -l/--lrc-only)",
|
||||
)
|
||||
parser.add_argument(
|
||||
'-l',
|
||||
'--lrc-only',
|
||||
action='store_true',
|
||||
help='Skip downloading songs and only create .lrc files'
|
||||
)
|
||||
parser.add_argument(
|
||||
'-s',
|
||||
'--skip-cleanup',
|
||||
action = 'store_true',
|
||||
help = 'Skip cleanup',
|
||||
action='store_true',
|
||||
help='Skip cleanup',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-e',
|
||||
'--print-exceptions',
|
||||
action = 'store_true',
|
||||
help = 'Print execeptions',
|
||||
action='store_true',
|
||||
help='Print execeptions',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-v',
|
||||
'--version',
|
||||
action = 'version',
|
||||
version = f'%(prog)s {__version__}',
|
||||
action='version',
|
||||
version=f'%(prog)s {__version__}',
|
||||
)
|
||||
args = parser.parse_args()
|
||||
if not args.url and not args.urls_txt:
|
||||
parser.error('you must specify an url or a text file using -u/--urls-txt')
|
||||
if args.urls_txt:
|
||||
with open(args.urls_txt, 'r', encoding = 'utf8') as f:
|
||||
with open(args.urls_txt, 'r', encoding='utf8') as f:
|
||||
args.url = f.read().splitlines()
|
||||
dl = Gamdl(
|
||||
args.wvd_location,
|
||||
@@ -105,7 +111,7 @@ def main():
|
||||
args.prefer_hevc,
|
||||
args.temp_path,
|
||||
args.final_path,
|
||||
args.no_lrc,
|
||||
args.lrc_only,
|
||||
args.overwrite,
|
||||
args.skip_cleanup,
|
||||
)
|
||||
@@ -130,7 +136,7 @@ def main():
|
||||
if track['type'] == 'music-videos':
|
||||
tags = dl.get_tags_music_video(track['attributes']['url'].split('/')[-1].split('?')[0])
|
||||
final_location = dl.get_final_location('.m4v', tags)
|
||||
if dl.check_exists(final_location) and not args.overwrite:
|
||||
if final_location.exists() and not args.overwrite:
|
||||
continue
|
||||
stream_url_video, stream_url_audio = dl.get_stream_url_music_video(webplayback)
|
||||
decryption_keys_audio = dl.get_decryption_keys_music_video(stream_url_audio, track_id)
|
||||
@@ -145,12 +151,17 @@ def main():
|
||||
dl.decrypt(encrypted_location_video, decrypted_location_video, decryption_keys_video)
|
||||
fixed_location = dl.get_fixed_location(track_id, '.m4v')
|
||||
dl.fixup_music_video(decrypted_location_audio, decrypted_location_video, fixed_location)
|
||||
dl.make_final(final_location, fixed_location, tags)
|
||||
final_location.parent.mkdir(parents=True, exist_ok=True)
|
||||
dl.move_final(final_location, fixed_location, tags)
|
||||
else:
|
||||
unsynced_lyrics, synced_lyrics = dl.get_lyrics(track_id)
|
||||
tags = dl.get_tags_song(webplayback, unsynced_lyrics)
|
||||
final_location = dl.get_final_location('.m4a', tags)
|
||||
if dl.check_exists(final_location) and not args.overwrite:
|
||||
if args.lrc_only:
|
||||
final_location.parent.mkdir(parents=True, exist_ok=True)
|
||||
dl.make_lrc(final_location, synced_lyrics)
|
||||
continue
|
||||
if final_location.exists() and not args.overwrite:
|
||||
continue
|
||||
stream_url = dl.get_stream_url_song(webplayback)
|
||||
decryption_keys = dl.get_decryption_keys_song(stream_url, track_id)
|
||||
@@ -160,8 +171,10 @@ def main():
|
||||
dl.decrypt(encrypted_location, decrypted_location, decryption_keys)
|
||||
fixed_location = dl.get_fixed_location(track_id, '.m4a')
|
||||
dl.fixup_song(decrypted_location, fixed_location)
|
||||
dl.make_final(final_location, fixed_location, tags)
|
||||
dl.make_lrc(final_location, synced_lyrics)
|
||||
final_location.parent.mkdir(parents=True, exist_ok=True)
|
||||
dl.move_final(final_location, fixed_location, tags)
|
||||
if not args.no_lrc:
|
||||
dl.make_lrc(final_location, synced_lyrics)
|
||||
except KeyboardInterrupt:
|
||||
exit(1)
|
||||
except:
|
||||
|
||||
+14
-16
@@ -17,19 +17,20 @@ from mutagen.mp4 import MP4, MP4Cover
|
||||
|
||||
|
||||
class Gamdl:
|
||||
def __init__(self, wvd_location, cookies_location, disable_music_video_skip, prefer_hevc, temp_path, final_path, no_lrc, overwrite, skip_cleanup):
|
||||
def __init__(self, wvd_location, cookies_location, disable_music_video_skip, prefer_hevc, temp_path, final_path, lrc_only, overwrite, skip_cleanup):
|
||||
self.disable_music_video_skip = disable_music_video_skip
|
||||
self.prefer_hevc = prefer_hevc
|
||||
self.temp_path = Path(temp_path)
|
||||
self.final_path = Path(final_path)
|
||||
self.no_lrc = no_lrc
|
||||
self.overwrite = overwrite
|
||||
self.skip_cleanup = skip_cleanup
|
||||
wvd_location = glob.glob(wvd_location)
|
||||
if not wvd_location:
|
||||
raise Exception('.wvd file not found')
|
||||
self.cdm = Cdm.from_device(Device.load(wvd_location[0]))
|
||||
self.cdm_session = self.cdm.open()
|
||||
self.lrc_only = lrc_only
|
||||
if not self.lrc_only:
|
||||
wvd_location = glob.glob(wvd_location)
|
||||
if not wvd_location:
|
||||
raise Exception('.wvd file not found')
|
||||
self.cdm = Cdm.from_device(Device.load(wvd_location[0]))
|
||||
self.cdm_session = self.cdm.open()
|
||||
cookies = MozillaCookieJar(cookies_location)
|
||||
cookies.load(ignore_discard=True, ignore_expires=True)
|
||||
self.session = requests.Session()
|
||||
@@ -62,12 +63,14 @@ class Gamdl:
|
||||
download_queue = []
|
||||
product_id = url.split('/')[-1].split('i=')[-1].split('&')[0].split('?')[0]
|
||||
response = self.session.get(f'https://amp-api.music.apple.com/v1/catalog/{self.country}/?ids[songs]={product_id}&ids[albums]={product_id}&ids[playlists]={product_id}&ids[music-videos]={product_id}').json()['data'][0]
|
||||
if response['type'] in ('songs', 'music-videos') and 'playParams' in response['attributes']:
|
||||
if response['type'] == 'songs' and 'playParams' in response['attributes']:
|
||||
download_queue.append(response)
|
||||
if response['type'] == 'music-videos' and 'playParams' in response['attributes'] and not self.lrc_only:
|
||||
download_queue.append(response)
|
||||
if response['type'] == 'albums' or response['type'] == 'playlists':
|
||||
for track in response['relationships']['tracks']['data']:
|
||||
if 'playParams' in track['attributes']:
|
||||
if track['type'] == 'music-videos' and self.disable_music_video_skip:
|
||||
if track['type'] == 'music-videos' and self.disable_music_video_skip and not self.lrc_only:
|
||||
download_queue.append(track)
|
||||
if track['type'] == 'songs':
|
||||
download_queue.append(track)
|
||||
@@ -106,10 +109,6 @@ class Gamdl:
|
||||
return stream_url_video, stream_url_audio
|
||||
|
||||
|
||||
def check_exists(self, final_location):
|
||||
return Path(final_location).exists()
|
||||
|
||||
|
||||
def get_encrypted_location_video(self, track_id):
|
||||
return self.temp_path / f'{track_id}_encrypted_video.mp4'
|
||||
|
||||
@@ -383,13 +382,12 @@ class Gamdl:
|
||||
|
||||
|
||||
def make_lrc(self, final_location, synced_lyrics):
|
||||
if synced_lyrics and not self.no_lrc:
|
||||
if synced_lyrics:
|
||||
with open(final_location.with_suffix('.lrc'), 'w', encoding='utf8') as f:
|
||||
f.write(synced_lyrics)
|
||||
|
||||
|
||||
def make_final(self, final_location, fixed_location, tags):
|
||||
final_location.parent.mkdir(parents=True, exist_ok=True)
|
||||
def move_final(self, final_location, fixed_location, tags):
|
||||
shutil.move(fixed_location, final_location)
|
||||
file = MP4(final_location)
|
||||
file.update(tags)
|
||||
|
||||
Reference in New Issue
Block a user