mirror of
				https://github.com/KevinMidboe/spotify-downloader.git
				synced 2025-10-29 18:00:15 +00:00 
			
		
		
		
	Fix conflicts
This commit is contained in:
		| @@ -7,12 +7,13 @@ | |||||||
|  |  | ||||||
| - Can also download a song by entering its artist and song name (in case if you don't have the Spotify's HTTP link for some song). | - Can also download a song by entering its artist and song name (in case if you don't have the Spotify's HTTP link for some song). | ||||||
|  |  | ||||||
| - Automatically fixes song's meta-tags which include: | - Automatically applies metadata to the downloaded song which include: | ||||||
|  |  | ||||||
|   - Title |   - Title | ||||||
|   - Artist |   - Artist | ||||||
|   - Album |   - Album | ||||||
|   - Album art |   - Album art | ||||||
|  |   - Lyrics (if found on http://lyrics.wikia.com) | ||||||
|   - Album artist |   - Album artist | ||||||
|   - Genre |   - Genre | ||||||
|   - Track number |   - Track number | ||||||
|   | |||||||
| @@ -13,7 +13,7 @@ def log_leveller(log_level_str): | |||||||
|     return loggin_level |     return loggin_level | ||||||
|  |  | ||||||
|  |  | ||||||
| def get_arguments(to_group=False, raw_args=None): | def get_arguments(to_group=True, raw_args=None): | ||||||
|     parser = argparse.ArgumentParser( |     parser = argparse.ArgumentParser( | ||||||
|         description='Download and convert songs from Spotify, Youtube etc.', |         description='Download and convert songs from Spotify, Youtube etc.', | ||||||
|         formatter_class=argparse.ArgumentDefaultsHelpFormatter) |         formatter_class=argparse.ArgumentDefaultsHelpFormatter) | ||||||
| @@ -56,6 +56,10 @@ def get_arguments(to_group=False, raw_args=None): | |||||||
|     parser.add_argument( |     parser.add_argument( | ||||||
|         '-o', '--output-ext', default='.mp3', |         '-o', '--output-ext', default='.mp3', | ||||||
|         help='prefered output extension .mp3 or .m4a (AAC)') |         help='prefered output extension .mp3 or .m4a (AAC)') | ||||||
|  |     parser.add_argument( | ||||||
|  |         '-dm', '--download-only-metadata', default=False, | ||||||
|  |         help='download songs for which metadata is found', | ||||||
|  |         action='store_true') | ||||||
|     parser.add_argument( |     parser.add_argument( | ||||||
|         '-d', '--dry-run', default=False, |         '-d', '--dry-run', default=False, | ||||||
|         help='Show only track title and YouTube URL', |         help='Show only track title and YouTube URL', | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| from mutagen.easyid3 import EasyID3 | from mutagen.easyid3 import EasyID3 | ||||||
| from mutagen.id3 import ID3, APIC | from mutagen.id3 import ID3, TORY, TYER, TPUB, APIC, USLT, COMM | ||||||
| from mutagen.mp4 import MP4, MP4Cover | from mutagen.mp4 import MP4, MP4Cover | ||||||
| from core.const import log | from core.const import log | ||||||
|  |  | ||||||
| @@ -45,6 +45,9 @@ class EmbedMetadata: | |||||||
|         music_file = self.music_file |         music_file = self.music_file | ||||||
|         meta_tags = self.meta_tags |         meta_tags = self.meta_tags | ||||||
|         # EasyID3 is fun to use ;) |         # EasyID3 is fun to use ;) | ||||||
|  |         # For supported easyid3 tags: | ||||||
|  |         # https://github.com/quodlibet/mutagen/blob/master/mutagen/easyid3.py | ||||||
|  |         # Check out somewhere at end of above linked file | ||||||
|         audiofile = EasyID3(music_file) |         audiofile = EasyID3(music_file) | ||||||
|         audiofile['artist'] = meta_tags['artists'][0]['name'] |         audiofile['artist'] = meta_tags['artists'][0]['name'] | ||||||
|         audiofile['albumartist'] = meta_tags['artists'][0]['name'] |         audiofile['albumartist'] = meta_tags['artists'][0]['name'] | ||||||
| @@ -61,7 +64,7 @@ class EmbedMetadata: | |||||||
|         audiofile['arranger'] = meta_tags['artists'][0]['name'] |         audiofile['arranger'] = meta_tags['artists'][0]['name'] | ||||||
|         audiofile['performer'] = meta_tags['artists'][0]['name'] |         audiofile['performer'] = meta_tags['artists'][0]['name'] | ||||||
|         audiofile['website'] = meta_tags['external_urls']['spotify'] |         audiofile['website'] = meta_tags['external_urls']['spotify'] | ||||||
|         audiofile['length'] = str(meta_tags['duration_ms'] / 1000) |         audiofile['length'] = str(meta_tags['duration_ms'] / 1000.0) | ||||||
|         if meta_tags['publisher']: |         if meta_tags['publisher']: | ||||||
|             audiofile['encodedby'] = meta_tags['publisher'] |             audiofile['encodedby'] = meta_tags['publisher'] | ||||||
|         if meta_tags['genre']: |         if meta_tags['genre']: | ||||||
| @@ -71,14 +74,26 @@ class EmbedMetadata: | |||||||
|         if meta_tags['external_ids']['isrc']: |         if meta_tags['external_ids']['isrc']: | ||||||
|             audiofile['isrc'] = meta_tags['external_ids']['isrc'] |             audiofile['isrc'] = meta_tags['external_ids']['isrc'] | ||||||
|         audiofile.save(v2_version=3) |         audiofile.save(v2_version=3) | ||||||
|  |  | ||||||
|  |         # For supported id3 tags: | ||||||
|  |         # https://github.com/quodlibet/mutagen/blob/master/mutagen/id3/_frames.py | ||||||
|  |         # Each class represents an id3 tag | ||||||
|         audiofile = ID3(music_file) |         audiofile = ID3(music_file) | ||||||
|  |         year, *_ = meta_tags['release_date'].split('-') | ||||||
|  |         audiofile['TORY'] = TORY(encoding=3, text=year) | ||||||
|  |         audiofile['TYER'] = TYER(encoding=3, text=year) | ||||||
|  |         audiofile['TPUB'] = TPUB(encoding=3, text=meta_tags['publisher']) | ||||||
|  |         audiofile['COMM'] = COMM(encoding=3, text=meta_tags['external_urls']['spotify']) | ||||||
|  |         if meta_tags['lyrics']: | ||||||
|  |             audiofile['USLT'] = USLT(encoding=3, desc=u'Lyrics', text=meta_tags['lyrics']) | ||||||
|         try: |         try: | ||||||
|             albumart = urllib.request.urlopen(meta_tags['album']['images'][0]['url']) |             albumart = urllib.request.urlopen(meta_tags['album']['images'][0]['url']) | ||||||
|             audiofile["APIC"] = APIC(encoding=3, mime='image/jpeg', type=3, |             audiofile['APIC'] = APIC(encoding=3, mime='image/jpeg', type=3, | ||||||
|                                      desc=u'Cover', data=albumart.read()) |                                      desc=u'Cover', data=albumart.read()) | ||||||
|             albumart.close() |             albumart.close() | ||||||
|         except IndexError: |         except IndexError: | ||||||
|             pass |             pass | ||||||
|  |  | ||||||
|         audiofile.save(v2_version=3) |         audiofile.save(v2_version=3) | ||||||
|         return True |         return True | ||||||
|  |  | ||||||
| @@ -88,22 +103,24 @@ class EmbedMetadata: | |||||||
|         meta_tags = self.meta_tags |         meta_tags = self.meta_tags | ||||||
|         # Apple has specific tags - see mutagen docs - |         # Apple has specific tags - see mutagen docs - | ||||||
|         # http://mutagen.readthedocs.io/en/latest/api/mp4.html |         # http://mutagen.readthedocs.io/en/latest/api/mp4.html | ||||||
|         tags = {'album': '\xa9alb', |         tags = { 'album'        : '\xa9alb', | ||||||
|                 'artist': '\xa9ART', |                  'artist'       : '\xa9ART', | ||||||
|                 'date': '\xa9day', |                  'date'         : '\xa9day', | ||||||
|                 'title': '\xa9nam', |                  'title'        : '\xa9nam', | ||||||
|                 'originaldate': 'purd', |                  'year'         : '\xa9day', | ||||||
|                 'comment': '\xa9cmt', |                  'originaldate' : 'purd', | ||||||
|                 'group': '\xa9grp', |                  'comment'      : '\xa9cmt', | ||||||
|                 'writer': '\xa9wrt', |                  'group'        : '\xa9grp', | ||||||
|                 'genre': '\xa9gen', |                  'writer'       : '\xa9wrt', | ||||||
|                 'tracknumber': 'trkn', |                  'genre'        : '\xa9gen', | ||||||
|                 'albumartist': 'aART', |                  'tracknumber'  : 'trkn', | ||||||
|                 'disknumber': 'disk', |                  'albumartist'  : 'aART', | ||||||
|                 'cpil': 'cpil', |                  'disknumber'   : 'disk', | ||||||
|                 'albumart': 'covr', |                  'cpil'         : 'cpil', | ||||||
|                 'copyright': 'cprt', |                  'albumart'     : 'covr', | ||||||
|                 'tempo': 'tmpo'} |                  'copyright'    : 'cprt', | ||||||
|  |                  'tempo'        : 'tmpo', | ||||||
|  |                  'lyrics'       : '\xa9lyr' } | ||||||
|  |  | ||||||
|         audiofile = MP4(music_file) |         audiofile = MP4(music_file) | ||||||
|         audiofile[tags['artist']] = meta_tags['artists'][0]['name'] |         audiofile[tags['artist']] = meta_tags['artists'][0]['name'] | ||||||
| @@ -114,11 +131,16 @@ class EmbedMetadata: | |||||||
|                                            meta_tags['total_tracks'])] |                                            meta_tags['total_tracks'])] | ||||||
|         audiofile[tags['disknumber']] = [(meta_tags['disc_number'], 0)] |         audiofile[tags['disknumber']] = [(meta_tags['disc_number'], 0)] | ||||||
|         audiofile[tags['date']] = meta_tags['release_date'] |         audiofile[tags['date']] = meta_tags['release_date'] | ||||||
|  |         year, *_ = meta_tags['release_date'].split('-') | ||||||
|  |         audiofile[tags['year']] = year | ||||||
|         audiofile[tags['originaldate']] = meta_tags['release_date'] |         audiofile[tags['originaldate']] = meta_tags['release_date'] | ||||||
|  |         audiofile[tags['comment']] = meta_tags['external_urls']['spotify'] | ||||||
|         if meta_tags['genre']: |         if meta_tags['genre']: | ||||||
|             audiofile[tags['genre']] = meta_tags['genre'] |             audiofile[tags['genre']] = meta_tags['genre'] | ||||||
|         if meta_tags['copyright']: |         if meta_tags['copyright']: | ||||||
|             audiofile[tags['copyright']] = meta_tags['copyright'] |             audiofile[tags['copyright']] = meta_tags['copyright'] | ||||||
|  |         if meta_tags['lyrics']: | ||||||
|  |             audiofile[tags['lyrics']] = meta_tags['lyrics'] | ||||||
|         try: |         try: | ||||||
|             albumart = urllib.request.urlopen(meta_tags['album']['images'][0]['url']) |             albumart = urllib.request.urlopen(meta_tags['album']['images'][0]['url']) | ||||||
|             audiofile[tags['albumart']] = [MP4Cover( |             audiofile[tags['albumart']] = [MP4Cover( | ||||||
|   | |||||||
| @@ -1,11 +1,13 @@ | |||||||
| import spotipy | import spotipy | ||||||
| import spotipy.oauth2 as oauth2 | import spotipy.oauth2 as oauth2 | ||||||
|  | import lyricwikia | ||||||
| from titlecase import titlecase | from titlecase import titlecase | ||||||
| import pprint |  | ||||||
|  |  | ||||||
| from core import internals | from core import internals | ||||||
| from core.const import log | from core.const import log | ||||||
|  |  | ||||||
|  | import pprint | ||||||
|  |  | ||||||
|  |  | ||||||
| def generate_token(): | def generate_token(): | ||||||
|     """ Generate the token. Please respect these credentials :) """ |     """ Generate the token. Please respect these credentials :) """ | ||||||
| @@ -52,6 +54,19 @@ def generate_metadata(raw_song): | |||||||
|     meta_tags[u'publisher'] = album['label'] |     meta_tags[u'publisher'] = album['label'] | ||||||
|     meta_tags[u'total_tracks'] = album['tracks']['total'] |     meta_tags[u'total_tracks'] = album['tracks']['total'] | ||||||
|  |  | ||||||
|  |     log.debug('Fetching lyrics') | ||||||
|  |  | ||||||
|  |     try: | ||||||
|  |         meta_tags['lyrics'] = lyricwikia.get_lyrics( | ||||||
|  |                         meta_tags['artists'][0]['name'], | ||||||
|  |                         meta_tags['name']) | ||||||
|  |     except lyricwikia.LyricsNotFound: | ||||||
|  |         meta_tags['lyrics'] = None | ||||||
|  |  | ||||||
|  |     # remove unused clutter when debug meta_tags | ||||||
|  |     del meta_tags['available_markets'] | ||||||
|  |     del meta_tags['album']['available_markets'] | ||||||
|  |  | ||||||
|     log.debug(pprint.pformat(meta_tags)) |     log.debug(pprint.pformat(meta_tags)) | ||||||
|     return meta_tags |     return meta_tags | ||||||
|  |  | ||||||
|   | |||||||
| @@ -71,15 +71,15 @@ def generate_youtube_url(raw_song, meta_tags, tries_remaining=5): | |||||||
|     else: |     else: | ||||||
|         song = internals.generate_songname(meta_tags) |         song = internals.generate_songname(meta_tags) | ||||||
|         query['q'] = song |         query['q'] = song | ||||||
|     log.debug('Query: {0}'.format(query)) |     log.debug('query: {0}'.format(query)) | ||||||
|  |  | ||||||
|     data = pafy.call_gdata('search', query) |     data = pafy.call_gdata('search', query) | ||||||
|     query2 = {'part': 'contentDetails,snippet,statistics', |     query_results = {'part': 'contentDetails,snippet,statistics', | ||||||
|               'maxResults': 50, |               'maxResults': 50, | ||||||
|               'id': ','.join(i['id']['videoId'] for i in data['items'])} |               'id': ','.join(i['id']['videoId'] for i in data['items'])} | ||||||
|     log.debug('Query2: {0}'.format(query2)) |     log.debug('query_results: {0}'.format(query_results)) | ||||||
|  |  | ||||||
|     vdata = pafy.call_gdata('videos', query2) |     vdata = pafy.call_gdata('videos', query_results) | ||||||
|  |  | ||||||
|     videos = [] |     videos = [] | ||||||
|     for x in vdata['items']: |     for x in vdata['items']: | ||||||
| @@ -94,8 +94,6 @@ def generate_youtube_url(raw_song, meta_tags, tries_remaining=5): | |||||||
|     if not videos: |     if not videos: | ||||||
|         return None |         return None | ||||||
|  |  | ||||||
|     log.debug(pprint.pformat(videos)) |  | ||||||
|  |  | ||||||
|     if const.args.manual: |     if const.args.manual: | ||||||
|         log.info(song) |         log.info(song) | ||||||
|         log.info('0. Skip downloading this song.\n') |         log.info('0. Skip downloading this song.\n') | ||||||
|   | |||||||
| @@ -6,3 +6,4 @@ mutagen >= 1.37 | |||||||
| unicode-slugify >= 0.1.3 | unicode-slugify >= 0.1.3 | ||||||
| titlecase >= 0.10.0 | titlecase >= 0.10.0 | ||||||
| logzero >= 1.3.1 | logzero >= 1.3.1 | ||||||
|  | lyricwikia >= 0.1.8 | ||||||
|   | |||||||
							
								
								
									
										11
									
								
								spotdl.py
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								spotdl.py
									
									
									
									
									
								
							| @@ -137,7 +137,12 @@ def grab_single(raw_song, number=None): | |||||||
|         meta_tags = spotify_tools.generate_metadata(raw_song) |         meta_tags = spotify_tools.generate_metadata(raw_song) | ||||||
|         content = youtube_tools.go_pafy(raw_song, meta_tags) |         content = youtube_tools.go_pafy(raw_song, meta_tags) | ||||||
|  |  | ||||||
|     if not content: |     if const.args.download_only_metadata: | ||||||
|  |         if meta_tags is None: | ||||||
|  |             log.info('Found No metadata. Skipping the download') | ||||||
|  |             return | ||||||
|  |  | ||||||
|  |     if content is None: | ||||||
|         log.debug('Found no matching video') |         log.debug('Found no matching video') | ||||||
|         return |         return | ||||||
|  |  | ||||||
| @@ -215,8 +220,8 @@ if __name__ == '__main__': | |||||||
|         elif const.args.username: |         elif const.args.username: | ||||||
|             spotify_tools.feed_playlist(username=const.args.username) |             spotify_tools.feed_playlist(username=const.args.username) | ||||||
|  |  | ||||||
|         # Actually we don't necessarily need this, but yeah... |         # actually we don't necessarily need this, but yeah... | ||||||
|         # Explicit is better than implicit! |         # explicit is better than implicit! | ||||||
|         sys.exit(0) |         sys.exit(0) | ||||||
|  |  | ||||||
|     except KeyboardInterrupt as e: |     except KeyboardInterrupt as e: | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user