From 6587402bd350a1390849fad45315eda6107eb107 Mon Sep 17 00:00:00 2001 From: markhenry Date: Tue, 13 Aug 2024 18:55:13 -0700 Subject: [PATCH 1/4] add -q to options doc also regularize docopt per claude's recommendations --- scdl/scdl.py | 87 +++++++++++++++++++++++++++------------------------- 1 file changed, 45 insertions(+), 42 deletions(-) diff --git a/scdl/scdl.py b/scdl/scdl.py index 882ee359..0fddf3cc 100755 --- a/scdl/scdl.py +++ b/scdl/scdl.py @@ -4,52 +4,55 @@ """scdl allows you to download music from Soundcloud Usage: - scdl -l [-a | -f | -C | -t | -p][-c][-n ][-o ]\ -[--hidewarnings][--debug | --error][--path ][--addtofile][--addtimestamp] -[--onlymp3][--hide-progress][--min-size ][--max-size ][--remove] -[--no-playlist-folder][--download-archive ][--extract-artist][--flac] - scdl me (-s | -a | -f | -t | -p | -m)[-c][-o ]\ -[--hidewarnings][--debug | --error][--path ][--addtofile][--addtimestamp] -[--onlymp3][--hide-progress][--min-size ][--max-size ][--remove] -[--no-playlist-folder][--download-archive ][--extract-artist][--flac] + scdl (-l | -q ) [options] + scdl me (-s | -a | -f | -t | -p | -m) [options] scdl -h | --help scdl --version - Options: - -h --help Show this screen - --version Show version - me Use the user profile from the auth_token - -l [url] URL can be track/playlist/user - -n [maxtracks] Download the n last tracks of a playlist according to the creation date - -s Download the stream of a user (token needed) - -a Download all tracks of user (including reposts) - -t Download all uploads of a user (no reposts) - -f Download all favorites of a user - -C Download all commented by a user - -p Download all playlists of a user - -m Download all liked and owned playlists of user - -c Continue if a downloaded file already exists - -o [offset] Begin with a custom offset - --addtimestamp Add track creation timestamp to filename, - which allows for chronological sorting - --addtofile Add artist to filename if missing - --debug Set log level to DEBUG - --download-archive [file] Keep track of track IDs in an archive file, - and skip already-downloaded files - --error Set log level to ERROR - --extract-artist Set artist tag from title instead of username - --hide-progress Hide the wget progress bar - --hidewarnings Hide Warnings. (use with precaution) - --max-size [max-size] Skip tracks larger than size (k/m/g) - --min-size [min-size] Skip tracks smaller than size (k/m/g) - --no-playlist-folder Download playlist tracks into main directory, - instead of making a playlist subfolder - --onlymp3 Download only the streamable mp3 file, - even if track has a Downloadable file - --path [path] Use a custom path for downloaded files - --remove Remove any files not downloaded from execution - --flac Convert original files to .flac + -h --help Show this screen + --version Show version + -l URL can be track/playlist/user + -q Search for a track/playlist/user and use the first result + -s Download the stream of a user + -a Download all tracks of user (including reposts) + -t Download all uploads of a user (no reposts) + -f Download all favorites of a user + -C Download all commented by a user + -p Download all playlists of a user + -m Download all likes and owned playlists of user + -c Continue if a downloaded file already exists + -n Download the n last tracks of a playlist [default: all] + -o Begin with a custom offset + --addtimestamp Add track creation timestamp to filename + --addtofile Add artist to filename if missing + --debug Set log level to DEBUG + --download-archive Keep track of track IDs in an archive file, and skip already-downloaded files + --error Set log level to ERROR + --extract-artist Set artist tag from title instead of username + --hide-progress Hide the wget progress bar + --hidewarnings Hide Warnings (use with precaution) + --max-size Skip tracks larger than size (k/m/g) + --min-size Skip tracks smaller than size (k/m/g) + --no-playlist-folder Download playlist tracks into main directory + --onlymp3 Download only the streamable mp3 file + --path Use a custom path for downloaded files + --remove Remove any files not downloaded from execution + --flac Convert original files to .flac + --original-art Download original cover art + --original-name Do not change name of original file downloads + --original-metadata Do not change metadata of original file downloads + --no-original Do not download original file + --only-original Only download songs with original file available + --name-format Specify the downloaded file name format + --playlist-name-format Specify the playlist name format + --client-id Specify the client_id to use + --auth-token Specify the auth token to use + --overwrite Overwrite file if it already exists + --strict-playlist Abort playlist downloading if one track fails + --add-description Adds the description to a separate txt file + --no-playlist Skip downloading playlists + --opus Prefer downloading opus streams over mp3 streams """ import logging From fa7a8aabdf9dfe9cf0031975d8b92114d5d7febb Mon Sep 17 00:00:00 2001 From: markhenry Date: Tue, 13 Aug 2024 19:22:47 -0700 Subject: [PATCH 2/4] non-working implementation of soundcloud search --- scdl/scdl.py | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/scdl/scdl.py b/scdl/scdl.py index 0fddf3cc..f84403ef 100755 --- a/scdl/scdl.py +++ b/scdl/scdl.py @@ -107,7 +107,8 @@ 'trackinfo': ('https://api-v2.soundcloud.com/tracks/{0}'), 'original_download' : ("https://api-v2.soundcloud.com/tracks/{0}/download"), 'user': ('https://api-v2.soundcloud.com/users/{0}'), - 'me': ('https://api-v2.soundcloud.com/me?oauth_token={0}') + 'me': ('https://api-v2.soundcloud.com/me?oauth_token={0}'), + 'search': ('https://api-v2.soundcloud.com/search/queries?q={0}&client_id={1}&limit=1&offset=0') } client = client.Client() @@ -183,6 +184,15 @@ def main(): if arguments['-l']: parse_url(arguments['-l']) + elif arguments['-q']: + search_query = arguments['-q'] + url = search_soundcloud(search_query) + if url: + parse_url(url) + else: + logger.error("Search failed. Exiting...") + sys.exit(1) + elif arguments['me']: if arguments['-f']: download(who_am_i(), 'favorites', 'likes') @@ -200,6 +210,34 @@ def main(): if arguments['--remove']: remove_files() +def search_soundcloud(query: str, client_id=CLIENT_ID): + """ + Search SoundCloud and return the URL of the first result + """ + try: + search_url = url['search'].format(query, client_id) + r = requests.get(search_url) + logger.debug(r.url) + if r.status_code == 403: + return search_soundcloud(query, ALT_CLIENT_ID) + + results = r.json() + logger.debug(results) + if results['collection']: + item = results['collection'][0] + if item['kind'] in ['track', 'playlist', 'user']: + return item['permalink_url'] + logger.warning(f"Unexpected search result type: {item['kind']}") + logger.error(f"No results found for query: {query}") + except Exception: + if client_id == ALT_CLIENT_ID: + logger.error('Failed to search...') + return None + logger.error('Error searching, retrying...') + time.sleep(5) + return search_soundcloud(query, ALT_CLIENT_ID) + return None + def get_config(): """ From e5dbdba5ef8dc753a6dbcf4a322e30359cc95e40 Mon Sep 17 00:00:00 2001 From: markhenry Date: Tue, 13 Aug 2024 19:43:02 -0700 Subject: [PATCH 3/4] checkpoint --- scdl/scdl.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/scdl/scdl.py b/scdl/scdl.py index f84403ef..cfd197a3 100755 --- a/scdl/scdl.py +++ b/scdl/scdl.py @@ -108,7 +108,7 @@ 'original_download' : ("https://api-v2.soundcloud.com/tracks/{0}/download"), 'user': ('https://api-v2.soundcloud.com/users/{0}'), 'me': ('https://api-v2.soundcloud.com/me?oauth_token={0}'), - 'search': ('https://api-v2.soundcloud.com/search/queries?q={0}&client_id={1}&limit=1&offset=0') + 'search': ('https://api-v2.soundcloud.com/search/queries?q={0}&limit=1&offset=0') } client = client.Client() @@ -188,6 +188,7 @@ def main(): search_query = arguments['-q'] url = search_soundcloud(search_query) if url: + logger.info(url) parse_url(url) else: logger.error("Search failed. Exiting...") @@ -215,9 +216,10 @@ def search_soundcloud(query: str, client_id=CLIENT_ID): Search SoundCloud and return the URL of the first result """ try: - search_url = url['search'].format(query, client_id) - r = requests.get(search_url) + search_url = url['search'].format(query) + r = requests.get(search_url, params={'client_id': client_id}) logger.debug(r.url) + logger.debug(r.status_code) if r.status_code == 403: return search_soundcloud(query, ALT_CLIENT_ID) @@ -229,10 +231,11 @@ def search_soundcloud(query: str, client_id=CLIENT_ID): return item['permalink_url'] logger.warning(f"Unexpected search result type: {item['kind']}") logger.error(f"No results found for query: {query}") - except Exception: + except Exception as e: if client_id == ALT_CLIENT_ID: logger.error('Failed to search...') return None + logger.error(e) logger.error('Error searching, retrying...') time.sleep(5) return search_soundcloud(query, ALT_CLIENT_ID) From 5508ac5efa991929fa3050d949e6be5d85dafb30 Mon Sep 17 00:00:00 2001 From: markhenry Date: Tue, 13 Aug 2024 20:21:45 -0700 Subject: [PATCH 4/4] fix search endpoint --- scdl/scdl.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scdl/scdl.py b/scdl/scdl.py index cfd197a3..8e9dc07a 100755 --- a/scdl/scdl.py +++ b/scdl/scdl.py @@ -495,7 +495,7 @@ def download_original_file(track, title): if r.status_code == 401: logger.info('The original file has no download left.') return None - + if r.status_code == 404: logger.info('Could not get name from stream - using basic name') return None @@ -535,7 +535,7 @@ def download_original_file(track, title): newfilename = filename[:-4] + ".flac" new = shlex.quote(newfilename) old = shlex.quote(filename) - + commands = ['ffmpeg', '-i', old, new, '-loglevel', 'fatal'] logger.debug("Commands: {}".format(commands)) subprocess.call(commands)