1# -*- coding: utf-8 -*- 2 3import os 4import warnings 5 6from mutagen._compat import PY3, text_type, PY2, izip, cBytesIO 7from mutagen.asf import ASF, ASFHeaderError, ASFValue, UNICODE, DWORD, QWORD 8from mutagen.asf import BOOL, WORD, BYTEARRAY, GUID 9from mutagen.asf._util import guid2bytes, bytes2guid 10from mutagen.asf._objects import ContentDescriptionObject, \ 11 ExtendedContentDescriptionObject, HeaderExtensionObject, \ 12 MetadataObject, MetadataLibraryObject, CodecListObject, PaddingObject, \ 13 HeaderObject 14from mutagen.asf import ASFUnicodeAttribute, ASFError, ASFByteArrayAttribute, \ 15 ASFBoolAttribute, ASFDWordAttribute, ASFQWordAttribute, ASFWordAttribute, \ 16 ASFGUIDAttribute 17 18from tests import TestCase, DATA_DIR, get_temp_copy 19 20 21class TASFFile(TestCase): 22 23 def test_not_my_file(self): 24 self.failUnlessRaises( 25 ASFHeaderError, ASF, 26 os.path.join(DATA_DIR, "empty.ogg")) 27 self.failUnlessRaises( 28 ASFHeaderError, ASF, 29 os.path.join(DATA_DIR, "click.mpc")) 30 31 32class TASFMisc(TestCase): 33 34 def test_guid(self): 35 ex = "75B22633-668E-11CF-A6D9-00AA0062CE6C" 36 b = guid2bytes(ex) 37 self.assertEqual(len(b), 16) 38 self.assertTrue(isinstance(b, bytes)) 39 self.assertEqual(bytes2guid(b), ex) 40 41 42class TASFInfo(TestCase): 43 44 def setUp(self): 45 # WMA 9.1 64kbps CBR 48khz 46 self.wma1 = ASF(os.path.join(DATA_DIR, "silence-1.wma")) 47 # WMA 9.1 Professional 192kbps VBR 44khz 48 self.wma2 = ASF(os.path.join(DATA_DIR, "silence-2.wma")) 49 # WMA 9.1 Lossless 44khz 50 self.wma3 = ASF(os.path.join(DATA_DIR, "silence-3.wma")) 51 52 def test_length(self): 53 self.failUnlessAlmostEqual(self.wma1.info.length, 3.7, 1) 54 self.failUnlessAlmostEqual(self.wma2.info.length, 3.7, 1) 55 self.failUnlessAlmostEqual(self.wma3.info.length, 3.7, 1) 56 57 def test_bitrate(self): 58 self.failUnlessEqual(self.wma1.info.bitrate // 1000, 64) 59 self.failUnlessEqual(self.wma2.info.bitrate // 1000, 38) 60 self.failUnlessEqual(self.wma3.info.bitrate // 1000, 58) 61 62 def test_sample_rate(self): 63 self.failUnlessEqual(self.wma1.info.sample_rate, 48000) 64 self.failUnlessEqual(self.wma2.info.sample_rate, 44100) 65 self.failUnlessEqual(self.wma3.info.sample_rate, 44100) 66 67 def test_channels(self): 68 self.failUnlessEqual(self.wma1.info.channels, 2) 69 self.failUnlessEqual(self.wma2.info.channels, 2) 70 self.failUnlessEqual(self.wma3.info.channels, 2) 71 72 def test_codec_type(self): 73 self.assertEqual(self.wma1.info.codec_type, 74 "Windows Media Audio 9 Standard") 75 self.assertEqual(self.wma2.info.codec_type, 76 "Windows Media Audio 9 Professional") 77 self.assertEqual(self.wma3.info.codec_type, 78 "Windows Media Audio 9 Lossless") 79 80 def test_codec_name(self): 81 self.assertEqual(self.wma1.info.codec_name, 82 "Windows Media Audio 9.1") 83 self.assertEqual(self.wma2.info.codec_name, 84 "Windows Media Audio 9.1 Professional") 85 self.assertEqual(self.wma3.info.codec_name, 86 "Windows Media Audio 9.1 Lossless") 87 88 def test_codec_description(self): 89 self.assertEqual(self.wma1.info.codec_description, 90 "64 kbps, 48 kHz, stereo 2-pass CBR") 91 self.assertEqual(self.wma2.info.codec_description, 92 "192 kbps, 44 kHz, 2 channel 24 bit 2-pass VBR") 93 self.assertEqual(self.wma3.info.codec_description, 94 "VBR Quality 100, 44 kHz, 2 channel 16 bit 1-pass VBR") 95 96 def test_pprint(self): 97 self.assertTrue(self.wma1.info.pprint()) 98 self.assertTrue(isinstance(self.wma1.info.pprint(), text_type)) 99 100 101class TASF(TestCase): 102 103 def setUp(self): 104 self.filename = get_temp_copy(self.original) 105 self.audio = ASF(self.filename) 106 107 def tearDown(self): 108 os.unlink(self.filename) 109 110 111class TASFMixin(object): 112 113 def test_header_object_misc(self): 114 header = self.audio._header 115 header.pprint() 116 repr(header) 117 118 def test_delete(self): 119 self.audio["QL/Bla"] = u"Foooooooooooooooooo" 120 self.audio.save(padding=lambda x: 0) 121 filesize = os.path.getsize(self.audio.filename) 122 self.audio.delete() 123 self.assertTrue(os.path.getsize(self.audio.filename) < filesize) 124 125 def test_pprint(self): 126 self.failUnless(self.audio.pprint()) 127 128 def set_key(self, key, value, result=None, expected=True): 129 self.audio[key] = value 130 self.audio.save() 131 self.audio = ASF(self.audio.filename) 132 self.failUnless(key in self.audio) 133 self.failUnless(key in self.audio.tags) 134 self.failUnless(key in self.audio.tags.keys()) 135 self.failUnless(key in self.audio.tags.as_dict().keys()) 136 newvalue = self.audio[key] 137 if isinstance(newvalue, list): 138 for a, b in izip(sorted(newvalue), sorted(result or value)): 139 self.failUnlessEqual(a, b) 140 else: 141 self.failUnlessEqual(self.audio[key], result or value) 142 143 def test_slice(self): 144 tags = self.audio.tags 145 tags.clear() 146 tags["Author"] = [u"Foo", u"Bar"] 147 self.assertEqual(tags[:], [("Author", "Foo"), ("Author", "Bar")]) 148 del tags[:] 149 self.assertEqual(tags[:], []) 150 tags[:] = [("Author", "Baz")] 151 self.assertEqual(tags.items(), [("Author", ["Baz"])]) 152 153 def test_iter(self): 154 self.assertEqual(next(iter(self.audio.tags)), ("Title", "test")) 155 self.assertEqual(list(self.audio.tags)[0], ("Title", "test")) 156 157 def test_contains(self): 158 self.failUnlessEqual("notatag" in self.audio.tags, False) 159 160 def test_inval_type(self): 161 self.failUnlessRaises(ValueError, ASFValue, "", 4242) 162 163 def test_repr(self): 164 repr(ASFValue(u"foo", UNICODE, stream=1, language=2)) 165 166 def test_auto_guuid(self): 167 value = ASFValue(b'\x9eZl}\x89\xa2\xb5D\xb8\xa30\xfe', GUID) 168 self.set_key(u"WM/WMCollectionGroupID", value, [value]) 169 170 def test_py3_bytes(self): 171 if PY3: 172 value = ASFValue(b'\xff\x00', BYTEARRAY) 173 self.set_key(u"QL/Something", [b'\xff\x00'], [value]) 174 175 def test_set_invalid(self): 176 setitem = self.audio.__setitem__ 177 if PY2: 178 self.assertRaises(ValueError, setitem, u"QL/Something", [b"\xff"]) 179 self.assertRaises(TypeError, setitem, u"QL/Something", [object()]) 180 181 # don't delete on error 182 setitem(u"QL/Foobar", [u"ok"]) 183 self.assertRaises(TypeError, setitem, u"QL/Foobar", [object()]) 184 self.assertEqual(self.audio[u"QL/Foobar"], [u"ok"]) 185 186 def test_auto_unicode(self): 187 self.set_key(u"WM/AlbumTitle", u"foo", 188 [ASFValue(u"foo", UNICODE)]) 189 190 def test_auto_unicode_list(self): 191 self.set_key(u"WM/AlbumTitle", [u"foo", u"bar"], 192 [ASFValue(u"foo", UNICODE), ASFValue(u"bar", UNICODE)]) 193 194 def test_word(self): 195 self.set_key(u"WM/Track", ASFValue(24, WORD), [ASFValue(24, WORD)]) 196 197 def test_auto_word(self): 198 self.set_key(u"WM/Track", 12, 199 [ASFValue(12, DWORD)]) 200 201 def test_auto_word_list(self): 202 self.set_key(u"WM/Track", [12, 13], 203 [ASFValue(12, WORD), ASFValue(13, WORD)]) 204 205 def test_auto_dword(self): 206 self.set_key(u"WM/Track", 12, 207 [ASFValue(12, DWORD)]) 208 209 def test_auto_dword_list(self): 210 self.set_key(u"WM/Track", [12, 13], 211 [ASFValue(12, DWORD), ASFValue(13, DWORD)]) 212 213 def test_auto_qword(self): 214 self.set_key(u"WM/Track", 12, 215 [ASFValue(12, QWORD)]) 216 217 def test_auto_qword_list(self): 218 self.set_key(u"WM/Track", [12, 13], 219 [ASFValue(12, QWORD), ASFValue(13, QWORD)]) 220 221 def test_auto_bool(self): 222 self.set_key(u"IsVBR", True, 223 [ASFValue(True, BOOL)]) 224 225 def test_auto_bool_list(self): 226 self.set_key(u"IsVBR", [True, False], 227 [ASFValue(True, BOOL), ASFValue(False, BOOL)]) 228 229 def test_basic_tags(self): 230 self.set_key("Title", "Wheeee", ["Wheeee"]) 231 self.set_key("Author", "Whoooo", ["Whoooo"]) 232 self.set_key("Copyright", "Whaaaa", ["Whaaaa"]) 233 self.set_key("Description", "Wii", ["Wii"]) 234 self.set_key("Rating", "5", ["5"]) 235 236 def test_stream(self): 237 self.audio["QL/OneHasStream"] = [ 238 ASFValue("Whee", UNICODE, stream=2), 239 ASFValue("Whee", UNICODE), 240 ] 241 self.audio["QL/AllHaveStream"] = [ 242 ASFValue("Whee", UNICODE, stream=1), 243 ASFValue("Whee", UNICODE, stream=2), 244 ] 245 self.audio["QL/NoStream"] = ASFValue("Whee", UNICODE) 246 self.audio.save() 247 self.audio = ASF(self.audio.filename) 248 self.failUnlessEqual(self.audio["QL/NoStream"][0].stream, None) 249 self.failUnlessEqual(self.audio["QL/OneHasStream"][1].stream, 2) 250 self.failUnlessEqual(self.audio["QL/OneHasStream"][0].stream, None) 251 self.failUnlessEqual(self.audio["QL/AllHaveStream"][0].stream, 1) 252 self.failUnlessEqual(self.audio["QL/AllHaveStream"][1].stream, 2) 253 254 def test_language(self): 255 self.failIf("QL/OneHasLang" in self.audio) 256 self.failIf("QL/AllHaveLang" in self.audio) 257 self.audio["QL/OneHasLang"] = [ 258 ASFValue("Whee", UNICODE, language=2), 259 ASFValue("Whee", UNICODE), 260 ] 261 self.audio["QL/AllHaveLang"] = [ 262 ASFValue("Whee", UNICODE, language=1), 263 ASFValue("Whee", UNICODE, language=2), 264 ] 265 self.audio["QL/NoLang"] = ASFValue("Whee", UNICODE) 266 self.audio.save() 267 self.audio = ASF(self.audio.filename) 268 self.failUnlessEqual(self.audio["QL/NoLang"][0].language, None) 269 self.failUnlessEqual(self.audio["QL/OneHasLang"][1].language, 2) 270 self.failUnlessEqual(self.audio["QL/OneHasLang"][0].language, None) 271 self.failUnlessEqual(self.audio["QL/AllHaveLang"][0].language, 1) 272 self.failUnlessEqual(self.audio["QL/AllHaveLang"][1].language, 2) 273 274 def test_lang_and_stream_mix(self): 275 self.audio["QL/Mix"] = [ 276 ASFValue("Whee", UNICODE, stream=1), 277 ASFValue("Whee", UNICODE, language=2), 278 ASFValue("Whee", UNICODE, stream=3, language=4), 279 ASFValue("Whee", UNICODE), 280 ] 281 self.audio.save() 282 self.audio = ASF(self.audio.filename) 283 # order not preserved here because they end up in different objects. 284 self.failUnlessEqual(self.audio["QL/Mix"][1].language, None) 285 self.failUnlessEqual(self.audio["QL/Mix"][1].stream, 1) 286 self.failUnlessEqual(self.audio["QL/Mix"][2].language, 2) 287 self.failUnlessEqual(self.audio["QL/Mix"][2].stream, 0) 288 self.failUnlessEqual(self.audio["QL/Mix"][3].language, 4) 289 self.failUnlessEqual(self.audio["QL/Mix"][3].stream, 3) 290 self.failUnlessEqual(self.audio["QL/Mix"][0].language, None) 291 self.failUnlessEqual(self.audio["QL/Mix"][0].stream, None) 292 293 def test_data_size(self): 294 v = ASFValue("", UNICODE, data=b'4\xd8\x1e\xdd\x00\x00') 295 self.failUnlessEqual(v.data_size(), len(v._render())) 296 297 298class TASFAttributes(TestCase): 299 300 def test_ASFUnicodeAttribute(self): 301 if PY3: 302 self.assertRaises(TypeError, ASFUnicodeAttribute, b"\xff") 303 else: 304 self.assertRaises(ValueError, ASFUnicodeAttribute, b"\xff") 305 val = u'\xf6\xe4\xfc' 306 self.assertEqual(ASFUnicodeAttribute(val.encode("utf-8")), val) 307 308 self.assertRaises(ASFError, ASFUnicodeAttribute, data=b"\x00") 309 self.assertEqual(ASFUnicodeAttribute(u"foo").value, u"foo") 310 311 assert ASFUnicodeAttribute(data=b"") == u"" 312 313 def test_ASFUnicodeAttribute_dunder(self): 314 attr = ASFUnicodeAttribute(u"foo") 315 316 self.assertEqual(bytes(attr), b"f\x00o\x00o\x00") 317 self.assertEqual(text_type(attr), u"foo") 318 if PY3: 319 self.assertEqual(repr(attr), "ASFUnicodeAttribute('foo')") 320 else: 321 self.assertEqual(repr(attr), "ASFUnicodeAttribute(u'foo')") 322 self.assertRaises(TypeError, int, attr) 323 324 def test_ASFByteArrayAttribute(self): 325 self.assertRaises(TypeError, ASFByteArrayAttribute, u"foo") 326 self.assertEqual(ASFByteArrayAttribute(data=b"\xff").value, b"\xff") 327 328 def test_ASFByteArrayAttribute_dunder(self): 329 attr = ASFByteArrayAttribute(data=b"\xff") 330 self.assertEqual(bytes(attr), b"\xff") 331 self.assertEqual(text_type(attr), u"[binary data (1 bytes)]") 332 if PY3: 333 self.assertEqual(repr(attr), r"ASFByteArrayAttribute(b'\xff')") 334 else: 335 self.assertEqual(repr(attr), r"ASFByteArrayAttribute('\xff')") 336 self.assertRaises(TypeError, int, attr) 337 338 def test_ASFByteArrayAttribute_compat(self): 339 ba = ASFByteArrayAttribute() 340 ba.value = b"\xff" 341 self.assertEqual(ba._render(), b"\xff") 342 343 def test_ASFGUIDAttribute(self): 344 self.assertEqual(ASFGUIDAttribute(data=b"\xff").value, b"\xff") 345 self.assertRaises(TypeError, ASFGUIDAttribute, u"foo") 346 347 def test_ASFGUIDAttribute_dunder(self): 348 attr = ASFGUIDAttribute(data=b"\xff") 349 self.assertEqual(bytes(attr), b"\xff") 350 if PY3: 351 self.assertEqual(text_type(attr), u"b'\\xff'") 352 self.assertEqual(repr(attr), "ASFGUIDAttribute(b'\\xff')") 353 else: 354 self.assertEqual(text_type(attr), u"'\\xff'") 355 self.assertEqual(repr(attr), "ASFGUIDAttribute('\\xff')") 356 self.assertRaises(TypeError, int, attr) 357 358 def test_ASFBoolAttribute(self): 359 self.assertEqual( 360 ASFBoolAttribute(data=b"\x01\x00\x00\x00").value, True) 361 self.assertEqual( 362 ASFBoolAttribute(data=b"\x00\x00\x00\x00").value, False) 363 self.assertEqual(ASFBoolAttribute(False).value, False) 364 365 def test_ASFBoolAttribute_dunder(self): 366 attr = ASFBoolAttribute(False) 367 self.assertEqual(bytes(attr), b"False") 368 self.assertEqual(text_type(attr), u"False") 369 self.assertEqual(repr(attr), "ASFBoolAttribute(False)") 370 self.assertRaises(TypeError, int, attr) 371 372 def test_ASFWordAttribute(self): 373 self.assertEqual( 374 ASFWordAttribute(data=b"\x00" * 2).value, 0) 375 self.assertEqual( 376 ASFWordAttribute(data=b"\xff" * 2).value, 2 ** 16 - 1) 377 self.assertRaises(ValueError, ASFWordAttribute, -1) 378 self.assertRaises(ValueError, ASFWordAttribute, 2 ** 16) 379 380 def test_ASFWordAttribute_dunder(self): 381 attr = ASFWordAttribute(data=b"\x00" * 2) 382 self.assertEqual(bytes(attr), b"0") 383 self.assertEqual(text_type(attr), u"0") 384 self.assertEqual(repr(attr), "ASFWordAttribute(0)") 385 self.assertEqual(int(attr), 0) 386 387 def test_ASFDWordAttribute(self): 388 self.assertEqual( 389 ASFDWordAttribute(data=b"\x00" * 4).value, 0) 390 self.assertEqual( 391 ASFDWordAttribute(data=b"\xff" * 4).value, 2 ** 32 - 1) 392 self.assertRaises(ValueError, ASFDWordAttribute, -1) 393 self.assertRaises(ValueError, ASFDWordAttribute, 2 ** 32) 394 395 def test_ASFDWordAttribute_dunder(self): 396 attr = ASFDWordAttribute(data=b"\x00" * 4) 397 self.assertEqual(bytes(attr), b"0") 398 self.assertEqual(text_type(attr), u"0") 399 self.assertEqual(repr(attr).replace("0L", "0"), "ASFDWordAttribute(0)") 400 self.assertEqual(int(attr), 0) 401 402 def test_ASFQWordAttribute(self): 403 self.assertEqual( 404 ASFQWordAttribute(data=b"\x00" * 8).value, 0) 405 self.assertEqual( 406 ASFQWordAttribute(data=b"\xff" * 8).value, 2 ** 64 - 1) 407 self.assertRaises(ValueError, ASFQWordAttribute, -1) 408 self.assertRaises(ValueError, ASFQWordAttribute, 2 ** 64) 409 410 def test_ASFQWordAttribute_dunder(self): 411 attr = ASFQWordAttribute(data=b"\x00" * 8) 412 self.assertEqual(bytes(attr), b"0") 413 self.assertEqual(text_type(attr), u"0") 414 self.assertEqual(repr(attr).replace("0L", "0"), "ASFQWordAttribute(0)") 415 self.assertEqual(int(attr), 0) 416 417 418class TASFTags1(TASF, TASFMixin): 419 original = os.path.join(DATA_DIR, "silence-1.wma") 420 421 422class TASFTags2(TASF, TASFMixin): 423 original = os.path.join(DATA_DIR, "silence-2.wma") 424 425 426class TASFTags3(TASF, TASFMixin): 427 original = os.path.join(DATA_DIR, "silence-3.wma") 428 429 430class TASFIssue29(TestCase): 431 original = os.path.join(DATA_DIR, "issue_29.wma") 432 433 def setUp(self): 434 self.filename = get_temp_copy(self.original) 435 self.audio = ASF(self.filename) 436 437 def tearDown(self): 438 os.unlink(self.filename) 439 440 def test_pprint(self): 441 self.audio.pprint() 442 443 def test_issue_29_description(self): 444 self.audio["Description"] = "Hello" 445 self.audio.save() 446 audio = ASF(self.filename) 447 self.failUnless("Description" in audio) 448 self.failUnlessEqual(audio["Description"], ["Hello"]) 449 del(audio["Description"]) 450 self.failIf("Description" in audio) 451 audio.save() 452 audio = ASF(self.filename) 453 self.failIf("Description" in audio) 454 455 456class TASFObjects(TestCase): 457 458 filename = os.path.join(DATA_DIR, "silence-1.wma") 459 460 def test_invalid_header(self): 461 with warnings.catch_warnings(): 462 warnings.simplefilter("ignore") 463 asf = ASF() 464 fileobj = cBytesIO( 465 b"0&\xb2u\x8ef\xcf\x11\xa6\xd9\x00\xaa\x00b\xcel\x19\xbf\x01\x00" 466 b"\x00\x00\x00\x00\x07\x00\x00\x00\x01\x02") 467 self.assertRaises( 468 ASFHeaderError, HeaderObject.parse_full, asf, fileobj) 469 470 471class TASFAttrDest(TestCase): 472 473 original = os.path.join(DATA_DIR, "silence-1.wma") 474 475 def setUp(self): 476 self.filename = get_temp_copy(self.original) 477 audio = ASF(self.filename) 478 audio.clear() 479 audio.save() 480 481 def tearDown(self): 482 os.unlink(self.filename) 483 484 def test_author(self): 485 audio = ASF(self.filename) 486 values = [u"Foo", u"Bar", u"Baz"] 487 audio["Author"] = values 488 audio.save() 489 self.assertEqual( 490 list(audio.to_content_description.items()), [(u"Author", u"Foo")]) 491 self.assertEqual( 492 audio.to_metadata_library, 493 [(u"Author", u"Bar"), (u"Author", u"Baz")]) 494 495 new = ASF(self.filename) 496 self.assertEqual(new["Author"], values) 497 498 def test_author_long(self): 499 audio = ASF(self.filename) 500 # 2 ** 16 - 2 bytes encoded text + 2 bytes termination 501 just_small_enough = u"a" * (((2 ** 16) // 2) - 2) 502 audio["Author"] = [just_small_enough] 503 audio.save() 504 self.assertTrue(audio.to_content_description) 505 self.assertFalse(audio.to_metadata_library) 506 507 audio["Author"] = [just_small_enough + u"a"] 508 audio.save() 509 self.assertFalse(audio.to_content_description) 510 self.assertTrue(audio.to_metadata_library) 511 512 def test_multi_order(self): 513 audio = ASF(self.filename) 514 audio["Author"] = [u"a", u"b", u"c"] 515 audio.save() 516 audio = ASF(self.filename) 517 self.assertEqual(audio["Author"], [u"a", u"b", u"c"]) 518 519 def test_multi_order_extended(self): 520 audio = ASF(self.filename) 521 audio["WM/Composer"] = [u"a", u"b", u"c"] 522 audio.save() 523 audio = ASF(self.filename) 524 self.assertEqual(audio["WM/Composer"], [u"a", u"b", u"c"]) 525 526 def test_non_text_type(self): 527 audio = ASF(self.filename) 528 audio["Author"] = [42] 529 audio.save() 530 self.assertFalse(audio.to_content_description) 531 new = ASF(self.filename) 532 self.assertEqual(new["Author"], [42]) 533 534 def test_empty(self): 535 audio = ASF(self.filename) 536 audio["Author"] = [u"", u""] 537 audio["Title"] = [u""] 538 audio["Copyright"] = [] 539 audio.save() 540 541 new = ASF(self.filename) 542 self.assertEqual(new["Author"], [u"", u""]) 543 self.assertEqual(new["Title"], [u""]) 544 self.assertFalse("Copyright" in new) 545 546 547class TASFLargeValue(TestCase): 548 549 original = os.path.join(DATA_DIR, "silence-1.wma") 550 551 def setUp(self): 552 self.filename = get_temp_copy(self.original) 553 554 def tearDown(self): 555 os.unlink(self.filename) 556 557 def test_save_small_bytearray(self): 558 audio = ASF(self.filename) 559 audio["QL/LargeObject"] = [ASFValue(b"." * 0xFFFF, BYTEARRAY)] 560 audio.save() 561 self.failIf( 562 "QL/LargeObject" not in audio.to_extended_content_description) 563 self.failIf("QL/LargeObject" in audio.to_metadata) 564 self.failIf("QL/LargeObject" in dict(audio.to_metadata_library)) 565 566 def test_save_large_bytearray(self): 567 audio = ASF(self.filename) 568 audio["QL/LargeObject"] = [ASFValue(b"." * (0xFFFF + 1), BYTEARRAY)] 569 audio.save() 570 self.failIf("QL/LargeObject" in audio.to_extended_content_description) 571 self.failIf("QL/LargeObject" in audio.to_metadata) 572 self.failIf("QL/LargeObject" not in dict(audio.to_metadata_library)) 573 574 def test_save_small_string(self): 575 audio = ASF(self.filename) 576 audio["QL/LargeObject"] = [ASFValue("." * (0x7FFF - 1), UNICODE)] 577 audio.save() 578 self.failIf( 579 "QL/LargeObject" not in audio.to_extended_content_description) 580 self.failIf("QL/LargeObject" in audio.to_metadata) 581 self.failIf("QL/LargeObject" in dict(audio.to_metadata_library)) 582 583 def test_save_large_string(self): 584 audio = ASF(self.filename) 585 audio["QL/LargeObject"] = [ASFValue("." * 0x7FFF, UNICODE)] 586 audio.save() 587 self.failIf("QL/LargeObject" in audio.to_extended_content_description) 588 self.failIf("QL/LargeObject" in audio.to_metadata) 589 self.failIf("QL/LargeObject" not in dict(audio.to_metadata_library)) 590 591 def test_save_guid(self): 592 # https://github.com/quodlibet/mutagen/issues/81 593 audio = ASF(self.filename) 594 audio["QL/GuidObject"] = [ASFValue(b" " * 16, GUID)] 595 audio.save() 596 self.failIf("QL/GuidObject" in audio.to_extended_content_description) 597 self.failIf("QL/GuidObject" in audio.to_metadata) 598 self.failIf("QL/GuidObject" not in dict(audio.to_metadata_library)) 599 600 601class TASFSave(TestCase): 602 # https://github.com/quodlibet/mutagen/issues/81#issuecomment-207014936 603 604 original = os.path.join(DATA_DIR, "silence-1.wma") 605 606 def setUp(self): 607 self.filename = get_temp_copy(self.original) 608 self.audio = ASF(self.filename) 609 610 def tearDown(self): 611 os.unlink(self.filename) 612 613 def test_save_filename(self): 614 self.audio.save(self.audio.filename) 615 616 def test_multiple_delete(self): 617 self.audio["large_value1"] = "#" * 50000 618 self.audio.save() 619 620 audio = ASF(self.filename) 621 for tag in audio.keys(): 622 del(audio[tag]) 623 audio.save() 624 625 def test_readd_objects(self): 626 header = self.audio._header 627 del header.objects[:] 628 self.audio.save() 629 self.assertTrue(header.get_child(ContentDescriptionObject.GUID)) 630 self.assertTrue( 631 header.get_child(ExtendedContentDescriptionObject.GUID)) 632 self.assertTrue(header.get_child(HeaderExtensionObject.GUID)) 633 ext = header.get_child(HeaderExtensionObject.GUID) 634 self.assertTrue(ext.get_child(MetadataObject.GUID)) 635 self.assertTrue(ext.get_child(MetadataLibraryObject.GUID)) 636 637 def test_keep_others(self): 638 self.audio.save() 639 new = ASF(self.filename) 640 self.assertTrue(new._header.get_child(CodecListObject.GUID)) 641 642 def test_padding(self): 643 old_tags = sorted(self.audio.items()) 644 645 def get_padding(fn): 646 header = ASF(fn)._header 647 return len(header.get_child(PaddingObject.GUID).data) 648 649 for i in [0, 1, 2, 3, 42, 100, 5000, 30432, 1]: 650 651 def padding_cb(info): 652 self.assertEqual(info.size, 30432) 653 return i 654 655 self.audio.save(padding=padding_cb) 656 self.assertEqual(get_padding(self.filename), i) 657 658 last = ASF(self.filename) 659 self.assertEqual(sorted(last.items()), old_tags) 660