From 6663c2c2c6a8ec8ac79daeffdf826ce130227f2d Mon Sep 17 00:00:00 2001 From: Nzix Date: Mon, 1 Oct 2018 02:06:39 +0800 Subject: [PATCH] more options in app #3 --- README.md | 31 +++++++++++++---- app.py | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++++ ncmdump.py | 20 ++++++----- 3 files changed, 135 insertions(+), 16 deletions(-) create mode 100644 app.py diff --git a/README.md b/README.md index e7945d9..d1a5e36 100644 --- a/README.md +++ b/README.md @@ -2,20 +2,37 @@ ## 简介 -感激大佬的 [anonymous5l/ncmdump ](https://github.com/anonymous5l/ncmdump)项目,因好奇加密算法就用Python移植了下。自测发现转换出来的媒体文件都已包含媒体信息(包括专辑封面),~~故未再实现原repo中的写tag操作~~,应issue要求还是补上了写tag操作。Python实现比C++慢不少,实用性不大,仅供学习交流,请勿传播扩散。 +感谢大佬的 [anonymous5l/ncmdump ](https://github.com/anonymous5l/ncmdump)项目,因好奇加密算法就用Python移植了下。自测发现转换出来的媒体文件都已包含媒体信息(包括专辑封面),~~故未再实现原repo中的写tag操作~~,应issue要求还是补上了写tag操作。Python实现比C++慢不少,实用性不大,仅供学习交流,请勿传播扩散。 ## 依赖 ``` -pip(3) install pycryptodome mutagen +$ pip install pycryptodome mutagen ``` ## 使用 -指定ncm文件 +- 指定文件 ``` -python(3) ncmdump.py [files ...] +$ python ncmdump.py [files ...] ``` -工作目录下所有ncm文件 +- 遍历工作目录 ``` -python(3) ncmdump.py -``` \ No newline at end of file +$ python ncmdump.py +``` +- 更多选项 +``` +$ python app.py -h +usage: ncmdump [-h] [-f format] [-o output] [-d] [input [input ...]] + +positional arguments: + input ncm file or folder path + +optional arguments: + -h, --help show this help message and exit + -f format customize naming format + -o output customize saving folder + -d delete source after conversion +``` + +> 自定义命名参数: %artist%, %title%, %album% + diff --git a/app.py b/app.py new file mode 100644 index 0000000..ebc55f6 --- /dev/null +++ b/app.py @@ -0,0 +1,100 @@ +# -*- coding: utf-8 -*- +""" +Created on Fri Sep 28 13:32:51 2018 + +@author: Nzix +""" + +import argparse, os, sys, re +import ncmdump + +parser = argparse.ArgumentParser( + prog = 'ncmdump' +) +parser.add_argument( + 'input', metavar = 'input', nargs = '*', default = ['.'], + help = 'ncm file or folder path' +) +parser.add_argument( + '-f', metavar = 'format', dest = 'format', default = '', + help = 'customize naming format' +) +parser.add_argument( + '-o', metavar = 'output', dest = 'output', + help = 'customize saving folder' +) +parser.add_argument( + '-d', dest = 'delete', action='store_true', + help = 'delete source after conversion' +) +args = parser.parse_args() + +def validate_name(file_name): + pattern = {u'\\': u'\', u'/': u'/', u':': u':', u'*': u'*', u'?': u'?', u'"': u'"', u'<': u'<', u'>': u'>', u'|': u'|'} + for character in pattern: + file_name = file_name.replace(character, pattern[character]) + return file_name + +def validate_collision(file_path): + index = 1 + while os.path.exists(file_path): + file_path = '({})'.format(index).join(os.path.splitext(file_path)) + index += 1 + return file_path + +def name_format(path, meta): + information = { + 'artist': ','.join([artist[0] for artist in meta['artist']]), + 'title': meta['musicName'], + 'album': meta['album'] + } + + def substitute(matched): + key = matched.group(1) + if key in information: + return information[key] + else: + return key + + name = re.sub(r'%(.+?)%', substitute, args.format) + name = os.path.splitext(os.path.split(path)[1])[0] if not name else name + name = validate_name(name) + name += '.' + meta['format'] + folder = args.output if args.output else os.path.dirname(path) + save = os.path.join(folder, name) + save = validate_collision(save) + return save + +if args.output: + args.output = os.path.abspath(args.output) + if not os.path.exists(args.output): + print('output does not exist') + exit() + if not os.path.isdir(args.output): + print('output is not a folder') + exit() + +files = [] +for item in args.input: + item = os.path.abspath(item) + if not os.path.exists(item): + continue + if os.path.isdir(item): + files += [os.path.join(item, _file) for _file in os.listdir(item) if os.path.splitext(_file)[-1] == '.ncm'] + else: + flles += [item] + +if sys.version[0] == '2': + files = [file_name.decode(sys.stdin.encoding) for file_name in files] + +if not files: + print('empty input') + exit() + +for _file in files: + try: + _save = ncmdump.dump(_file, name_format) + print(os.path.split(_save)[-1]) + if args.delete: os.remove(_file) + except KeyboardInterrupt: + exit() \ No newline at end of file diff --git a/ncmdump.py b/ncmdump.py index d5eb63c..01babde 100644 --- a/ncmdump.py +++ b/ncmdump.py @@ -13,13 +13,14 @@ import os from Crypto.Cipher import AES from mutagen import mp3, flac, id3 -def dump(file_path): +def dump(input_path, output_path = None): + 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(file_path,'rb') + f = open(input_path,'rb') # magic header header = f.read(8) @@ -69,8 +70,8 @@ def dump(file_path): image_data = f.read(image_size) # media data - music_path = os.path.splitext(file_path)[0] + '.' + meta_data['format'] - m = open(music_path,'wb') + output_path = output_path(input_path, meta_data) + m = open(output_path,'wb') while True: chunk = bytearray(f.read(0x8000)) @@ -89,7 +90,7 @@ def dump(file_path): # media tag if meta_data['format'] == 'flac': - audio = flac.FLAC(music_path) + audio = flac.FLAC(output_path) # audio.delete() image = flac.Picture() image.type = 3 @@ -98,7 +99,7 @@ def dump(file_path): audio.clear_pictures() audio.add_picture(image) elif meta_data['format'] == 'mp3': - audio = mp3.MP3(music_path) + audio = mp3.MP3(output_path) # audio.delete() image = id3.APIC() image.type = 3 @@ -106,12 +107,14 @@ def dump(file_path): image.data = image_data audio.tags.add(image) audio.save() - audio = mp3.EasyMP3(music_path) + 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 if __name__ == '__main__': import sys @@ -131,5 +134,4 @@ if __name__ == '__main__': print(os.path.split(file_name)[-1]) except Exception as e: print(e) - pass - + pass \ No newline at end of file