def embed_image(): pic = flac.Picture() pic.type = 3 pic.mime = "image/jpeg" pic.desc = "Front Cover" pic.data = image if args.output_type == "flac": audio.add_picture(pic) else: data = base64.b64encode(pic.write()) audio["METADATA_BLOCK_PICTURE"] = [data.decode("ascii")]
def flac_art(self, flac_file): song = mflac.FLAC(flac_file) pic = mflac.Picture() pic.data = self.art_data pic.type = 3 pic.mime = 'image/jpeg' song.add_picture(pic) song.save()
def vorbis_art(self, vorbis_file): song = mogg.OggVorbis(vorbis_file) pic = mflac.Picture() pic.data = self.art_data pic.type = 3 pic.mime = 'image/jpeg' song['METADATA_BLOCK_PICTURE'] = str(base64.b64encode(pic.write()), 'utf-8') song.save()
def _savePic(self, coverPath): data = _getFileData(coverPath) if data is None: return if 'flac' in self._ext: pic = flac.Picture() pic.data = data if pathHelper.getFileExtension(coverPath) == '.jpg': pic.mime = u"image/jpeg" self._handle.clear_pictures() self._handle.add_picture(pic) if 'mp3' in self._ext: self._handle.tags.add(APIC(encoding=3, data=data)) if 'mp4' in self._ext or 'm4a' in self._ext: pic = mp4.MP4Cover(data) self._handle.tags['covr'] = [pic]
def __savePic__(self, coverPath): data = __content__(coverPath) if data is None: return if 'flac' in self._ext: pic = flac.Picture() pic.data = data if '.jpg' in coverPath: pic.mime = u"image/jpeg" self._handle.clear_pictures() self._handle.add_picture(pic) if 'mp3' in self._ext: self._handle.tags.add(APIC(encoding=3, data=data)) if 'mp4' in self._ext or 'm4a' in self._ext: pic = mp4.MP4Cover(data) self._handle.tags['covr'] = [pic]
def flacedit(self): """ FLACの曲情報を編集 """ # タグ書き換え self.tags['TITLE'] = self.title self.tags['ALBUM'] = self.album self.tags['ARTIST'] = self.artist self.tags['GENRE'] = self.genre self.tags['COMMENT'] = self.comment # アートワーク書き換え if not self.artwork_url == '': # 画像読み込み try: artwork_read = urlopen(self.artwork_url).read() except: raise URLOpenError("画像を取得できません") # 書き込み用画像オブジェクトを作成 pic = flac.Picture() pic.data = artwork_read pic.type = id3.PictureType.COVER_FRONT pic.mine = 'image/jpeg' # 画像消去 self.tags.clear_pictures() # 画像設定 self.tags.add_picture(pic) # 保存 self.tags.save(self.filepath) # 表示用アートワーク更新 artworks = self.tags.pictures artwork = None for artwork in artworks: # 抽出(最後に登録されている画像のみ) pass if artwork: # アートワーク画像が存在するか self.artwork = artwork.data # type: bytes else: self.artwork = None
def insert_flac(abs_audio_f): cover = cut_suffix(abs_audio_f) + '.jpg' mime = u"image/jpeg" if not os.path.exists(cover): cover = cut_suffix(abs_audio_f) + '.png' mime = u"image/png" if not os.path.exists(cover): # print('Cover for [' + abs_audio_f + '] not found.') return audio = flac.FLAC(abs_audio_f) audio.clear_pictures() with open(cover, 'rb') as img_f: pic = flac.Picture() pic.data = img_f.read() pic.type = id3.PictureType.COVER_FRONT pic.mime = mime # pic.width = 500 # pic.height = 500 audio.add_picture(pic) try: audio.save() except MutagenError: MutagenErrors.append(abs_audio_f)
def mark(path, song, id=None): def streamify(file): with file: return file.read() def embed(item, content, type): item.encoding = 0 item.type = type item.mime = 'image/png' if content[0:4] == binascii.a2b_hex( '89504E47') else 'image/jpeg' item.data = content format = 'flac' if open( path, 'rb').read(4) == binascii.a2b_hex('664C6143') else 'mp3' audio = mp3.EasyMP3(path) if format == 'mp3' else flac.FLAC(path) meta = { 'album': song['album']['name'], 'albumId': song['album']['id'], 'albumPic': song['album']['picUrl'], 'albumPicDocId': str(song['album']['pic'] if 'pic' in song['album'] else re. search(r'/(\d+)\.\w+$', song['album']['picUrl']).group(1)), 'alias': song['alias'] if 'alias' in song else [], 'artist': [[artist['name'], artist['id']] for artist in song['artists']], 'musicId': id if id else song['id'], 'musicName': song['name'] if 'name' in song else audio['title'][0], 'mvId': song['mvid'] if 'mvid' in song else 0, 'transNames': [], 'format': format, 'bitrate': audio.info.bitrate, 'duration': int(audio.info.length * 1000), 'mp3DocId': hashlib.md5(streamify(open(path, 'rb'))).hexdigest() } cryptor = AES.new(key, AES.MODE_ECB) identifier = 'music:' + json.dumps(meta) identifier = '163 key(Don\'t modify):' + base64.b64encode( cryptor.encrypt(pad(identifier.encode('utf8'), 16))).decode('utf8') audio.delete() audio['title'] = meta['musicName'] audio['album'] = meta['album'] audio['artist'] = '/'.join([artist[0] for artist in meta['artist']]) if format == 'flac': audio['description'] = identifier else: audio.tags.RegisterTextKey('comment', 'COMM') audio['comment'] = identifier audio.save() data = requests.get(meta['albumPic'] + '?param=300y300').content if format == 'flac': audio = flac.FLAC(path) image = flac.Picture() embed(image, data, 3) audio.clear_pictures() audio.add_picture(image) elif format == 'mp3': audio = mp3.MP3(path) image = id3.APIC() embed(image, data, 6) audio.tags.add(image) audio.save()
def dump(input_path, output_path=None, skip=True): output_path = (lambda path, meta: os.path.splitext(path)[0] + '.' + meta[ 'format']) if not output_path else output_path output_path = ( lambda path, meta: path) if not callable(output_path) else output_path core_key = binascii.a2b_hex('687A4852416D736F356B496E62617857') meta_key = binascii.a2b_hex('2331346C6A6B5F215C5D2630553C2728') unpad = lambda s: s[0:-(s[-1] if type(s[-1]) == int else ord(s[-1]))] f = open(input_path, 'rb') # magic header header = f.read(8) assert binascii.b2a_hex(header) == b'4354454e4644414d' # key data f.seek(2, 1) key_length = f.read(4) key_length = struct.unpack('<I', bytes(key_length))[0] key_data = bytearray(f.read(key_length)) key_data = bytes(bytearray([byte ^ 0x64 for byte in key_data])) cryptor = AES.new(core_key, AES.MODE_ECB) key_data = unpad(cryptor.decrypt(key_data))[17:] key_length = len(key_data) # S-box (standard RC4 Key-scheduling algorithm) key = bytearray(key_data) S = bytearray(range(256)) j = 0 for i in range(256): j = (j + S[i] + key[i % key_length]) & 0xFF S[i], S[j] = S[j], S[i] # meta data meta_length = f.read(4) meta_length = struct.unpack('<I', bytes(meta_length))[0] if meta_length: meta_data = bytearray(f.read(meta_length)) meta_data = bytes(bytearray([byte ^ 0x63 for byte in meta_data])) identification = meta_data.decode('utf-8') meta_data = base64.b64decode(meta_data[22:]) cryptor = AES.new(meta_key, AES.MODE_ECB) meta_data = unpad(cryptor.decrypt(meta_data)).decode('utf-8')[6:] meta_data = json.loads(meta_data) else: meta_data = { 'format': 'flac' if os.fstat(f.fileno()).st_size > 1024**2 * 16 else 'mp3' } # crc32 crc32 = f.read(4) crc32 = struct.unpack('<I', bytes(crc32))[0] # album cover f.seek(5, 1) image_size = f.read(4) image_size = struct.unpack('<I', bytes(image_size))[0] image_data = f.read(image_size) if image_size else None # media data output_path = output_path(input_path, meta_data) if skip and os.path.exists(output_path): return data = f.read() f.close() # stream cipher (modified RC4 Pseudo-random generation algorithm) stream = [S[(S[i] + S[(i + S[i]) & 0xFF]) & 0xFF] for i in range(256)] stream = bytes(bytearray(stream * (len(data) // 256 + 1))[1:1 + len(data)]) data = XOR(data, stream) m = open(output_path, 'wb') m.write(data) m.close() # media tag def embed(item, content, type): item.encoding = 0 item.type = type item.mime = 'image/png' if content[0:4] == binascii.a2b_hex( '89504E47') else 'image/jpeg' item.data = content if image_data: if meta_data['format'] == 'flac': audio = flac.FLAC(output_path) image = flac.Picture() embed(image, image_data, 3) audio.clear_pictures() audio.add_picture(image) elif meta_data['format'] == 'mp3': audio = mp3.MP3(output_path) image = id3.APIC() embed(image, image_data, 6) audio.tags.add(image) audio.save() if meta_length: if meta_data['format'] == 'flac': audio = flac.FLAC(output_path) audio['description'] = identification else: audio = mp3.EasyMP3(output_path) audio.tags.RegisterTextKey('comment', 'COMM') audio['comment'] = identification audio['title'] = meta_data['musicName'] audio['album'] = meta_data['album'] audio['artist'] = '/'.join( [artist[0] for artist in meta_data['artist']]) audio.save() return output_path
def dump(input_path, output_path=None, skip=True): output_path = (lambda path, meta: os.path.splitext(path)[0] + '.' + meta[ 'format']) if not output_path else output_path core_key = binascii.a2b_hex('687A4852416D736F356B496E62617857') meta_key = binascii.a2b_hex('2331346C6A6B5F215C5D2630553C2728') unpad = lambda s: s[0:-(s[-1] if type(s[-1]) == int else ord(s[-1]))] f = open(input_path, 'rb') # magic header header = f.read(8) assert binascii.b2a_hex(header) == b'4354454e4644414d' # key data f.seek(2, 1) key_length = f.read(4) key_length = struct.unpack('<I', bytes(key_length))[0] key_data = bytearray(f.read(key_length)) key_data = bytes(bytearray([byte ^ 0x64 for byte in key_data])) cryptor = AES.new(core_key, AES.MODE_ECB) key_data = unpad(cryptor.decrypt(key_data))[17:] key_length = len(key_data) # S-box (standard RC4 Key-scheduling algorithm) key = bytearray(key_data) S = bytearray(range(256)) j = 0 for i in range(256): j = (j + S[i] + key[i % key_length]) % 256 S[i], S[j] = S[j], S[i] # meta data meta_length = f.read(4) meta_length = struct.unpack('<I', bytes(meta_length))[0] meta_data = bytearray(f.read(meta_length)) meta_data = bytes(bytearray([byte ^ 0x63 for byte in meta_data])) meta_data = base64.b64decode(meta_data[22:]) cryptor = AES.new(meta_key, AES.MODE_ECB) meta_data = unpad(cryptor.decrypt(meta_data)).decode('utf-8')[6:] meta_data = json.loads(meta_data) # crc32 crc32 = f.read(4) crc32 = struct.unpack('<I', bytes(crc32))[0] # album cover f.seek(5, 1) image_size = f.read(4) image_size = struct.unpack('<I', bytes(image_size))[0] image_data = f.read(image_size) # media data output_path = output_path(input_path, meta_data) if skip and os.path.exists(output_path): return m = open(output_path, 'wb') data = bytearray(f.read()) # stream cipher (modified RC4 Pseudo-random generation algorithm) i = 0 j = 0 for k, _ in enumerate(data): i = (i + 1) % 256 j = (i + S[i]) % 256 # in RC4, is j = (j + S[i]) % 256 # S[i], S[j] = S[j], S[i] # no swapping data[k] ^= S[(S[i] + S[j]) % 256] m.write(data) m.close() f.close() # media tag if meta_data['format'] == 'flac': audio = flac.FLAC(output_path) # audio.delete() image = flac.Picture() image.encoding = 0 image.type = 3 image.mime = 'image/jpeg' image.data = image_data audio.clear_pictures() audio.add_picture(image) elif meta_data['format'] == 'mp3': audio = mp3.MP3(output_path) # audio.delete() image = id3.APIC() image.encoding = 0 image.type = 3 image.mime = 'image/jpeg' image.data = image_data audio.tags.add(image) audio.save() audio = mp3.EasyMP3(output_path) audio['title'] = meta_data['musicName'] audio['album'] = meta_data['album'] audio['artist'] = '/'.join([artist[0] for artist in meta_data['artist']]) audio.save() return output_path
def dumpfile(file_path): # 预定义key core_key = binascii.a2b_hex("687A4852416D736F356B496E62617857") meta_key = binascii.a2b_hex("2331346C6A6B5F215C5D2630553C2728") unpad = lambda s: s[0:-(s[-1] if type(s[-1]) == int else ord(s[-1]))] f = open(file_path, 'rb') # magic header header = f.read(8) assert binascii.b2a_hex(header) == b'4354454e4644414d' # key data f.seek(2, 1) key_length = f.read(4) key_length = struct.unpack('<I', bytes(key_length))[0] key_data = bytearray(f.read(key_length)) key_data = bytes(bytearray([byte ^ 0x64 for byte in key_data])) cryptor = AES.new(core_key, AES.MODE_ECB) key_data = unpad(cryptor.decrypt(key_data))[17:] key_length = len(key_data) # key box key_data = bytearray(key_data) key_box = bytearray(range(256)) j = 0 for i in range(256): j = (key_box[i] + j + key_data[i % key_length]) & 0xff key_box[i], key_box[j] = key_box[j], key_box[i] # meta data meta_length = f.read(4) meta_length = struct.unpack('<I', bytes(meta_length))[0] meta_data = bytearray(f.read(meta_length)) meta_data = bytes(bytearray([byte ^ 0x63 for byte in meta_data])) meta_data = base64.b64decode(meta_data[22:]) cryptor = AES.new(meta_key, AES.MODE_ECB) meta_data = unpad(cryptor.decrypt(meta_data)).decode('utf-8')[6:] meta_data = json.loads(meta_data) # crc32 crc32 = f.read(4) crc32 = struct.unpack('<I', bytes(crc32))[0] # album cover f.seek(5, 1) image_size = f.read(4) image_size = struct.unpack('<I', bytes(image_size))[0] image_data = f.read(image_size) author_name = "" # media data if len(meta_data['artist']) == 1: author_name = meta_data['artist'][0][0] else: for i in range(len(meta_data['artist'])): if i == len(meta_data['artist']) - 1: author_name += meta_data['artist'][i][0] else: author_name += meta_data['artist'][i][0] author_name += ',' file_name = author_name + ' - ' + meta_data['musicName'] + '.' + meta_data['format'] music_path = os.path.join(os.path.split(file_path)[0], file_name) m = open(music_path, 'wb') while True: chunk = bytearray(f.read(0x8000)) chunk_length = len(chunk) if not chunk: break for i in range(chunk_length): j = (i + 1) & 0xff chunk[i] ^= key_box[(key_box[j] + key_box[(key_box[j] + j) & 0xff]) & 0xff] m.write(chunk) m.close() f.close() # media tag if meta_data['format'] == 'flac': audio = flac.FLAC(music_path) # audio.delete() image = flac.Picture() image.type = 3 image.mime = 'image/jpeg' image.data = image_data audio.clear_pictures() audio.add_picture(image) elif meta_data['format'] == 'mp3': audio = mp3.MP3(music_path) # audio.delete() image = id3.APIC() image.type = 3 image.mime = 'image/jpeg' image.data = image_data audio.tags.add(image) audio.save() audio = mp3.EasyMP3(music_path) audio['title'] = meta_data['musicName'] audio['album'] = meta_data['album'] audio['artist'] = '/'.join([artist[0] for artist in meta_data['artist']]) audio.save()
def __get_tag_image(self, image_file, audio_mime_type): """ The function that returns cover image. :param str image_file: Path to image file :param list audio_mime_type: Mime-Types of image file :return binary image_data: Binary data of image file """ if not os.path.isfile(image_file): return # Create image with open(image_file, 'rb') as f: image_data = f.read() if image_data is None: raise InvalidDataException("Could not create image data.", image_file) image_mime_type = mimetypes.guess_type(image_file)[0] if contains_at_least(audio_mime_type, ['audio/x-mp4', 'audio/x-m4a', 'audio/mp4a-latm']): if image_mime_type == "image/jpeg": image_format = mp4.MP4Cover.FORMAT_JPEG elif image_mime_type == "image/png": image_format = mp4.MP4Cover.FORMAT_PNG else: raise InvalidMimeTypeException("Invalid image format.", image_mime_type) return mp4.MP4Cover(image_data, image_format) elif contains_at_least(audio_mime_type, ['audio/x-mp3', 'audio/mpeg']): if not image_mime_type == "image/jpeg" and not image_mime_type == "image/png": raise InvalidMimeTypeException("Invalid image format.", image_mime_type) return id3.APIC(encoding=3, mime=image_mime_type, type=3, desc='Cover', data=image_data) elif contains_at_least(audio_mime_type, ['audio/x-aac']): # TODO: Add aac support pass # if not image_mime_type == "image/jpeg" and not image_mime_type == "image/png": # raise InvalidMimeTypeException("Invalid image format.", image_mime_type) # # picture = flac.Picture() # picture.type = 3 # picture.desc = 'front cover' # picture.data = image_data # # return picture elif contains_at_least(audio_mime_type, ['audio/x-flac']): if not image_mime_type == "image/jpeg" and not image_mime_type == "image/png": raise InvalidMimeTypeException("Invalid image format.", image_mime_type) picture = flac.Picture() picture.type = 3 picture.desc = 'front cover' picture.data = image_data return picture else: raise InvalidMimeTypeException("Invalid audio format.", audio_mime_type)
def dump(input_path): f = open(input_path, 'rb') # 开头都一样,不一样解不了密 header = f.read(8) assert binascii.b2a_hex(header) == b'4354454e4644414d' f.seek(2, 1) # key data, 要翻译成小端的unsigned int key_length = f.read(4) key_length = struct.unpack('<I', bytes(key_length))[0] # AES-ECB 密文 key_data = bytearray(f.read(key_length)) key_data = bytes(bytearray([byte ^ 0x64 for byte in key_data])) # AES-ECB 解密 core_key = binascii.a2b_hex('687A4852416D736F356B496E62617857') # 神奇的key,hashcat还是有内鬼? cryptor = AES.new(core_key, AES.MODE_ECB) # decrypt 前17位是 neteasecloudmusic,采用pkcs7 padding方式 key_data = unpad(cryptor.decrypt(key_data), 16)[17:] key_length = len(key_data) # S-box (标准 RC4 KSA) key = bytearray(key_data) S = bytearray(range(256)) j = 0 for i in range(256): j = (j + S[i] + key[i % key_length]) & 0xFF S[i], S[j] = S[j], S[i] # meta data,要翻译成小端的unsigned int meta_length = f.read(4) meta_length = struct.unpack('<I', bytes(meta_length))[0] if meta_length: meta_data = bytearray(f.read(meta_length)) meta_data = bytes(bytearray([byte ^ 0x63 for byte in meta_data])) identifier = meta_data.decode('utf-8') # 惊现 '163 key(Don't modify):' 内鬼? # base64 解码 meta_data = base64.b64decode(meta_data[22:]) # 第二次 AES-ECB 解密 meta_key = binascii.a2b_hex('2331346C6A6B5F215C5D2630553C2728') cryptor = AES.new(meta_key, AES.MODE_ECB) meta_data = unpad(cryptor.decrypt(meta_data), 16).decode('utf-8') # 解密出来一个 json 格式文件 meta_data = json.loads(meta_data[6:]) else: # 没有 json 文件确定格式的话,就用文件大小区分(>16m) meta_data = {'format': 'flac' if os.fstat(f.fileno()).st_size > 16777216 else 'mp3'} f.seek(5, 1) # 专辑封面图片 image_space = f.read(4) image_space = struct.unpack('<I', bytes(image_space))[0] image_size = f.read(4) image_size = struct.unpack('<I', bytes(image_size))[0] image_data = f.read(image_size) if image_size else None f.seek(image_space - image_size, 1) # 音乐输出地址 output_path = os.path.splitext(input_path)[0] + '.' + meta_data['format'] # 已转换过的不做无用功 if os.path.exists(output_path): return # 剩下全是音乐文件 data = f.read() f.close() # 音乐文件主体部分 (修改的RC4-PRGA,没有用 j 加和随机化) # 直接循环的话会丢失最后几秒 stream = [S[(S[i] + S[(i + S[i]) & 0xFF]) & 0xFF] for i in range(256)] stream = bytes(bytearray(stream * (len(data) // 256 + 1))[1:1 + len(data)]) data = strxor(data, stream) m = open(output_path, 'wb') m.write(data) m.close() # 处理专辑封面 if image_data: if meta_data['format'] == 'flac': audio = flac.FLAC(output_path) image = flac.Picture() image.encoding = 0 image.type = 3 image.mime = 'image/png' if image_data[0:4] == binascii.a2b_hex('89504E47') else 'image/jpeg' # png开头是\x89 P N G image.data = image_data audio.clear_pictures() audio.add_picture(image) audio.save() elif meta_data['format'] == 'mp3': audio = mp3.MP3(output_path) image = id3.APIC() image.encoding = 0 image.type = 6 image.mime = 'image/png' if image_data[0:4] == binascii.a2b_hex('89504E47') else 'image/jpeg' # png开头是\x89 P N G image.data = image_data audio.tags.add(image) audio.save() # 添加音乐相关信息 if meta_length: if meta_data['format'] == 'flac': audio = flac.FLAC(output_path) audio['description'] = identifier else: audio = mp3.EasyMP3(output_path) audio['title'] = 'placeholder' audio.tags.RegisterTextKey('comment', 'COMM') audio['comment'] = identifier audio['title'] = meta_data['musicName'] audio['album'] = meta_data['album'] audio['artist'] = '/'.join([artist[0] for artist in meta_data['artist']]) audio.save() return output_path
import os import sys import tqdm from mutagen import flac, id3 if len(sys.argv) == 2: working_dir = sys.argv[1] else: working_dir = os.getcwd() for root, dirs, files in os.walk(working_dir): tracks = [filename for filename in files if filename[-5:] == '.flac'] if len(tracks) > 0: album_cover = flac.Picture() with open(root + "/cover.jpg", 'rb') as f: album_cover.data = f.read() album_cover.type = id3.PictureType.COVER_FRONT album_cover.mime = u"image/jpeg" artist, album = root.split("/")[-2:] print(artist, '-', album) for filename in tqdm.tqdm(tracks): track_number, track_name = filename[:-5].split(" - ") track = flac.FLAC(root + "/" + filename) track['TITLE'] = track_name track['ALBUM'] = album track['ARTIST'] = artist track['TRACKNUMBER'] = track_number track['TOTALTRACKS'] = str(len(tracks)) track.add_picture(album_cover) track.save()