def embed_metadata(music_file, cover_image, song): """Add metadata to extracted OGG files. For details on the METADATA_BLOCK_PICTURE struct format, see https://xiph.org/flac/format.html#metadata_block_picture """ music_file.seek(0) audio = OggVorbis(music_file) audio["title"] = song.title audio["artist"] = song.artist audio["album"] = song.album_name audio["tracknumber"] = str(song.track_number) audio["tracktotal"] = str(song.track_total) if song.genre is not None: audio["genre"] = song.genre picture = Picture() # PIL does not allow for direct saving to bytes cover_image_file = io.BytesIO() cover_image.save(cover_image_file, format="png") picture.data = cover_image_file.getvalue() picture.type = 3 # Cover (front) picture.mime = "image/png" picture.width = cover_image.width picture.height = cover_image.height # PIL does not give depth, so we assert then hardcode assert cover_image.mode == "RGBA" picture.depth = 32 audio["metadata_block_picture"] = [ base64.b64encode(picture.write()).decode("ascii") ] audio.save(music_file)
def set_image(self, image): """Replaces all embedded images by the passed image""" with translate_errors(): tag = FLAC(self["~filename"]) try: data = image.read() except EnvironmentError as e: raise AudioFileError(e) pic = Picture() pic.data = data pic.type = APICType.COVER_FRONT pic.mime = image.mime_type pic.width = image.width pic.height = image.height pic.depth = image.color_depth tag.add_picture(pic) with translate_errors(): tag.save() # clear vcomment tags super(FLACFile, self).clear_images() self.has_images = True
def set_image(self, image): """Replaces all embedded images by the passed image""" with translate_errors(): audio = self.MutagenType(self["~filename"]) try: data = image.read() except EnvironmentError as e: raise AudioFileError(e) pic = Picture() pic.data = data pic.type = APICType.COVER_FRONT pic.mime = image.mime_type pic.width = image.width pic.height = image.height pic.depth = image.color_depth audio.pop("coverart", None) audio.pop("coverartmime", None) audio["metadata_block_picture"] = base64.b64encode( pic.write()).decode("ascii") with translate_errors(): audio.save() self.has_images = True
def get_cover_art_from_song(song_path): """ Takes in path pointing to an audio file of arbitrary format and finds the cover art, if any, and returns it as a File. """ file_type = mutagen.File(song_path) pic = None # TODO: Look to add support for more tag formats. if isinstance(file_type.tags, mutagen.id3.ID3): apic_frames = [file_type.tags[t] for t in file_type.tags.keys() if t.startswith("APIC")] cover_frame = apic_frames[0] if len(apic_frames) > 1: cover_frame = [f for f in apic_frames if f.type == mutagen.id3.PictureType.COVER_FRONT][0] downscaled_data = __downscale_cover_art(cover_frame.data) pic = Picture() pic.data = downscaled_data pic.type = cover_frame.type pic.mime = cover_frame.mime pic.width = THUMBNAIL_DIMENSION pic.height = THUMBNAIL_DIMENSION pic.depth = 16 # color depth return pic
def download_and_fix_ogg(ogg, audio_metadata, cover_art_file): global DRY_RUN if DRY_RUN: print "This is a dry run. So pretending to download the ogg..." return "/tmp/ogg" print "Now downloading the ogg in order to set the metadata in it..." if not LIVE and len(sys.argv) >= 6 and os.path.exists(sys.argv[5]): ogg_local_fn = sys.argv[5] print "(using presupplied file %s)" % ogg_local_fn else: f, metadata = client.get_file_and_metadata(ogg) ogg_local_fn = fetch_file(f, metadata) print "Successfully downloaded (to %s): now editing metadata..." % ogg_local_fn audio = OggVorbis(ogg_local_fn) for k in audio_metadata.keys(): audio[k] = audio_metadata[k] # add cover art im = Image.open(cover_art_file) w, h = im.size p = Picture() imdata = open(cover_art_file, 'rb').read() p.data = imdata p.type = 3 p.desc = '' p.mime = 'image/jpeg' p.width = w p.height = h p.depth = 24 dt = p.write() enc = base64.b64encode(dt).decode('ascii') audio['metadata_block_picture'] = [enc] audio.save() print "Successfully updated metadata." return ogg_local_fn
def _addCoverToFile(self, album): if self.fileType == 'FLAC': if not self.hasCover or ( self.audioTag.pictures[0].height != 1000 and self.audioTag.pictures[0].width != 1000): if self.hasCover: self.audioTag.clear_pictures() # Build the file path by concatenating folder in the file path path = '' for folder in self.pathList: path += '{}/'.format(folder) path += album.coverName with open(path, "rb") as img: data = img.read() # Open physical image im = PIL.Image.open(path) width, height = im.size # Create picture and set its internals picture = Picture() picture.data = data picture.type = 3 # COVER_FRONT picture.desc = path.rsplit('/', 1)[-1] # Add picture name as a description picture.mime = mimetypes.guess_type(path)[0] picture.width = width picture.height = height picture.depth = mode_to_bpp[im.mode] # Save into file's audio tag self.audioTag.add_picture(picture) self.audioTag.save() else: # TODO for APIC frame pass
def download_and_fix_ogg(ogg, audio_metadata, cover_art_file): global DRY_RUN if DRY_RUN: print "This is a dry run. So pretending to download the ogg..." return "/tmp/ogg" print "Now downloading the ogg in order to set the metadata in it..." if not LIVE and len(sys.argv) >= 6 and os.path.exists(sys.argv[5]): ogg_local_fn = sys.argv[5] print "(using presupplied file %s)" % ogg_local_fn else: f, metadata = client.get_file_and_metadata(ogg) ogg_local_fn = fetch_file(f, metadata) print "Successfully downloaded (to %s): now editing metadata..." % ogg_local_fn audio = OggVorbis(ogg_local_fn) for k in audio_metadata.keys(): audio[k] = audio_metadata[k] # add cover art im=Image.open(cover_art_file) w,h=im.size p=Picture() imdata=open(cover_art_file,'rb').read() p.data=imdata p.type=3 p.desc='' p.mime='image/jpeg'; p.width=w; p.height=h p.depth=24 dt=p.write(); enc=base64.b64encode(dt).decode('ascii'); audio['metadata_block_picture']=[enc]; audio.save() print "Successfully updated metadata." return ogg_local_fn
def set_image(self, image): """Replaces all embedded images by the passed image""" with translate_errors(): tag = FLAC(self["~filename"]) try: data = image.read() except EnvironmentError as e: raise AudioFileError(e) pic = Picture() pic.data = data pic.type = APICType.COVER_FRONT pic.mime = image.mime_type pic.width = image.width pic.height = image.height pic.depth = image.color_depth tag.add_picture(pic) with translate_errors(): tag.save() # clear vcomment tags super().clear_images() self.has_images = True
def _picture_f2m(flackup_picture): """Create a Mutagen Picture from a Flackup Picture.""" picture = MutagenPicture() picture.type = flackup_picture.type picture.mime = flackup_picture.mime picture.width = flackup_picture.width picture.height = flackup_picture.height picture.depth = flackup_picture.depth picture.data = flackup_picture.data return picture
def buttonAddPictureClicked(self): self.logger("buttonAddPictureClicked") filename = QtGui.QFileDialog.getOpenFileName(self, "Image File") reader = QtGui.QImageReader(filename) rformat = reader.format() image = reader.read() self.coverArtPixmap = QtGui.QPixmap.fromImage(image) self.logger( 'width = %d, height = %d' % (self.coverArtPixmap.width(), self.coverArtPixmap.height())) self.labelPicture.setPixmap( self.coverArtPixmap.scaled(self.labelPicture.width(), self.labelPicture.height(), QtCore.Qt.KeepAspectRatio)) pic = Picture() data = Qt.QByteArray() buf = Qt.QBuffer(data) pic.type = 3 # APICType.COVER_FRONT self.logger('format: %s' % rformat) if rformat == 'png': pic.mime = 'image/png' image.save(buf, 'PNG') elif rformat == 'jpg' or rformat == 'jpeg': pic.mime = 'image/jpeg' image.save(buf, 'JPG') else: pic.mime = 'image/unknown' self.logger("format: %s" % pic.mime) pic.data = data.data() pic.width = self.coverArtPixmap.width() pic.height = self.coverArtPixmap.height() pic.depth = self.coverArtPixmap.depth() pic.colors = image.colorCount() if len(self.audio.pictures) > 0: ret = QtGui.QMessageBox.warning( self, "FLAC Tagger", "This FLAC file already contains one or more pictures.\n" "Do you want to replace them?", QtGui.QMessageBox.Yes | QtGui.QMessageBox.Cancel, QtGui.QMessageBox.Cancel) if ret == QtGui.QMessageBox.Yes: self.audio.clear_pictures() self.audio.addPicture(pic) else: # make sure the old picture is displayed again self.displayPictures() else: self.audio.addPicture(pic)
def tag_put_picture(album_id): pic = Picture() album = get_album(album_id) with open(album['Path'] + "/folder.jpg", "rb") as f: pic.data = f.read() if not pic.data: return 'No folder.jpg found' pic.type = id3.PictureType.COVER_FRONT pic.mime = u"image/jpeg" pic.width = 500 pic.height = 500 pic.depth = 16 for p in tag_get_piece_paths(album_id): set_pic(p, pic) return 'success'
def _add_ogg_image(audio: File, image: Image, image_data: bytes): picture = Picture() picture.data = image_data picture.type = 3 # Front cover picture.desc = u"Cover Art" picture.mime = _image_mimes[image.format] picture.width = image.width picture.height = image.height picture.depth = 24 picture_data = picture.write() encoded_data = base64.b64encode(picture_data) vcomment_value = encoded_data.decode("ascii") audio["metadata_block_picture"] = [vcomment_value] audio.save()
def tag_flac(self, file_path, track_info, album_info, album_art_path=None): tagger = FLAC(file_path) self._meta_tag(tagger, track_info, album_info) if self.fmtopts['embed_album_art'] and album_art_path is not None: pic = Picture() with open(album_art_path, 'rb') as f: pic.data = f.read() pic.type = PictureType.COVER_FRONT pic.mime = u"image/jpeg" # TODO: detect this automatically? pic.width = 1280 pic.height = 1280 pic.depth = 24 tagger.add_picture(pic) tagger.save(file_path)
def create_album_art(self, art_location: str): with open(art_location, 'rb') as image: image_data = image.read() image = Image.open(io.BytesIO(image_data)) picture = Picture() picture.data = image_data picture.type = 3 # COVER_FRONT picture.mime = file_type(art_location) picture.width, picture.height = image.size picture.depth = mode_to_depth(image.mode) picture.desc = "Front Cover" image.close() return picture
def addCover(self): if self.fileSuffix == "mp3" or self.fileSuffix == "MP3": self.songFile.tags.add( APIC(encoding=3, mime='image/jpeg', type=3, desc="Cover", data=self.albumart)) elif self.fileSuffix == "flac" or self.fileSuffix == "FLAC": image = Picture() image.type = id3.PictureType.COVER_FRONT image.mime = 'image/jpeg' image.desc = "Cover" image.data = self.albumart image.width = 500 image.height = 500 image.depth = 16 self.songFile.add_picture(image) self.songFile.save()
def updateCoverOgg(lossyFileName, artworkFileName): # # Embed album art into transcoded file: OGG # import base64 from mutagen.oggvorbis import OggVorbis from mutagen.flac import Picture import PIL.Image import tempfile from shutil import copyfile # Use copyfile b/c this will *not* copy rights (which is error prone on gvfs/samba) log('- embedding album art ' + artworkFileName + ' to ' + lossyFileName) # Copy lossy file to a local location; to prevent (save) errors in a samba environment tempLossyFile = tempfile.gettempdir() + '/' + 'temp.ogg' copyfile(lossyFileName, tempLossyFile) # Embed the image o = OggVorbis(tempLossyFile) im = PIL.Image.open(artworkFileName) w, h = im.size p = Picture() imdata = open(artworkFileName, 'rb').read() p.data = imdata p.type = 3 p.desc = '' p.mime = 'image/jpeg' p.width = w p.height = h p.depth = 24 dt = p.write() enc = base64.b64encode(dt).decode('ascii') o['metadata_block_picture'] = [enc] o.save() # Now we are ready; copy the file to the desired output directory copyfile(tempLossyFile, lossyFileName) os.remove(tempLossyFile) # Remove the temporary file(s) return
def tag_flac(self, file_path, track_info, album_info, album_art_path=None): tagger = FLAC(file_path) self._meta_tag(tagger, track_info, album_info, 'flac') if self.fmtopts['embed_album_art'] and album_art_path is not None: pic = Picture() with open(album_art_path, 'rb') as f: pic.data = f.read() pic.type = PictureType.COVER_FRONT pic.mime = u"image/jpeg" try: img = cv2.imread(album_art_path) pic.height, pic.width, channels = img.shape except Exception as e: pic.width = 1280 pic.height = 1280 pic.depth = 24 tagger.add_picture(pic) tagger.save(file_path)
def write_ogg_meta(ogg_file_path, cover_bytes, dimensions, year, artist, album, title): ogg_file = OggVorbis(ogg_file_path) picture = Picture() picture.data = cover_bytes picture.type = 3 picture.mime = u"image/jpeg" picture.width = dimensions[0] picture.height = dimensions[1] picture.depth = 24 picture_data = picture.write() encoded_data = b64encode(picture_data) comment = encoded_data.decode("ascii") ogg_file["metadata_block_picture"] = [comment] ogg_file["date"] = [year] ogg_file["artist"] = [artist] ogg_file["album"] = [album] ogg_file["title"] = [title] ogg_file.save()
def ogg_coverart(config): print("Adding " + config.coverart_mime + " cover art to " + config.ogg_file) coverart = config.coverart imgdata = open(coverart,'rb').read() im = Image.open(coverart) w,h = im.size p = Picture() p.data = imgdata p.type = 3 p.desc = 'Cover' p.mime = config.coverart_mime p.width = w p.height = h p.depth = 24 dt=p.write() enc=base64.b64encode(dt).decode('ascii') audio = OggVorbis(config.ogg_file) audio['metadata_block_picture']=[enc] audio.save()
def write_cover_opus(afile, cover): file_ = OggOpus(afile) with open(cover, "rb") as h: data = h.read() picture = Picture() picture.data = data picture.type = 17 picture.desc = u"cover art" picture.mime = u"image/" + os.path.splitext(cover)[1][1:] dim = [int(x) for x in COVER_DIMENSION_INTERN.split("x")] picture.width = dim[0] picture.height = dim[1] picture.depth = 24 picture_data = picture.write() encoded_data = base64.b64encode(picture_data) vcomment_value = encoded_data.decode("ascii") file_["metadata_block_picture"] = [vcomment_value] file_.save()
def set_image(self, image): """Replaces all embedded images by the passed image""" try: audio = self.MutagenType(self["~filename"]) data = image.file.read() except EnvironmentError: return pic = Picture() pic.data = data pic.type = APICType.COVER_FRONT pic.mime = image.mime_type pic.width = image.width pic.height = image.height pic.depth = image.color_depth audio.pop("coverart", None) audio.pop("coverartmime", None) audio["metadata_block_picture"] = base64.b64encode(pic.write()) audio.save() self.has_images = True
def tagOGG(conf, mediafile): # https://mutagen.readthedocs.io/en/latest/user/vcomment.html # https://wiki.xiph.org/VorbisComment#METADATA_BLOCK_PICTURE # https://xiph.org/flac/format.html#metadata_block_picture # https://github.com/quodlibet/mutagen/issues/200 img_stream, img_meta = imgConv(conf['tags']['img']) picture = Picture() picture.data = img_stream picture.type = 3 picture.description = '{0} ({1})'.format(conf['tags']['artist'], conf['tags']['comment']) picture.mime = img_meta['mime'] picture.width = img_meta['width'] picture.height = img_meta['height'] picture.depth = img_meta['depth'] picture.desc = '{0} ({1})'.format(conf['tags']['artist'], conf['tags']['comment']) containered_data = picture.write() encoded_data = base64.b64encode(containered_data) img_tag = encoded_data.decode('ascii') print('{0}: Now adding tags to {1}...'.format(datetime.datetime.now(), mediafile)) tag = OggVorbis(mediafile) tag['TITLE'] = conf['episode']['pretty_title'] tag['ARTIST'] = conf['tags']['artist'] tag['ALBUM'] = conf['tags']['album'] tag['DATE'] = '{0}.{1}.{2}'.format(conf['tags']['year'], conf['episode']['month'], conf['episode']['day']) tag['TRACKNUMBER'] = conf['tags']['track'] tag['GENRE'] = conf['tags']['genre'] tag['DESCRIPTION'] = conf['tags']['comment'] tag['COPYRIGHT'] = conf['tags']['copyright'] tag['CONTACT'] = conf['tags']['url'] tag['ENCODED-BY'] = conf['tags']['encoded'] tag['ENCODER'] = conf['tags']['encoded'] tag['METADATA_BLOCK_PICTURE'] = [img_tag] tag.save()
def set_image(self, image): """Replaces all embedded images by the passed image""" try: tag = FLAC(self["~filename"]) data = image.file.read() except EnvironmentError: return pic = Picture() pic.data = data pic.type = APICType.COVER_FRONT pic.mime = image.mime_type pic.width = image.width pic.height = image.height pic.depth = image.color_depth tag.add_picture(pic) tag.save() # clear vcomment tags super(FLACFile, self).clear_images() self.has_images = True
def UpdateCoverOgg(lossyFileName, artworkFileName): import base64; from mutagen.oggvorbis import OggVorbis from mutagen.flac import Picture; import PIL.Image import tempfile from shutil import copyfile # Use copyfile b/c this will *not* copy rights (which is error prone on gvfs/samba) Log('- embedding album art in ' + lossyFileName) # Copy lossy file to a local location; to prevent (save) errors in a samba environment tempLossyFile = tempfile.gettempdir() + '/' + 'temp.ogg' copyfile(lossyFileName, tempLossyFile) o = OggVorbis(tempLossyFile) im = PIL.Image.open(artworkFileName) w,h = im.size p = Picture() imdata = open(artworkFileName,'rb').read() p.data = imdata p.type = 3 p.desc = '' p.mime = 'image/jpeg' p.width = w p.height = h p.depth = 24 dt = p.write() enc = base64.b64encode(dt).decode('ascii') o['metadata_block_picture'] = [enc] o.save() copyfile(tempLossyFile, lossyFileName) os.remove(tempLossyFile) return
def Utils_Meta_setMusicInfo(path, info): """ TODO: Write lyrics to file :param path:文件目录 :param info:字典,详情: { "TALB": "Name of Album", "TIT2": "Title", "TPE1": "Author,Separate with '/'", "APIC": "Path to cover photo", "STRICT": Boolean:strict cover mode, "TRANSCODE": Boolean:convert to mp3, "TRANSPATH": "Path to converted file" } :return: int { 0: Nothing need done 1: Need reExt } """ status_code = 0 try: id3 = ID3(path) id3.update_to_v23() id3.delall("TALB") id3.delall("TIT2") id3.delall("TPE1") id3.delall("APIC") id3.add(TALB(encoding=3, text=info["TALB"])) id3.add(TIT2(encoding=3, text=info["TIT2"])) id3.add(TPE1(encoding=3, text=info["TPE1"])) if info["STRICT"]: image = Image.open(info["APIC"]) img_bytes = io.BytesIO() if image.size[0] > image.size[1]: image = image.crop( (int((image.size[0] - image.size[1]) / 2), 0, int((image.size[0] + image.size[1]) / 2), image.size[1])) elif image.size[0] < image.size[1]: image = image.crop((0, int( (image.size[1] - image.size[0]) / 2), 0, int((image.size[0] + image.size[1]) / 2))) image.resize((300, 300)).save(img_bytes, format="JPEG") id3.add( APIC(encoding=0, mime=mimetypes.guess_type(info["APIC"])[0], type=6, data=img_bytes.getvalue())) else: with open(info["APIC"], "rb") as f: id3.add( APIC(encoding=0, mime=mimetypes.guess_type(info["APIC"])[0], type=6, data=f.read())) id3.save() except ID3NoHeaderError: traceback.print_exc() ext = os.path.splitext(path)[1] if ".flac" in ext or ".FLAC" in ext: flac = FLAC(path) flac.tags['TITLE'] = info["TIT2"] flac.tags['ALBUM'] = info["TALB"] flac.tags['ARTIST'] = info["TPE1"] with open(info["APIC"], "rb") as f: image = Image.open(info["APIC"]) p = Picture() p.data = f.read() p.type = 3 p.mime = mimetypes.guess_type(info["APIC"])[0] p.width = image.size[0] p.height = image.size[1] p.depth = 24 # color depth flac.add_picture(p) image.close() flac.save() else: try: mp4 = MP4(path) mp4.tags["\xa9alb"] = info["TALB"] mp4.tags["\xa9nam"] = info["TIT2"] mp4.tags["\xa9ART"] = info["TPE1"] with open(info["APIC"], "rb") as f: mp4["covr"] = [ MP4Cover(f.read(), imageformat=MP4Cover.FORMAT_PNG) ] mp4.save() status_code = 1 except Exception: traceback.print_exc() if info["TRANSCODE"]: if not os.path.exists(os.path.split(info["TRANSPATH"])[0]): os.makedirs(os.path.split(info["TRANSPATH"])[0]) Utils_FormatTools.Utils_Format_autoTranscode( path, info["TRANSPATH"]) info["TRANSCODE"] = False Utils_Meta_setMusicInfo(info["TRANSPATH"], info) except MutagenError: traceback.print_exc() return status_code
def set_release_metadata(artist_name, album_name, record_id): translation = {'/': '-'} table = str.maketrans(translation) record = get_local_record_list(artist_name, album_name.translate(table)) record_dir = record['record_dir'] try: release = musicbrainzngs.get_release_by_id(record_id, includes=[ 'recordings', ]) release = release['release'] except: release = musicbrainzngs.get_release_by_id(record_id) try: album = release['title'] except KeyError: album = 'not defined' try: date = release['date'] except KeyError: date = 'not defined' try: country = release['country'] except KeyError: country = 'not defined' try: mediumtotal = release['medium-count'] except KeyError: mediumtotal = 0 tracknumber = 1 for i in range(1, mediumtotal + 1): if mediumtotal > 1: base_dir = record_dir + f'/CD{i}/' base_dir_art = record_dir + '/' else: base_dir = record_dir + '/' base_dir_art = record_dir + '/' try: pic = Picture() with open(f'{base_dir_art}fanart.jpg', "rb") as f: pic.data = f.read() pic.mime = u"image/jpeg" pic.width = 1000 pic.height = 1000 pic.depth = 16 except: logger.warning('No Fan Art Available!') songs = os.listdir(base_dir) for song in songs: fullpath = base_dir + song extension = pathlib.Path(fullpath).suffix if extension == '.flac': metadata = FLAC(fullpath) for item in metadata.items(): if item[0] == 'tracknumber': tracknumber = int(item[1].pop()) records = filter(lambda x: int(x['position']) == i, release['medium-list']) for recording in records: tracktotal = recording['track-count'] for track in recording['track-list']: if int(track['number']) == tracknumber: tracktitle = track['recording']['title'].translate( table) trackid = track['recording']['id'] track_musicbrainz = musicbrainzngs.get_recording_by_id( trackid, includes=[ 'artists', ]) artist = track_musicbrainz['recording'][ 'artist-credit-phrase'] metadata.delete() metadata.clear_pictures() if pic: metadata.add_picture(pic) metadata["Album"] = album metadata["Albumartist"] = artist_name metadata["Artist"] = artist_name metadata["Country"] = country metadata["Date"] = date metadata["Discnumber"] = str(i) metadata["Title"] = tracktitle metadata["Tracktotal"] = str(tracktotal) metadata["Tracknumber"] = str(tracknumber) metadata.save() logger.info(f'old name: {song}') if mediumtotal != 1: logger.info( f'new name: {artist} - {album.translate(table)} - CD{i} - {tracknumber:02} - {tracktitle}.flac' ) os.rename( fullpath, f'{base_dir}{artist} - {album.translate(table)} - CD{i} - {tracknumber:02} - {tracktitle}.flac' ) else: logger.info( f'new name: {artist} - {album.translate(table)} - {tracknumber:02} - {tracktitle}.flac' ) os.rename( fullpath, f'{base_dir}{artist} - {album.translate(table)} - {tracknumber:02} - {tracktitle}.flac' )