from mutagen.easyid3 import EasyID3 from mutagen.id3 import ID3, TORY, TYER, TPUB, APIC, USLT, COMM from mutagen.mp4 import MP4, MP4Cover from mutagen.flac import Picture, FLAC import urllib.request from logzero import logger as log from spotdl.const import TAG_PRESET, M4A_TAG_PRESET def compare(music_file, metadata): """Check if the input music file title matches the expected title.""" already_tagged = False try: if music_file.endswith(".mp3"): audiofile = EasyID3(music_file) already_tagged = audiofile["title"][0] == metadata["name"] elif music_file.endswith(".m4a"): audiofile = MP4(music_file) already_tagged = audiofile["\xa9nam"][0] == metadata["name"] except (KeyError, TypeError): pass return already_tagged def embed(music_file, meta_tags): """ Embed metadata. """ embed = EmbedMetadata(music_file, meta_tags) if music_file.endswith(".m4a"): log.info("Applying metadata") return embed.as_m4a() elif music_file.endswith(".mp3"): log.info("Applying metadata") return embed.as_mp3() elif music_file.endswith(".flac"): log.info("Applying metadata") return embed.as_flac() else: log.warning("Cannot embed metadata into given output extension") return False class EmbedMetadata: def __init__(self, music_file, meta_tags): self.music_file = music_file self.meta_tags = meta_tags self.spotify_metadata = meta_tags["spotify_metadata"] self.provider = "spotify" if meta_tags["spotify_metadata"] else "youtube" def as_mp3(self): """ Embed metadata to MP3 files. """ music_file = self.music_file meta_tags = self.meta_tags # 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) self._embed_basic_metadata(audiofile, preset=TAG_PRESET) audiofile["media"] = meta_tags["type"] audiofile["author"] = meta_tags["artists"][0]["name"] audiofile["lyricist"] = meta_tags["artists"][0]["name"] audiofile["arranger"] = meta_tags["artists"][0]["name"] audiofile["performer"] = meta_tags["artists"][0]["name"] audiofile["website"] = meta_tags["external_urls"][self.provider] audiofile["length"] = str(meta_tags["duration"]) if meta_tags["publisher"]: audiofile["encodedby"] = meta_tags["publisher"] if meta_tags["external_ids"]["isrc"]: audiofile["isrc"] = meta_tags["external_ids"]["isrc"] 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["TORY"] = TORY(encoding=3, text=meta_tags["year"]) audiofile["TYER"] = TYER(encoding=3, text=meta_tags["year"]) if meta_tags["publisher"]: audiofile["TPUB"] = TPUB(encoding=3, text=meta_tags["publisher"]) audiofile["COMM"] = COMM(encoding=3, text=meta_tags["external_urls"][self.provider]) if meta_tags["lyrics"]: audiofile["USLT"] = USLT( encoding=3, desc=u"Lyrics", text=meta_tags["lyrics"] ) try: albumart = urllib.request.urlopen(meta_tags["album"]["images"][0]["url"]) audiofile["APIC"] = APIC( encoding=3, mime="image/jpeg", type=3, desc=u"Cover", data=albumart.read(), ) albumart.close() except IndexError: pass audiofile.save(v2_version=3) return True def as_m4a(self): """ Embed metadata to M4A files. """ music_file = self.music_file meta_tags = self.meta_tags audiofile = MP4(music_file) self._embed_basic_metadata(audiofile, preset=M4A_TAG_PRESET) audiofile[M4A_TAG_PRESET["year"]] = meta_tags["year"] audiofile[M4A_TAG_PRESET["comment"]] = meta_tags["external_urls"][self.provider] if meta_tags["lyrics"]: audiofile[M4A_TAG_PRESET["lyrics"]] = meta_tags["lyrics"] try: albumart = urllib.request.urlopen(meta_tags["album"]["images"][0]["url"]) audiofile[M4A_TAG_PRESET["albumart"]] = [ MP4Cover(albumart.read(), imageformat=MP4Cover.FORMAT_JPEG) ] albumart.close() except IndexError: pass audiofile.save() return True def as_flac(self): music_file = self.music_file meta_tags = self.meta_tags audiofile = FLAC(music_file) self._embed_basic_metadata(audiofile) audiofile["year"] = meta_tags["year"] audiofile["comment"] = meta_tags["external_urls"][self.provider] if meta_tags["lyrics"]: audiofile["lyrics"] = meta_tags["lyrics"] image = Picture() image.type = 3 image.desc = "Cover" image.mime = "image/jpeg" albumart = urllib.request.urlopen(meta_tags["album"]["images"][0]["url"]) image.data = albumart.read() albumart.close() audiofile.add_picture(image) audiofile.save() return True def _embed_basic_metadata(self, audiofile, preset=TAG_PRESET): meta_tags = self.meta_tags audiofile[preset["artist"]] = meta_tags["artists"][0]["name"] if meta_tags["album"]["artists"][0]["name"]: audiofile[preset["albumartist"]] = meta_tags["album"]["artists"][0]["name"] if meta_tags["album"]["name"]: audiofile[preset["album"]] = meta_tags["album"]["name"] audiofile[preset["title"]] = meta_tags["name"] audiofile[preset["date"]] = meta_tags["release_date"] audiofile[preset["originaldate"]] = meta_tags["release_date"] if meta_tags["genre"]: audiofile[preset["genre"]] = meta_tags["genre"] if meta_tags["copyright"]: audiofile[preset["copyright"]] = meta_tags["copyright"] if self.music_file.endswith(".flac"): audiofile[preset["discnumber"]] = str(meta_tags["disc_number"]) else: audiofile[preset["discnumber"]] = [(meta_tags["disc_number"], 0)] if self.music_file.endswith(".flac"): audiofile[preset["tracknumber"]] = str(meta_tags["track_number"]) else: if preset["tracknumber"] == TAG_PRESET["tracknumber"]: audiofile[preset["tracknumber"]] = "{}/{}".format( meta_tags["track_number"], meta_tags["total_tracks"] ) else: audiofile[preset["tracknumber"]] = [ (meta_tags["track_number"], meta_tags["total_tracks"]) ]