File indexing completed on 2024-05-05 04:55:40
0001 #!/usr/bin/env python3 0002 # -*- coding: utf-8 -*- 0003 import unittest 0004 import sys 0005 import os 0006 import subprocess 0007 import tempfile 0008 import platform 0009 import json 0010 from kid3testsupport import kid3_cli_path, call_kid3_cli, create_test_file 0011 0012 0013 def setUpModule(): 0014 # Use an invalid config file to use a default configuration. 0015 os.environ['KID3_CONFIG_FILE'] = '' 0016 # Use English locale. 0017 os.environ['LANGUAGE'] = 'en_US.UTF-8' 0018 os.environ['LANG'] = 'en_US.UTF-8' 0019 0020 0021 class CliFunctionsTestCase(unittest.TestCase): 0022 def test_help(self): 0023 full_help = call_kid3_cli('-h') 0024 self.assertIn('Available Commands', full_help) 0025 self.assertRegex(full_help, r'help \[S\]\s+Help') 0026 self.assertRegex(full_help, r'^kid3-cli (\d\.\d\.\d|git\d{8}) \(c\) \d{4} Urs Fleisch') 0027 full_help = call_kid3_cli(['-c', 'help']) 0028 self.assertIn('Available Commands', full_help) 0029 self.assertRegex(full_help, r'help \[S\]\s+Help') 0030 self.assertTrue(full_help.startswith('Parameter\n P = File path')) 0031 self.assertRegex(full_help, r'playlist\s+Create playlist') 0032 self.assertRegex(call_kid3_cli(['-c', 'help paste']), r'paste \[T\]\s+Paste') 0033 0034 def test_invalid(self): 0035 p = subprocess.Popen([kid3_cli_path(), '-c', 'invalid'], 0036 stdout=subprocess.PIPE, stderr=subprocess.PIPE) 0037 stdout, stderr = p.communicate() 0038 self.assertEqual(stdout, b'') 0039 self.assertIn(b"Unknown command 'invalid', -h for help.", stderr) 0040 self.assertEqual(p.returncode, 1) 0041 0042 def test_timeout(self): 0043 self.assertEqual(call_kid3_cli( 0044 ['-c', 'timeout', '-c', 'timeout 5000', '-c', 'timeout', 0045 '-c', 'timeout off', '-c', 'timeout', 0046 '-c', 'timeout default', '-c', 'timeout']), 0047 'Timeout: default\n' 0048 'Timeout: 5000 ms\n' 0049 'Timeout: 5000 ms\n' 0050 'Timeout: off\n' 0051 'Timeout: off\n' 0052 'Timeout: default\n' 0053 'Timeout: default\n') 0054 0055 def test_exit(self): 0056 self.assertEqual(call_kid3_cli(['-c', 'exit']), '') 0057 0058 def test_ls(self): 0059 with tempfile.TemporaryDirectory() as tmpdir: 0060 self.assertEqual(call_kid3_cli(['-c', 'ls', tmpdir]), '') 0061 create_test_file(os.path.join(tmpdir, 'test.mp3')) 0062 self.assertEqual(call_kid3_cli(['-c', 'ls', tmpdir]), ' --- test.mp3\n') 0063 0064 def test_id3v1(self): 0065 with tempfile.TemporaryDirectory() as tmpdir: 0066 mp3path = os.path.join(tmpdir, 'test.mp3') 0067 create_test_file(mp3path) 0068 with open(mp3path, 'rb') as mp3fh: 0069 empty_mp3_bytes = mp3fh.read() 0070 self.assertEqual(call_kid3_cli( 0071 ['-c', 'get title 1', 0072 '-c', 'set title "A Title" 1', 0073 '-c', 'get title 1', 0074 '-c', 'get all 1', 0075 '-c', 'save', mp3path]), 0076 'A Title\n' 0077 'File: MPEG 1 Layer 3 64 kbps 44100 Hz 1 Channels\n' 0078 ' Name: test.mp3\n' 0079 'Tag 1: ID3v1.1\n' 0080 '* Title A Title\n') 0081 self.assertEqual(call_kid3_cli( 0082 ['-c', 'set artist "An Artist" 1', 0083 '-c', 'set album "An Album" 1', 0084 '-c', 'set track 5 1', 0085 '-c', 'set genre Metal 1', 0086 '-c', 'set date 2016 1', 0087 '-c', 'set comment "A Comment" 1', 0088 '-c', 'get all 1', mp3path]), 0089 'File: MPEG 1 Layer 3 64 kbps 44100 Hz 1 Channels\n' 0090 ' Name: test.mp3\nTag 1: ID3v1.1\n' 0091 ' Title A Title\n' 0092 '* Artist An Artist\n' 0093 '* Album An Album\n' 0094 '* Comment A Comment\n' 0095 '* Date 2016\n' 0096 '* Track Number 5\n' 0097 '* Genre Metal\n') 0098 with open(mp3path, 'rb') as mp3fh: 0099 ba = mp3fh.read() 0100 self.assertEqual( 0101 ba[-128:], 0102 b'TAGA ' 0103 b'Title\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 0104 b'An Artist\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 0105 b'An Album\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 0106 b'2016' 0107 b'A Comment\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\t') 0108 self.assertEqual(call_kid3_cli( 0109 ['-c', 'copy 1', 0110 '-c', 'remove 1', 0111 '-c', 'get all 1', 0112 '-c', 'paste 1', 0113 '-c', 'get all 1', 0114 '-c', 'set track 6 1', 0115 '-c', 'get all 1', 0116 '-c', 'revert', 0117 '-c', 'get all 1', 0118 '-c', 'remove 1', mp3path]), 0119 'File: MPEG 1 Layer 3 64 kbps 44100 Hz 1 Channels\n' 0120 ' Name: test.mp3\n' 0121 'File: MPEG 1 Layer 3 64 kbps 44100 Hz 1 Channels\n' 0122 ' Name: test.mp3\n' 0123 'Tag 1: ID3v1.1\n' 0124 '* Title A Title\n' 0125 '* Artist An Artist\n' 0126 '* Album An Album\n' 0127 '* Comment A Comment\n' 0128 '* Date 2016\n' 0129 '* Track Number 5\n' 0130 '* Genre Metal\n' 0131 'File: MPEG 1 Layer 3 64 kbps 44100 Hz 1 Channels\n' 0132 ' Name: test.mp3\nTag 1: ID3v1.1\n' 0133 '* Title A Title\n' 0134 '* Artist An Artist\n' 0135 '* Album An Album\n' 0136 '* Comment A Comment\n' 0137 '* Date 2016\n' 0138 '* Track Number 6\n' 0139 '* Genre Metal\n' 0140 'File: MPEG 1 Layer 3 64 kbps 44100 Hz 1 Channels\n' 0141 ' Name: test.mp3\n' 0142 'Tag 1: ID3v1.1\n' 0143 ' Title A Title\n' 0144 ' Artist An Artist\n' 0145 ' Album An Album\n' 0146 ' Comment A Comment\n' 0147 ' Date 2016\n' 0148 ' Track Number 5\n' 0149 ' Genre Metal\n') 0150 with open(mp3path, 'rb') as mp3fh: 0151 ba = mp3fh.read() 0152 self.assertEqual(ba, empty_mp3_bytes) 0153 0154 def test_id3v2(self): 0155 with tempfile.TemporaryDirectory() as tmpdir: 0156 mp3path = os.path.join(tmpdir, 'test.mp3') 0157 jpgpath = os.path.join(tmpdir, 'test.jpg') 0158 picpath = os.path.join(tmpdir, 'folder.jpg') 0159 create_test_file(mp3path) 0160 create_test_file(jpgpath) 0161 with open(mp3path, 'rb') as mp3fh: 0162 empty_mp3_bytes = mp3fh.read() 0163 with open(jpgpath, 'rb') as jpgfh: 0164 jpg_bytes = jpgfh.read() 0165 self.assertEqual(call_kid3_cli( 0166 ['-c', 'get title 2', 0167 '-c', 'get all 2', mp3path]), 0168 'File: MPEG 1 Layer 3 64 kbps 44100 Hz 1 Channels\n' 0169 ' Name: test.mp3\n') 0170 self.assertEqual(call_kid3_cli( 0171 ['-c', 'set artist "An Artist" 2', 0172 '-c', 'set album "An Album" 2', 0173 '-c', 'set track 3 2', 0174 '-c', 'set genre "Power Metal" 2', 0175 '-c', 'set date 2016 2', 0176 '-c', 'set comment "A Comment" 2', 0177 '-c', 'set Lyricist "A Lyricist" 2', 0178 '-c', 'set TDLY 100 2', 0179 '-c', 'set picture:"%s" "A Description" 2' % jpgpath, 0180 '-c', 'set comment "A Comment" 2', 0181 '-c', 'get all 2', mp3path]), 0182 'File: MPEG 1 Layer 3 64 kbps 44100 Hz 1 Channels\n' 0183 ' Name: test.mp3\n' 0184 'Tag 2: ID3v2.3.0\n' 0185 '* Artist An Artist\n' 0186 '* Album An Album\n' 0187 '* Comment A Comment\n' 0188 '* Date 2016\n' 0189 '* Track Number 3\n' 0190 '* Genre Power Metal\n' 0191 '* Lyricist A Lyricist\n' 0192 '* Picture: Cover (front) A Description\n' 0193 '* Playlist Delay 100\n') 0194 self.assertEqual(call_kid3_cli( 0195 ['-c', 'copy 2', 0196 '-c', 'remove 2', 0197 '-c', 'get all 2', 0198 '-c', 'paste 2', 0199 '-c', 'get all 2', 0200 '-c', 'set track 6 2', 0201 '-c', 'syncto 1', 0202 '-c', 'get all', 0203 '-c', 'revert', 0204 '-c', 'get all', 0205 '-c', 'to24', 0206 '-c', 'get all 2', 0207 '-c', 'to23', 0208 '-c', 'get picture:"%s" "A Description" 2' % picpath, 0209 '-c', 'get all 2', 0210 '-c', 'remove 2', mp3path]), 0211 'File: MPEG 1 Layer 3 64 kbps 44100 Hz 1 Channels\n' 0212 ' Name: test.mp3\n' 0213 'File: MPEG 1 Layer 3 64 kbps 44100 Hz 1 Channels\n' 0214 ' Name: test.mp3\n' 0215 'Tag 2: ID3v2.3.0\n' 0216 '* Artist An Artist\n' 0217 '* Album An Album\n' 0218 '* Comment A Comment\n' 0219 '* Date 2016\n' 0220 '* Track Number 3\n' 0221 '* Genre Power Metal\n' 0222 '* Lyricist A Lyricist\n' 0223 '* Picture: Cover (front) A Description\n' 0224 '* Playlist Delay 100\n' 0225 'File: MPEG 1 Layer 3 64 kbps 44100 Hz 1 Channels\n' 0226 ' Name: test.mp3\n' 0227 'Tag 1: ID3v1.1\n' 0228 '* Artist An Artist\n' 0229 '* Album An Album\n' 0230 '* Comment A Comment\n' 0231 '* Date 2016\n' 0232 '* Track Number 6\n' 0233 'Tag 2: ID3v2.3.0\n' 0234 '* Artist An Artist\n' 0235 '* Album An Album\n' 0236 '* Comment A Comment\n' 0237 '* Date 2016\n' 0238 '* Track Number 6\n' 0239 '* Genre Power Metal\n' 0240 '* Lyricist A Lyricist\n' 0241 '* Picture: Cover (front) A Description\n' 0242 '* Playlist Delay 100\n' 0243 'File: MPEG 1 Layer 3 64 kbps 44100 Hz 1 Channels\n' 0244 ' Name: test.mp3\n' 0245 'Tag 2: ID3v2.3.0\n' 0246 ' Artist An Artist\n' 0247 ' Album An Album\n' 0248 ' Comment A Comment\n' 0249 ' Date 2016\n' 0250 ' Track Number 3\n' 0251 ' Genre Power Metal\n' 0252 ' Lyricist A Lyricist\n' 0253 ' Picture: Cover (front) A Description\n' 0254 ' Playlist Delay 100\n' 0255 'File: MPEG 1 Layer 3 64 kbps 44100 Hz 1 Channels\n' 0256 ' Name: test.mp3\n' 0257 'Tag 2: ID3v2.4.0\n' 0258 ' Artist An Artist\n' 0259 ' Album An Album\n' 0260 ' Comment A Comment\n' 0261 ' Date 2016\n' 0262 ' Track Number 3\n' 0263 ' Genre Power Metal\n' 0264 ' Lyricist A Lyricist\n' 0265 ' Picture: Cover (front) A Description\n' 0266 ' Playlist Delay 100\n' 0267 'A Description\n' 0268 'File: MPEG 1 Layer 3 64 kbps 44100 Hz 1 Channels\n' 0269 ' Name: test.mp3\n' 0270 'Tag 2: ID3v2.3.0\n' 0271 ' Artist An Artist\n' 0272 ' Album An Album\n' 0273 ' Comment A Comment\n' 0274 ' Date 2016\n' 0275 ' Track Number 3\n' 0276 ' Genre Power Metal\n' 0277 ' Lyricist A Lyricist\n' 0278 ' Picture: Cover (front) A Description\n' 0279 ' Playlist Delay 100\n') 0280 with open(mp3path, 'rb') as mp3fh: 0281 ba = mp3fh.read() 0282 self.assertEqual(ba, empty_mp3_bytes) 0283 with open(picpath, 'rb') as jpgfh: 0284 ba = jpgfh.read() 0285 self.assertEqual(ba, jpg_bytes) 0286 self.assertEqual(call_kid3_cli( 0287 ['-c', 'set POPM 5', 0288 '-c', 'get all 2', 0289 '-c', 'get POPM.Email', 0290 '-c', 'get POPM.Rating', 0291 '-c', 'get POPM.Counter', 0292 '-c', 'set POPM.Email ufleisch@users.sourceforge.net', 0293 '-c', 'set POPM.Rating 4', 0294 '-c', 'set POPM.Counter 3', 0295 '-c', 'get all 2', 0296 '-c', 'get POPM.Email', 0297 '-c', 'get POPM.Rating', 0298 '-c', 'get POPM.Counter', 0299 '-c', 'get POPM', mp3path]), 0300 'File: MPEG 1 Layer 3 64 kbps 44100 Hz 1 Channels\n' 0301 ' Name: test.mp3\n' 0302 'Tag 2: ID3v2.3.0\n' 0303 '* Rating 5\n' 0304 '\n' 0305 '5\n' 0306 '0\n' 0307 'File: MPEG 1 Layer 3 64 kbps 44100 Hz 1 Channels\n' 0308 ' Name: test.mp3\n' 0309 'Tag 2: ID3v2.3.0\n' 0310 '* Rating 4\n' 0311 'ufleisch@users.sourceforge.net\n' 0312 '4\n' 0313 '3\n' 0314 '4\n') 0315 0316 def test_flac(self): 0317 with tempfile.TemporaryDirectory() as tmpdir: 0318 flacpath = os.path.join(tmpdir, 'test.flac') 0319 jpgpath = os.path.join(tmpdir, 'test.jpg') 0320 picpath = os.path.join(tmpdir, 'folder.jpg') 0321 create_test_file(flacpath) 0322 create_test_file(jpgpath) 0323 with open(jpgpath, 'rb') as jpgfh: 0324 jpg_bytes = jpgfh.read() 0325 self.assertRegex(call_kid3_cli( 0326 ['-c', 'get title 2', 0327 '-c', 'get all 2', flacpath]), 0328 'File: FLAC [^\\n]+ kbps 44100 Hz 1 Channels\n' 0329 ' Name: test\\.flac\n') 0330 self.assertRegex(call_kid3_cli( 0331 ['-c', 'set artist "A first artist"', 0332 '-c', 'set artist[1] "A second artist"', 0333 '-c', 'set album "Album Name"', 0334 '-c', 'set track 4', 0335 '-c', 'set genre "Heavy Metal"', 0336 '-c', 'set date 2017', 0337 '-c', 'set comment[0] "Comment 1"', 0338 '-c', 'set comment[1] "Comment 2"', 0339 '-c', 'set comment[2] "Comment 3"', 0340 '-c', 'set Lyricist "A Lyricist" 2', 0341 '-c', 'set tracktotal 12', 0342 '-c', 'set "disc number" 1', 0343 '-c', 'set DISCTOTAL 2', 0344 '-c', 'set picture:"%s" "Picture Description"' % jpgpath, 0345 '-c', 'get comment[1]', 0346 '-c', 'get comment', 0347 '-c', 'get', flacpath]), 0348 'Comment 2\n' 0349 'Comment 1\n' 0350 'File: FLAC [^\\n]+ kbps 44100 Hz 1 Channels\n' 0351 ' Name: test\\.flac\n' 0352 'Tag 2: Vorbis\n' 0353 '\\* Artist A first artist\n' 0354 '\\* Artist A second artist\n' 0355 '\\* Album Album Name\n' 0356 '\\* Comment Comment 1\n' 0357 '\\* Comment Comment 2\n' 0358 '\\* Comment Comment 3\n' 0359 '\\* Date 2017\n' 0360 '\\* Track Number 4\n' 0361 '\\* Genre Heavy Metal\n' 0362 '\\* Disc Number 1\n' 0363 '\\* Lyricist A Lyricist\n' 0364 '\\* Picture: Cover \\(front\\) Picture Description\n' 0365 '\\* Total Discs 2\n' 0366 '\\* Total Tracks 12\n') 0367 self.assertRegex(call_kid3_cli( 0368 ['-c', 'set artist[1] ""', 0369 '-c', 'set comment[1] "Comment B"', 0370 '-c', 'set comment[0] "Comment A"', 0371 '-c', 'set comment[2] "Comment C"', 0372 '-c', 'set lyricist ""', 0373 '-c', 'set picture[1]:"%s" "Back Cover"' % jpgpath, 0374 '-c', 'set picture[1].picturetype 4', 0375 '-c', 'set picture[0].description "Front Cover"', 0376 '-c', 'get picture[1]:"%s"' % picpath, 0377 '-c', 'get', 0378 '-c', 'remove', 0379 '-c', 'get', flacpath]), 0380 'Back Cover\n' 0381 'File: FLAC [^\\n]+ kbps 44100 Hz 1 Channels\n' 0382 ' Name: test\\.flac\n' 0383 'Tag 2: Vorbis\n' 0384 '\\* Artist A first artist\n' 0385 ' Album Album Name\n' 0386 '\\* Comment Comment A\n' 0387 '\\* Comment Comment B\n' 0388 '\\* Comment Comment C\n' 0389 ' Date 2017\n' 0390 ' Track Number 4\n' 0391 ' Genre Heavy Metal\n' 0392 ' Disc Number 1\n' 0393 '\\* Picture: Cover \\(front\\) Front Cover\n' 0394 '\\* Picture: Cover \\(back\\) Back Cover\n' 0395 ' Total Discs 2\n' 0396 ' Total Tracks 12\n' 0397 'File: FLAC [^\\n]+ kbps 44100 Hz 1 Channels\n' 0398 ' Name: test\\.flac\n') 0399 with open(picpath, 'rb') as jpgfh: 0400 ba = jpgfh.read() 0401 self.assertEqual(ba, jpg_bytes) 0402 0403 def test_frame_selection(self): 0404 with tempfile.TemporaryDirectory() as tmpdir: 0405 test1path = os.path.join(tmpdir, 'test1.mp3') 0406 test2path = os.path.join(tmpdir, 'test2.mp3') 0407 create_test_file(test1path) 0408 create_test_file(test2path) 0409 self.assertEqual(call_kid3_cli( 0410 ['-c', 'get all 2', test1path]), 0411 'File: MPEG 1 Layer 3 64 kbps 44100 Hz 1 Channels\n' 0412 ' Name: test1.mp3\n') 0413 self.assertEqual(call_kid3_cli( 0414 ['-c', 'get all 2', test2path]), 0415 'File: MPEG 1 Layer 3 64 kbps 44100 Hz 1 Channels\n' 0416 ' Name: test2.mp3\n') 0417 expected = ( 0418 'Tag 2:\n' 0419 ' Title ≠\n' 0420 ' Artist An Artist\n' 0421 ' Album An Album\n' 0422 ' Copyright ≠\n' 0423 ' Disc Number ≠\n') 0424 if sys.platform == 'win32': 0425 expected = expected.replace('≠', '?') 0426 self.assertEqual(call_kid3_cli( 0427 ['-c', 'select all', 0428 '-c', 'set artist "An Artist"', 0429 '-c', 'set album "An Album"', 0430 '-c', 'select none', 0431 '-c', 'select first', 0432 '-c', 'set title "Title 1"', 0433 '-c', 'set discnumber "1/2"', 0434 '-c', 'set copyright "2017 Kid3"', 0435 '-c', 'select next', 0436 '-c', 'set title "Title 2"', 0437 '-c', 'save', 0438 '-c', 'select all', 0439 '-c', 'get', tmpdir]), 0440 expected) 0441 self.assertEqual(call_kid3_cli( 0442 ['-c', 'select first', 0443 '-c', 'get', 0444 '-c', 'set "*.selected" 0', 0445 '-c', 'set "album.selected" 1', 0446 '-c', 'remove', 0447 '-c', 'get', 0448 '-c', 'revert', 0449 '-c', 'set "*.selected" 0', 0450 '-c', 'set "discnumber.selected" 1', 0451 '-c', 'set "copyright.selected" 1', 0452 '-c', 'copy', 0453 '-c', 'select next', 0454 '-c', 'paste', 0455 '-c', 'get', 0456 '-c', 'revert', 0457 '-c', 'select all', 0458 '-c', 'set "*.selected" 0', 0459 '-c', 'set "discnumber.selected" 1', 0460 '-c', 'paste', 0461 '-c', 'select first', 0462 '-c', 'get', 0463 '-c', 'select next', 0464 '-c', 'get', tmpdir]), 0465 'File: MPEG 1 Layer 3 64 kbps 44100 Hz 1 Channels\n' 0466 ' Name: test1.mp3\n' 0467 'Tag 2: ID3v2.3.0\n' 0468 ' Title Title 1\n' 0469 ' Artist An Artist\n' 0470 ' Album An Album\n' 0471 ' Copyright 2017 Kid3\n' 0472 ' Disc Number 1/2\n' 0473 'File: MPEG 1 Layer 3 64 kbps 44100 Hz 1 Channels\n' 0474 ' Name: test1.mp3\n' 0475 'Tag 2: ID3v2.3.0\n' 0476 ' Title Title 1\n' 0477 ' Artist An Artist\n' 0478 ' Copyright 2017 Kid3\n' 0479 ' Disc Number 1/2\n' 0480 'File: MPEG 1 Layer 3 64 kbps 44100 Hz 1 Channels\n' 0481 ' Name: test2.mp3\n' 0482 'Tag 2: ID3v2.3.0\n' 0483 ' Title Title 2\n' 0484 ' Artist An Artist\n' 0485 ' Album An Album\n' 0486 '* Copyright 2017 Kid3\n' 0487 '* Disc Number 1/2\n' 0488 'File: MPEG 1 Layer 3 64 kbps 44100 Hz 1 Channels\n' 0489 ' Name: test1.mp3\n' 0490 'Tag 2: ID3v2.3.0\n' 0491 ' Title Title 1\n' 0492 ' Artist An Artist\n' 0493 ' Album An Album\n' 0494 ' Copyright 2017 Kid3\n' 0495 ' Disc Number 1/2\n' 0496 'File: MPEG 1 Layer 3 64 kbps 44100 Hz 1 Channels\n' 0497 ' Name: test2.mp3\n' 0498 'Tag 2: ID3v2.3.0\n' 0499 ' Title Title 2\n' 0500 ' Artist An Artist\n' 0501 ' Album An Album\n' 0502 '* Disc Number 1/2\n') 0503 0504 def test_riff_info(self): 0505 with tempfile.TemporaryDirectory() as tmpdir: 0506 wavpath = os.path.join(tmpdir, 'test.wav') 0507 create_test_file(wavpath) 0508 with open(wavpath, 'rb') as wavfh: 0509 empty_wav_bytes = wavfh.read() 0510 self.assertRegex(call_kid3_cli( 0511 ['-c', 'get title 3', 0512 '-c', 'get all 3', wavpath]), 0513 'File: WAV (?:PCM 16 bit )?1411 kbps 44100 Hz 2 Channels\n' 0514 ' Name: test\\.wav\n') 0515 self.assertRegex(call_kid3_cli( 0516 ['-c', 'set artist "An Artist" 3', 0517 '-c', 'set album "An Album" 3', 0518 '-c', 'set tracknumber 2 3', 0519 '-c', 'set genre "Heavy Metal" 3', 0520 '-c', 'set date 2017 3', 0521 '-c', 'set comment "A Comment" 3', 0522 '-c', 'set bpm "120" 3', 0523 '-c', 'set comment "A Comment" 3', 0524 '-c', 'get all 3', wavpath]), 0525 'File: WAV (?:PCM 16 bit )?1411 kbps 44100 Hz 2 Channels\n' 0526 ' Name: test\\.wav\n' 0527 '(?:Tag 3: RIFF INFO\n' 0528 '\\* Artist An Artist\n' 0529 '\\* Album An Album\n' 0530 '\\* Comment A Comment\n' 0531 '\\* Date 2017\n' 0532 '\\* Track Number 2\n' 0533 '\\* Genre Heavy Metal\n' 0534 '\\* BPM 120\n)?') 0535 self.assertRegex(call_kid3_cli( 0536 ['-c', 'copy 3', 0537 '-c', 'remove 3', 0538 '-c', 'get all 3', 0539 '-c', 'paste 3', 0540 '-c', 'get all 3', 0541 '-c', 'set tracknumber 6 3', 0542 '-c', 'get all 3', 0543 '-c', 'set comment "" 3', 0544 '-c', 'get all 3', 0545 '-c', 'remove 3', wavpath]), 0546 'File: WAV (?:PCM 16 bit )?1411 kbps 44100 Hz 2 Channels\n' 0547 ' Name: test\\.wav\n' 0548 'File: WAV (?:PCM 16 bit )?1411 kbps 44100 Hz 2 Channels\n' 0549 ' Name: test\\.wav\n' 0550 '(?:Tag 3: RIFF INFO\n' 0551 '\\* Artist An Artist\n' 0552 '\\* Album An Album\n' 0553 '\\* Comment A Comment\n' 0554 '\\* Date 2017\n' 0555 '\\* Track Number 2\n' 0556 '\\* Genre Heavy Metal\n' 0557 '\\* BPM 120\n)?' 0558 'File: WAV (?:PCM 16 bit )?1411 kbps 44100 Hz 2 Channels\n' 0559 ' Name: test\\.wav\n' 0560 '(?:Tag 3: RIFF INFO\n' 0561 '\\* Artist An Artist\n' 0562 '\\* Album An Album\n' 0563 '\\* Comment A Comment\n' 0564 '\\* Date 2017\n' 0565 '\\* Track Number 6\n' 0566 '\\* Genre Heavy Metal\n' 0567 '\\* BPM 120\n)?' 0568 'File: WAV (?:PCM 16 bit )?1411 kbps 44100 Hz 2 Channels\n' 0569 ' Name: test\\.wav\n' 0570 '(?:Tag 3: RIFF INFO\n' 0571 '\\* Artist An Artist\n' 0572 '\\* Album An Album\n' 0573 '\\* Date 2017\n' 0574 '\\* Track Number 6\n' 0575 '\\* Genre Heavy Metal\n' 0576 '\\* BPM 120\n)?') 0577 with open(wavpath, 'rb') as wavfh: 0578 ba = wavfh.read() 0579 self.assertEqual(ba, empty_wav_bytes) 0580 0581 def test_multiple_files(self): 0582 with tempfile.TemporaryDirectory() as tmpdir: 0583 extensions = ('m4a', 'flac', 'spx', 'mp3', 'ape', 'wav', 'opus', 'aif') 0584 for nr, ext in enumerate(extensions): 0585 create_test_file(os.path.join(tmpdir, 'track%02d.%s' % (nr, ext))) 0586 import_csv = \ 0587 '01\tWheels Of Fire\tManowar\tKings Of Metal\t1988\tMetal\t\t0:00.00\n' \ 0588 '02\tKings Of Metal\tManowar\tKings Of Metal\t1988\tMetal\t\t0:00.00\n' \ 0589 '03\tHeart Of Steel\tManowar\tKings Of Metal\t1988\tMetal\t\t0:00.00\n' \ 0590 '05\tThe Crown And The Ring (Lament Of The Kings)\tManowar\tKings Of Metal\t1988\tMetal\t\t0:00.00\n' \ 0591 '06\tKingdom Come\tManowar\tKings Of Metal\t1988\tMetal\t\t0:00.00\n' \ 0592 '08\tHail And Kill\tManowar\tKings Of Metal\t1988\tMetal\t\t0:00.00\n' \ 0593 '09\tThe Warriors Prayer\tManowar\tKings Of Metal\t1988\tMetal\t\t0:00.00\n' \ 0594 '10\tBlood Of The Kings\tManowar\tKings Of Metal\t1988\tMetal\t\t0:00.00\n' 0595 importpath = os.path.join(tmpdir, 'import.csv') 0596 exportpath = os.path.join(tmpdir, 'export.csv') 0597 with open(importpath, 'w') as importfh: 0598 importfh.write(import_csv) 0599 self.assertEqual(call_kid3_cli( 0600 ['-c', 'import "%s" "CSV unquoted"' % importpath, 0601 '-c', 'export "%s" "CSV unquoted"' % exportpath, tmpdir]), '') 0602 with open(exportpath) as exportfh: 0603 export_csv = exportfh.read() 0604 self.assertEqual(export_csv, import_csv) 0605 os.remove(importpath) 0606 os.remove(exportpath) 0607 expected = ( 0608 'File: MP4 (?:AAC 16 bit )?9 kbps 44100 Hz 2 Channels\n' 0609 ' Name: track00\\.m4a\n' 0610 'Tag 2: MP4\n' 0611 ' Title Wheels Of Fire\n' 0612 ' Artist Manowar\n' 0613 ' Album Kings Of Metal\n' 0614 ' Date 1988\n' 0615 ' Track Number 1\n' 0616 ' Genre Metal\n' 0617 'File: FLAC [^\\n]+ kbps 44100 Hz 1 Channels\n' 0618 ' Name: track01\\.flac\n' 0619 '(?:Tag 1: ID3v1\\.1\n' 0620 ' Title Kings Of Metal\n' 0621 ' Artist Manowar\n' 0622 ' Album Kings Of Metal\n' 0623 ' Date 1988\n' 0624 ' Track Number 02\n' 0625 ' Genre Metal\n)?' 0626 'Tag 2: Vorbis\n' 0627 ' Title Kings Of Metal\n' 0628 ' Artist Manowar\n' 0629 ' Album Kings Of Metal\n' 0630 ' Date 1988\n' 0631 ' Track Number 02\n' 0632 ' Genre Metal\n' 0633 'File: Speex 1 44100 Hz 1 Channels\n' 0634 ' Name: track02\\.spx\n' 0635 'Tag 2: Vorbis\n' 0636 ' Title Heart Of Steel\n' 0637 ' Artist Manowar\n' 0638 ' Album Kings Of Metal\n' 0639 ' Date 1988\n' 0640 ' Track Number 03\n' 0641 ' Genre Metal\n' 0642 'File: MPEG 1 Layer 3 64 kbps 44100 Hz 1 Channels\n' 0643 ' Name: track03\\.mp3\n' 0644 'Tag 1: ID3v1\\.1\n' 0645 ' Title The Crown And The Ring \\(Lament\n' 0646 ' Artist Manowar\n' 0647 ' Album Kings Of Metal\n' 0648 ' Date 1988\n' 0649 ' Track Number 5\n' 0650 ' Genre Metal\n' 0651 'Tag 2: ID3v2\\.3\\.0\n' 0652 ' Title The Crown And The Ring \\(Lament Of The Kings\\)\n' 0653 ' Artist Manowar\n' 0654 ' Album Kings Of Metal\n' 0655 ' Date 1988\n' 0656 ' Track Number 05\n' 0657 ' Genre Metal\n' 0658 'File: APE 3\\.990 16 bit 44100 Hz 1 Channels\n' 0659 ' Name: track04\\.ape\n' 0660 'Tag 1: ID3v1\\.1\n' 0661 ' Title Kingdom Come\n' 0662 ' Artist Manowar\n' 0663 ' Album Kings Of Metal\n' 0664 ' Date 1988\n' 0665 ' Track Number 6\n' 0666 ' Genre Metal\n' 0667 'Tag 2: APE\n' 0668 ' Title Kingdom Come\n' 0669 ' Artist Manowar\n' 0670 ' Album Kings Of Metal\n' 0671 ' Date 1988\n' 0672 ' Track Number 06\n' 0673 ' Genre Metal\n' 0674 'File: WAV (?:PCM 16 bit )?1411 kbps 44100 Hz 2 Channels\n' 0675 ' Name: track05\\.wav\n' 0676 'Tag 2: ID3v2\\.3\\.0\n' 0677 ' Title Hail And Kill\n' 0678 ' Artist Manowar\n' 0679 ' Album Kings Of Metal\n' 0680 ' Date 1988\n' 0681 ' Track Number 08\n' 0682 ' Genre Metal\n' 0683 'File: Opus 1 48000 Hz 1 Channels\n' 0684 ' Name: track06\\.opus\n' 0685 'Tag 2: Vorbis\n' 0686 ' Title The Warriors Prayer\n' 0687 ' Artist Manowar\n' 0688 ' Album Kings Of Metal\n' 0689 ' Date 1988\n' 0690 ' Track Number 09\n' 0691 ' Genre Metal\n' 0692 'File: AIFF [^\\n]+ kbps 44100 Hz 2 Channels\n' 0693 ' Name: track07\\.aif\n' 0694 'Tag 2: ID3v2\\.4\\.0\n' 0695 ' Title Blood Of The Kings\n' 0696 ' Artist Manowar\n' 0697 ' Album Kings Of Metal\n' 0698 ' Date 1988\n' 0699 ' Track Number 10\n' 0700 ' Genre Metal\n' 0701 'Tag 1:\n' 0702 ' Title ≠\n' 0703 ' Artist Manowar\n' 0704 ' Album Kings Of Metal\n' 0705 ' Date 1988\n' 0706 ' Track Number ≠\n' 0707 ' Genre Metal\n' 0708 'Tag 2:\n' 0709 ' Title ≠\n' 0710 ' Artist Manowar\n' 0711 ' Album Kings Of Metal\n' 0712 ' Date 1988\n' 0713 ' Track Number ≠\n' 0714 ' Genre Metal\n') 0715 if sys.platform == 'win32': 0716 expected = expected.replace('≠', '\\?') 0717 self.assertRegex(call_kid3_cli( 0718 ['-c', 'select first', 0719 '-c', 'get', 0720 '-c', 'select next', 0721 '-c', 'get', 0722 '-c', 'select next', 0723 '-c', 'get', 0724 '-c', 'select next', 0725 '-c', 'get', 0726 '-c', 'select next', 0727 '-c', 'get', 0728 '-c', 'select next', 0729 '-c', 'get', 0730 '-c', 'select next', 0731 '-c', 'get', 0732 '-c', 'select next', 0733 '-c', 'get', 0734 '-c', 'select all', 0735 '-c', 'get', tmpdir]), 0736 expected) 0737 call_kid3_cli(['-c', 'fromtag "%{track} %{title}" 2', os.path.join(tmpdir, '*.*')]) 0738 self.assertRegex(call_kid3_cli( 0739 ['-c', 'ls', tmpdir]), 0740 ' -2- 01 Wheels Of Fire.m4a\n' 0741 ' [-1]2- 02 Kings Of Metal.flac\n' 0742 ' -2- 03 Heart Of Steel.spx\n' 0743 ' 12- 05 The Crown And The Ring \\(Lament Of The Kings\\).mp3\n' 0744 ' 12- 06 Kingdom Come.ape\n' 0745 ' -2- 08 Hail And Kill.wav\n' 0746 ' -2- 09 The Warriors Prayer.opus\n' 0747 ' -2- 10 Blood Of The Kings.aif\n') 0748 self.assertEqual(call_kid3_cli( 0749 ['-c', 'tag 2', 0750 '-c', 'numbertracks 101', 0751 '-c', 'select first', 0752 '-c', 'get track', 0753 '-c', 'select next', 0754 '-c', 'get track', 0755 '-c', 'select next', 0756 '-c', 'get track', 0757 '-c', 'select next', 0758 '-c', 'get track', 0759 '-c', 'select next', 0760 '-c', 'get track', 0761 '-c', 'select next', 0762 '-c', 'get track', 0763 '-c', 'select next', 0764 '-c', 'get track', 0765 '-c', 'select next', 0766 '-c', 'get track', 0767 '-c', 'revert', tmpdir 0768 ]), 0769 '101\n102\n103\n104\n105\n106\n107\n108\n') 0770 lines = call_kid3_cli( 0771 ['-c', 'filter "%{tag2} equals ID3v2.3.0"', tmpdir]).splitlines() 0772 self.assertEqual(lines[0], 'Started') 0773 self.assertTrue(os.path.samefile(lines[1].lstrip(), tmpdir)) 0774 self.assertEqual( 0775 lines[2:], 0776 ['- 01 Wheels Of Fire.m4a', '- 02 Kings Of Metal.flac', 0777 '- 03 Heart Of Steel.spx', '+ 05 The Crown And The Ring (Lament Of The Kings).mp3', 0778 '- 06 Kingdom Come.ape', '+ 08 Hail And Kill.wav', '- 09 The Warriors Prayer.opus', 0779 '- 10 Blood Of The Kings.aif', 'Finished']) 0780 call_kid3_cli(['-c', 'renamedir "%{artist} - [%{year}] %{album}" "create"', tmpdir]) 0781 new_dirname = 'Manowar - [1988] Kings Of Metal' 0782 new_dirpath = os.path.join(tmpdir, new_dirname) 0783 self.assertEqual(os.listdir(tmpdir)[0], new_dirname) 0784 call_kid3_cli(['-c', 'playlist', new_dirpath]) 0785 with open(os.path.join(new_dirpath, new_dirname + '.m3u')) as m3ufh: 0786 export_m3u = m3ufh.read() 0787 self.assertEqual( 0788 export_m3u, 0789 '01 Wheels Of Fire.m4a\n' 0790 '02 Kings Of Metal.flac\n' 0791 '03 Heart Of Steel.spx\n' 0792 '05 The Crown And The Ring (Lament Of The Kings).mp3\n' 0793 '06 Kingdom Come.ape\n' 0794 '08 Hail And Kill.wav\n' 0795 '09 The Warriors Prayer.opus\n' 0796 '10 Blood Of The Kings.aif\n') 0797 0798 def test_filename_tag_format(self): 0799 with tempfile.TemporaryDirectory() as tmpdir: 0800 albumdir = os.path.join(tmpdir, 'An Artist - 2016 - An Album') 0801 os.mkdir(albumdir) 0802 create_test_file(os.path.join(albumdir, '01. A title.mp3')) 0803 self.assertEqual(call_kid3_cli( 0804 ['-c', 'totag "%{artist} - %{date} - %{album}/%{track}. %{title}" 2', 0805 '-c', 'save', 0806 '-c', 'get', os.path.join(albumdir, '*.mp3')]), 0807 'File: MPEG 1 Layer 3 64 kbps 44100 Hz 1 Channels\n' 0808 ' Name: 01. A title.mp3\n' 0809 'Tag 2: ID3v2.3.0\n' 0810 ' Title A title\n' 0811 ' Artist An Artist\n' 0812 ' Album An Album\n' 0813 ' Date 2016\n' 0814 ' Track Number 1\n') 0815 self.assertEqual(call_kid3_cli( 0816 ['-c', 'select all', 0817 '-c', 'tagformat', 0818 '-c', 'save', 0819 '-c', 'get all', albumdir]), 0820 'File: MPEG 1 Layer 3 64 kbps 44100 Hz 1 Channels\n' 0821 ' Name: 01. A title.mp3\n' 0822 'Tag 2: ID3v2.3.0\n' 0823 ' Title A Title\n' 0824 ' Artist An Artist\n' 0825 ' Album An Album\n' 0826 ' Date 2016\n' 0827 ' Track Number 1\n') 0828 expected = ( 0829 'File: MPEG 1 Layer 3 64 kbps 44100 Hz 1 Channels\n' 0830 '* Name: 01. Schön.mp3\n' 0831 'Tag 2: ID3v2.3.0\n' 0832 '* Title Schön\n' 0833 ' Artist An Artist\n' 0834 ' Album An Album\n' 0835 ' Date 2016\n' 0836 ' Track Number 1\n' 0837 ) 0838 if sys.platform == 'win32' and platform.release() == 'XP': 0839 expected = expected.replace('ö', '?') 0840 self.assertEqual(call_kid3_cli( 0841 ['-c', 'select first', 0842 '-c', 'set title "Schön"', 0843 '-c', 'fromtag "%{track}. %{title}" 2', 0844 '-c', 'get', 0845 '-c', 'filenameformat', albumdir]), 0846 expected) 0847 expected = ( 0848 'File: MPEG 1 Layer 3 64 kbps 44100 Hz 1 Channels\n' 0849 ' Name: 01 Schoen.mp3\n' 0850 'Tag 2: ID3v2.3.0\n' 0851 ' Title Schön\n' 0852 ' Artist An Artist\n' 0853 ' Album An Album\n' 0854 ' Date 2016\n' 0855 ' Track Number 1\n' 0856 ) 0857 if sys.platform == 'win32' and platform.release() == 'XP': 0858 expected = expected.replace('ö', '?') 0859 self.assertEqual(call_kid3_cli( 0860 ['-c', 'get', os.path.join(albumdir, '*.mp3')]), 0861 expected) 0862 0863 0864 class CliFunctionsJsonTestCase(unittest.TestCase): 0865 def test_invalid(self): 0866 for cmd, rsp in ( 0867 ('{"abc":"def"}', 0868 {'error': {'code': -1, 0869 'message': 'missing method: {"abc":"def"}'}}), 0870 ('{"method":"unknown"}', 0871 {'error': {'code': -1, 0872 'message': 'Unknown command ' 0873 '\'{"method":"unknown"}\', -h for help.'}}), 0874 ('{"method":"timeout",params:["off"]}', 0875 {'error': {'code': -1, 0876 'message': 'unterminated object: ' 0877 '{"method":"timeout",params:["off"]}'}}), 0878 ('{"method":"timeout","params":["default"}', 0879 {'error': {'code': -1, 0880 'message': 'unterminated array: ' 0881 '{"method":"timeout","params":["default"}'}}), 0882 ('{"method":"unknown"}', 0883 {'error': {'code': -1, 0884 'message': 'Unknown command ' 0885 '\'{"method":"unknown"}\', -h for help.'}}), 0886 ): 0887 p = subprocess.Popen([kid3_cli_path(), '-c', cmd], 0888 stdout=subprocess.PIPE, stderr=subprocess.PIPE, 0889 universal_newlines=True) 0890 stdout, stderr = p.communicate() 0891 result = json.loads(stdout) 0892 self.assertEqual(result, rsp) 0893 self.assertEqual(p.returncode, 1) 0894 0895 def test_timeout(self): 0896 self.assertEqual(call_kid3_cli( 0897 ['-c', '{"method":"timeout"}', 0898 '-c', '{"method":"timeout","params":[5000]}', 0899 '-c', '{"method":"timeout"}', 0900 '-c', '{"method":"timeout","params":["off"]}', 0901 '-c', '{"method":"timeout"}', 0902 '-c', '{"method":"timeout","params":["default"]}', 0903 '-c', '{"method":"timeout"}']), 0904 '{"result":{"timeout":"default"}}\n' 0905 '{"result":{"timeout":"5000 ms"}}\n' 0906 '{"result":{"timeout":"5000 ms"}}\n' 0907 '{"result":{"timeout":"off"}}\n' 0908 '{"result":{"timeout":"off"}}\n' 0909 '{"result":{"timeout":"default"}}\n' 0910 '{"result":{"timeout":"default"}}\n') 0911 0912 def test_json_methods(self): 0913 self.maxDiff=None 0914 with tempfile.TemporaryDirectory() as tmpdir: 0915 mp3path = os.path.join(tmpdir, 'test.mp3') 0916 create_test_file(mp3path) 0917 self.assertEqual(call_kid3_cli( 0918 ['-c', '{"method":"get","params":["title", 2]}', 0919 '-c', '{"method":"get","params":["all",[1,2]]}', 0920 '-c', '{"method":"set","params":["title","A Title"]}', 0921 '-c', '{"method":"get","params":["title"]}', 0922 '-c', '{"method": "get", "params": ["title"]}', 0923 '-c', '{"method":"get"}', 0924 '-c', '{"method":"select","params":["none"]}', 0925 '-c', '{"method":"ls"}', 0926 '-c', '{"method":"select","params":["first"]}', 0927 '-c', '{"method":"filenameformat"}', 0928 '-c', '{"method":"tagformat"}', 0929 '-c', '{"method":"textencoding"}', 0930 '-c', '{"method":"numbertracks","params":[3,2]}', 0931 '-c', '{"jsonrpc":"2.0","id":"1","method":"remove",' 0932 '"params":[1]}', 0933 '-c', '{"method":"ls"}', 0934 mp3path]), 0935 '{"result":null}\n' 0936 '{"result":{"taggedFile":{"fileName":"test.mp3",' 0937 '"fileNameChanged":false,"format":' 0938 '"MPEG 1 Layer 3 64 kbps 44100 Hz 1 Channels"}}}\n' 0939 '{"result":null}\n' 0940 '{"result":"A Title"}\n' 0941 '{\n' 0942 ' "result": "A Title"\n' 0943 '}\n\n' 0944 '{"result":{"taggedFile":{"fileName":"test.mp3",' 0945 '"fileNameChanged":false,"format":' 0946 '"MPEG 1 Layer 3 64 kbps 44100 Hz 1 Channels",' 0947 '"tag2":{"format":"ID3v2.3.0","frames":[{"changed":true,' 0948 '"name":"Title","value":"A Title"}]}}}}\n' 0949 '{"result":null}\n' 0950 '{"result":{"files":[{"changed":true,"fileName":"test.mp3",' 0951 '"selected":false,"tags":[2]}]}}\n' 0952 '{"result":null}\n' 0953 '{"result":null}\n' 0954 '{"result":null}\n' 0955 '{"result":null}\n' 0956 '{"result":null}\n' 0957 '{"id":"1","jsonrpc":"2.0","result":null}\n' 0958 '{"result":{"files":[{"changed":true,"fileName":"test.mp3",' 0959 '"selected":true,"tags":[2]}]}}\n') 0960 0961 0962 if __name__ == '__main__': 0963 unittest.main()