File indexing completed on 2024-05-05 04:55:40

0001 import os
0002 import sys
0003 import re
0004 import locale
0005 import subprocess
0006 
0007 
0008 _kid3_cli_path = ''
0009 
0010 
0011 def kid3_cli_path():
0012     global _kid3_cli_path
0013     if not _kid3_cli_path:
0014         if sys.platform == 'win32':
0015             craft_root = os.getenv('CRAFTROOT')
0016             if craft_root:
0017                 from pathlib import Path
0018                 cli_path = Path(craft_root).parent / 'kid3-cli.exe'
0019                 if cli_path.exists():
0020                     _kid3_cli_path = str(cli_path)
0021                     return _kid3_cli_path
0022         curdir = os.getcwd()
0023         cli_exe = 'kid3-cli'
0024         if sys.platform == 'win32':
0025             cli_exe += '.exe'
0026         while True:
0027             cli_path = os.path.join(curdir, 'src', 'app', 'cli', cli_exe)
0028             if os.path.isfile(cli_path):
0029                 _kid3_cli_path = cli_path
0030                 if sys.platform == 'win32':
0031                     setup_run_environment()
0032                 break
0033             else:
0034                 cli_path = os.path.join(curdir, cli_exe)
0035                 if os.path.isfile(cli_path):
0036                     _kid3_cli_path = cli_path
0037                     break
0038             parentdir = os.path.dirname(curdir)
0039             if len(parentdir) < 2 or parentdir == curdir:
0040                 raise FileNotFoundError(cli_exe)
0041             curdir = parentdir
0042     return _kid3_cli_path
0043 
0044 
0045 def call_kid3_cli(args):
0046     if isinstance(args, str):
0047         args = [args]
0048     if sys.platform == 'win32':
0049         args.insert(0, '--portable')
0050     out = subprocess.check_output([kid3_cli_path()] + args)
0051     try:
0052         s = out.decode()
0053     except UnicodeDecodeError:
0054         s = out.decode(locale.getpreferredencoding())
0055     return s.replace('\r\n', '\n')
0056 
0057 
0058 def setup_run_environment():
0059     from pathlib import Path
0060     cache_path = Path(_kid3_cli_path).parents[3] / 'CMakeCache.txt'
0061     if cache_path.exists():
0062         qmake_re = re.compile(r'^QT_QMAKE_EXECUTABLE[^=]*=(.*)qmake')
0063         with open(cache_path, 'r') as fh:
0064             for line in fh:
0065                 m = qmake_re.match(line)
0066                 if m:
0067                     qt_bin_path = Path(m.group(1))
0068                     qt_plugin_path = qt_bin_path.parent / 'plugins'
0069                     os.environ['PATH'] += ';' + str(qt_bin_path) + ';' + \
0070                         str(Path('src/core')) + ';' + str(Path('src/gui'))
0071                     os.environ['QT_PLUGIN_PATH'] = str(qt_plugin_path)
0072                     return
0073 
0074 
0075 def create_test_file(filename):
0076     ext = os.path.splitext(filename)[1]
0077     if ext == '.m4a':
0078         d = b'\x00\x00\x00\x18ftypM4A \x00\x00\x02\x00isomiso2\x00\x00\x00\x08free\x00\x00\x00!mdat\xde\x02\x00' \
0079             b'Lavc56.41.100\x00\x020@\x0e\x01\x18 \x07\x00\x00\x02\xcdmoov\x00\x00\x00lmvhd\x00\x00\x00\x00\x00' \
0080             b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xe8\x00\x00\x00\x18\x00\x01\x00\x00\x01\x00\x00\x00\x00' \
0081             b'\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' \
0082             b'\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00' \
0083             b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00' \
0084             b'\x01\xf7trak\x00\x00\x00\\tkhd\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' \
0085             b'\x00\x00\x00\x00\x00\x00\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00' \
0086             b'\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00' \
0087             b'\x00\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00$edts' \
0088             b'\x00\x00\x00\x1celst\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x04\x00\x00\x01\x00' \
0089             b'\x00\x00\x00\x01omdia\x00\x00\x00 mdhd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xacD' \
0090             b'\x00\x00\x04\x01U\xc4\x00\x00\x00\x00\x00-hdlr\x00\x00\x00\x00\x00\x00\x00\x00soun\x00\x00\x00\x00' \
0091             b'\x00\x00\x00\x00\x00\x00\x00\x00SoundHandler\x00\x00\x00\x01\x1aminf\x00\x00\x00\x10smhd\x00\x00' \
0092             b'\x00\x00\x00\x00\x00\x00\x00\x00\x00$dinf\x00\x00\x00\x1cdref\x00\x00\x00\x00\x00\x00\x00\x01\x00' \
0093             b'\x00\x00\x0curl \x00\x00\x00\x01\x00\x00\x00\xdestbl\x00\x00\x00jstsd\x00\x00\x00\x00\x00\x00\x00' \
0094             b'\x01\x00\x00\x00Zmp4a\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00' \
0095             b'\x10\x00\x00\x00\x00\xacD\x00\x00\x00\x00\x006esds\x00\x00\x00\x00\x03\x80\x80\x80%\x00\x01\x00' \
0096             b'\x04\x80\x80\x80\x17@\x15\x00\x00\x00\x00\x01\xf4\x00\x00\x00!\x9c\x05\x80\x80\x80\x05\x12\x08V' \
0097             b'\xe5\x00\x06\x80\x80\x80\x01\x02\x00\x00\x00 stts\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01' \
0098             b'\x00\x00\x04\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x1cstsc\x00\x00\x00\x00\x00\x00\x00' \
0099             b'\x01\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x1cstsz\x00\x00\x00\x00\x00\x00' \
0100             b'\x00\x00\x00\x00\x00\x02\x00\x00\x00\x15\x00\x00\x00\x04\x00\x00\x00\x14stco\x00\x00\x00\x00\x00' \
0101             b'\x00\x00\x01\x00\x00\x00(\x00\x00\x00budta\x00\x00\x00Zmeta\x00\x00\x00\x00\x00\x00\x00!hdlr\x00' \
0102             b'\x00\x00\x00\x00\x00\x00\x00mdirappl\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08ilst\x00' \
0103             b'\x00\x00%free\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01' \
0104             b'\x01\x01\x01\x01\x01\x01\x01\x01'
0105     elif ext == '.flac':
0106         d = b'fLaC\x80\x00\x00"\x10\x00\x10\x00\x00\x00\x0c\x00\x00\x0c\n\xc4@\xf0\x00\x00\x00\x01\xc4\x10?\x12-\'g|' \
0107             b'\x9d\xb1D\xca\xe19Jf\xff\xf8i\x08\x00\x00\x1d\x02\x00\x00 \x0c'
0108     elif ext == '.spx':
0109         d = b'OggS\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x0c8_\n\x00\x00\x00\x00\x1a\xb2i\xd1\x01PSpeex   1.2rc1' \
0110             b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00P\x00\x00\x00D\xac\x00\x00\x02' \
0111             b'\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00\xff\xff\xff\xff\x80\x02\x00\x00\x00\x00\x00\x00\x01\x00' \
0112             b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00OggS\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' \
0113             b'\x0c8_\n\x01\x00\x00\x00\xe1j\xaf\xc6\x01!\x19\x00\x00\x00Encoded with Speex 1.2rc1\x00\x00\x00' \
0114             b'\x00OggS\x00\x04\x01\x00\x00\x00\x00\x00\x00\x00\x0c8_\n\x02\x00\x00\x00b\xc6\xa4\xf9\x01Z>\x9d\x1b' \
0115             b'\x9a \x00\x01\x7f\xff\xff\xff\xff\xff\xdbm\xb6\xdbm\xb6\x89\x00\xbf\xff\xff\xff\xff\xff\xed\xb6\xdbm' \
0116             b'\xb6\xdbB\x00_\xff\xff\xff\xff\xff\xf6\xdbm\xb6\xdbm\xa1\x00/\xff\xff\xff\xff\xff\xfbm\xb6\xdbm\xb6' \
0117             b'\xdb;`\xab\xab\xab\xab\xab\n\xba\xba\xba\xba\xb0\xab\xab\xab\xab\xab\n\xba\xba\xba\xba\xb9;`\x00\x00'
0118     elif ext == '.mp3':
0119         d = b'\xff\xfbP\xc4\x00\x03\xc0\x00\x01\xa4\x00\x00\x00 \x00\x004\x80\x00\x00\x04LAME3.99.5UUUUUUUUUUUUUUUU' \
0120             b'UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU' \
0121             b'UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU\xff\xfbR\xc4]\x83\xc0\x00\x01\xa4\x00\x00' \
0122             b'\x00 \x00\x004\x80\x00\x00\x04UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU' \
0123             b'UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU' \
0124             b'UUUUUUUUUUUUUUUU'
0125     elif ext == '.wv':
0126         d = b'wvpk`\x00\x00\x00\x07\x04\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x05\x18\x80\x04\xfd' \
0127             b'\xff\xff\xff!\x16RIFF&\x00\x00\x00WAVEfmt \x10\x00\x00\x00\x01\x00\x01\x00D\xac\x00\x00\x88X\x01\x00' \
0128             b'\x02\x00\x10\x00data\x02\x00\x00\x00\x02\x00\x03\x00\x04\x00\x05\x03\x00\x00\x00\x00\x00\x00e\x02\x00' \
0129             b'\x00\x00\x00\x8a\x01\x00\x00\xfd\xff'
0130     elif ext == '.ape':
0131         d = b'MAC \x96\x0f\x00\x004\x00\x00\x00\x18\x00\x00\x00\x04\x00\x00\x00,\x00\x00\x00\x10\x00\x00\x00\x00\x00' \
0132             b'\x00\x00\x00\x00\x00\x00@\xd6\x946\xc8\xdd\xe3Q\x83\x89\xf63GuM\xef\xd0\x07\x00\x00\x00 \x01\x00\x01' \
0133             b'\x00\x00\x00\x01\x00\x00\x00\x10\x00\x01\x00D\xac\x00\x00|\x00\x00\x00RIFF&\x00\x00\x00WAVEfmt \x10' \
0134             b'\x00\x00\x00\x01\x00\x01\x00D\xac\x00\x00\x88X\x01\x00\x02\x00\x10\x00data\x02\x00\x00\x00\x7f\x89\xec' \
0135             b'\xa0\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00'
0136     elif ext == '.wav':
0137         d = b'RIFFL\x01\x00\x00WAVEfmt \x10\x00\x00\x00\x01\x00\x02\x00D\xac\x00\x00\x10\xb1\x02\x00\x04\x00\x10' \
0138             b'\x00data(\x01\x00\x00\xd0\xe5l\xe5x\xe5X\xe5\x19\xe5;\xe5\xb6\xe4\x1a\xe5^\xe4\xff\xe4\x06\xe4\xfe' \
0139             b'\xe4\xbf\xe3\xef\xe4T\xe3\xed\xe4\xf9\xe2\xc7\xe4\x91\xe2\xb2\xe44\xe2\x8a\xe4\xc6\xe1f\xe4d\xe1=' \
0140             b'\xe4\xf0\xe0\x0b\xe4\x88\xe0\xe0\xe3<\xe0\xb2\xe3\x07\xe0\x8b\xe3\xd4\xdfW\xe3\xb5\xdf2\xe3\xb9\xdf' \
0141             b'\x06\xe3\xd2\xdf\x03\xe3\xf5\xdf\xf5\xe2!\xe0\xee\xe2R\xe0\xdb\xe2\x96\xe0\xdd\xe2\xc0\xe0\xe7\xe2' \
0142             b'\xec\xe0\xed\xe2\r\xe1\xea\xe2:\xe1\xf8\xe2X\xe1\x17\xe3s\xe11\xe3\x97\xe1N\xe3\xb3\xe1g\xe3\xd3\xe1' \
0143             b'\x93\xe3\xd7\xe1\xa6\xe3\xf9\xe1\xad\xe3\x01\xe2\xb4\xe3\x1e\xe2\xb8\xe3&\xe2\xbd\xe3F\xe2\xb5\xe3Z' \
0144             b'\xe2\xa3\xe3x\xe2\x92\xe3\x97\xe2v\xe3\xb3\xe2w\xe3\xcb\xe2h\xe3\xe8\xe2b\xe3\x0b\xe3O\xe3\x1b\xe3E' \
0145             b'\xe3-\xe3H\xe3,\xe3C\xe3H\xe3:\xe3;\xe3Q\xe3I\xe3Z\xe3Q\xe3\x84\xe3q\xe3\x9c\xe3\x94\xe3\xdf\xe3\xb5' \
0146             b'\xe3#\xe4\xe2\xe3f\xe4\x0f\xe4\xb0\xe4O\xe4!\xe5o\xe4\x94\xe5\xa3\xe4\x07\xe6\xbc\xe4X\xe6\xd2\xe4' \
0147             b'\xc3\xe6\xd6\xe4\x18\xe7\xf1\xe4\x83\xe7\t\xe5\xed\xe7 \xe5_\xe88\xe5\xce\xe8r\xe5C\xe9\xae\xe5\xc9' \
0148             b'\xe9\xe9\xe5o\xea(\xe6\x06\xeb\x82\xe6\xac\xeb'
0149     elif ext == '.opus':
0150         d = b'OggS\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x91d\x87S\x00\x00\x00\x00\xfb\x1f\xdfC\x01\x13OpusHead' \
0151             b'\x01\x01d\x01D\xac\x00\x00\x00\x00\x00OggS\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x91d\x87S\x01\x00' \
0152             b'\x00\x002\xa1\x1d5\x01\x1bOpusTags\x0b\x00\x00\x00libopus 1.1\x00\x00\x00\x00OggS\x00\x04f\x01\x00\x00' \
0153             b'\x00\x00\x00\x00\x91d\x87S\x02\x00\x00\x00\xe6\xe1CE\x01\x03\xf8\xff\xfe'
0154     elif ext == '.mpc':
0155         d = b'MPCKSH\x0cp\n\xc9\xab\x08\x01\x00\x1b\x0bRG\x0c\x01\x00\x00\x00\x00\x00\x00\x00\x00EI\x07\xa0\x01\x1e' \
0156             b'\x01SO\x08\x0c\x00\x00\x00\x00AP\x04\x00ST\x06\x01\x12\xb0SE\x03'
0157     elif ext == '.aif':
0158         d = b'FORM\x00\x00\x01VAIFFCOMM\x00\x00\x00\x12\x00\x02\x00\x00\x00J\x00\x10@\x0e\xacD\x00\x00\x00\x00\x00' \
0159             b'\x00SSND\x00\x00\x010\x00\x00\x00\x00\x00\x00\x00\x00\xe5\xd0\xe5l\xe5x\xe5X\xe5\x19\xe5;\xe4\xb6\xe5' \
0160             b'\x1a\xe4^\xe4\xff\xe4\x06\xe4\xfe\xe3\xbf\xe4\xef\xe3T\xe4\xed\xe2\xf9\xe4\xc7\xe2\x91\xe4\xb2\xe24' \
0161             b'\xe4\x8a\xe1\xc6\xe4f\xe1d\xe4=\xe0\xf0\xe4\x0b\xe0\x88\xe3\xe0\xe0<\xe3\xb2\xe0\x07\xe3\x8b\xdf\xd4' \
0162             b'\xe3W\xdf\xb5\xe32\xdf\xb9\xe3\x06\xdf\xd2\xe3\x03\xdf\xf5\xe2\xf5\xe0!\xe2\xee\xe0R\xe2\xdb\xe0\x96' \
0163             b'\xe2\xdd\xe0\xc0\xe2\xe7\xe0\xec\xe2\xed\xe1\r\xe2\xea\xe1:\xe2\xf8\xe1X\xe3\x17\xe1s\xe31\xe1\x97' \
0164             b'\xe3N\xe1\xb3\xe3g\xe1\xd3\xe3\x93\xe1\xd7\xe3\xa6\xe1\xf9\xe3\xad\xe2\x01\xe3\xb4\xe2\x1e\xe3\xb8' \
0165             b'\xe2&\xe3\xbd\xe2F\xe3\xb5\xe2Z\xe3\xa3\xe2x\xe3\x92\xe2\x97\xe3v\xe2\xb3\xe3w\xe2\xcb\xe3h\xe2\xe8' \
0166             b'\xe3b\xe3\x0b\xe3O\xe3\x1b\xe3E\xe3-\xe3H\xe3,\xe3C\xe3H\xe3:\xe3;\xe3Q\xe3I\xe3Z\xe3Q\xe3\x84\xe3q' \
0167             b'\xe3\x9c\xe3\x94\xe3\xdf\xe3\xb5\xe4#\xe3\xe2\xe4f\xe4\x0f\xe4\xb0\xe4O\xe5!\xe4o\xe5\x94\xe4\xa3\xe6' \
0168             b'\x07\xe4\xbc\xe6X\xe4\xd2\xe6\xc3\xe4\xd6\xe7\x18\xe4\xf1\xe7\x83\xe5\t\xe7\xed\xe5 \xe8_\xe58\xe8' \
0169             b'\xce\xe5r\xe9C\xe5\xae\xe9\xc9\xe5\xe9\xeao\xe6(\xeb\x06\xe6\x82\xeb\xac'
0170     elif ext == '.jpg':
0171         d = b'\xff\xd8\xff\xdb\x00C\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff' \
0172             b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff' \
0173             b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xdb\x00C\x01\xff' \
0174             b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff' \
0175             b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff' \
0176             b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc2\x00\x11\x08\x00\n\x00\n\x03\x01"\x00\x02' \
0177             b'\x11\x01\x03\x11\x01\xff\xc4\x00\x15\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' \
0178             b'\x00\x00\x01\xff\xc4\x00\x15\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' \
0179             b'\x02\xff\xda\x00\x0c\x03\x01\x00\x02\x10\x03\x10\x00\x00\x01\x80\xaf\xff\xc4\x00\x14\x10\x01\x00\x00' \
0180             b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \xff\xda\x00\x08\x01\x01\x00\x01\x05\x02\x1f\xff' \
0181             b'\xc4\x00\x14\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08' \
0182             b'\x01\x03\x01\x01?\x01\x7f\xff\xc4\x00\x14\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' \
0183             b'\x00\x00\x00\xff\xda\x00\x08\x01\x02\x01\x01?\x01\x7f\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00' \
0184             b'\x00\x00\x00\x00\x00\x00\x00\x00\x00 \xff\xda\x00\x08\x01\x01\x00\x06?\x02\x1f\xff\xc4\x00\x14\x10\x01' \
0185             b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \xff\xda\x00\x08\x01\x01\x00\x01?!\x1f' \
0186             b'\xff\xda\x00\x0c\x03\x01\x00\x02\x00\x03\x00\x00\x00\x10\x0b\xff\xc4\x00\x14\x11\x01\x00\x00\x00\x00' \
0187             b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x03\x01\x01?\x10\x7f\xff\xc4\x00' \
0188             b'\x14\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x02' \
0189             b'\x01\x01?\x10\x7f\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 ' \
0190             b'\xff\xda\x00\x08\x01\x01\x00\x01?\x10\x1f\xff\xd9'
0191     else:
0192         d = b''
0193     with open(filename, 'wb') as fh:
0194         fh.write(d)