mirror of
				https://github.com/KevinMidboe/spotify-downloader.git
				synced 2025-10-29 18:00:15 +00:00 
			
		
		
		
	Write lyrics to track metadata (#194)
* TODO: Don't throw traceback if no lyrics found * Add more metadata fields * Refactor debug logging * Fix traceback when lyrics not found * It already vomits metadata :3 * Bump lyricwikia >= 0.1.8
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 | ||||||
|   | |||||||
| @@ -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.logger import log | from core.logger import log | ||||||
|  |  | ||||||
| @@ -43,6 +43,9 @@ def embed(music_file, meta_tags): | |||||||
| def embed_mp3(music_file, meta_tags): | def embed_mp3(music_file, meta_tags): | ||||||
|     """ Embed metadata to MP3 files. """ |     """ Embed metadata to MP3 files. """ | ||||||
|     # 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'] | ||||||
| @@ -59,7 +62,8 @@ def embed_mp3(music_file, meta_tags): | |||||||
|     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']: | ||||||
| @@ -69,14 +73,27 @@ def embed_mp3(music_file, meta_tags): | |||||||
|     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) | ||||||
|  |     print(meta_tags['release_date'].split('-')[0]) | ||||||
|  |     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 | ||||||
|  |  | ||||||
| @@ -85,22 +102,24 @@ def embed_m4a(music_file, meta_tags): | |||||||
|     """ Embed metadata to M4A files. """ |     """ Embed metadata to M4A files. """ | ||||||
|     # 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'] | ||||||
| @@ -111,11 +130,16 @@ def embed_m4a(music_file, meta_tags): | |||||||
|                                        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( | ||||||
|   | |||||||
| @@ -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 | ||||||
|   | |||||||
							
								
								
									
										28
									
								
								spotdl.py
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								spotdl.py
									
									
									
									
									
								
							| @@ -9,6 +9,7 @@ from titlecase import titlecase | |||||||
| from slugify import slugify | from slugify import slugify | ||||||
| import spotipy | import spotipy | ||||||
| import pafy | import pafy | ||||||
|  | import lyricwikia | ||||||
| import urllib.request | import urllib.request | ||||||
| import os | import os | ||||||
| import sys | import sys | ||||||
| @@ -57,6 +58,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 | ||||||
|  |  | ||||||
| @@ -99,15 +113,15 @@ def generate_youtube_url(raw_song, meta_tags, tries_remaining=5): | |||||||
|     else: |     else: | ||||||
|         song = generate_songname(meta_tags) |         song = 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']: | ||||||
| @@ -122,8 +136,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 args.manual: |     if args.manual: | ||||||
|         log.info(song) |         log.info(song) | ||||||
|         log.info('0. Skip downloading this song.\n') |         log.info('0. Skip downloading this song.\n') | ||||||
| @@ -491,8 +503,8 @@ if __name__ == '__main__': | |||||||
|         elif args.username: |         elif args.username: | ||||||
|             feed_playlist(username=args.username) |             feed_playlist(username=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