def test_write_back_bad_atoms(self): # write a broken atom and try to load it data = Atom.render(b"datA", b"\x00\x00\x00\x01\x00\x00\x00\x00wheeee") data = Atom.render(b"aART", data) tags = self.wrap_ilst(data) self.assertFalse(tags) # save it into an existing mp4 original = os.path.join("tests", "data", "has-tags.m4a") fd, filename = mkstemp(suffix=".mp4") os.close(fd) shutil.copy(original, filename) delete(filename) # it should still end up in the file tags.save(filename) with open(filename, "rb") as h: self.assertTrue(b"wheeee" in h.read()) # if we define our own aART throw away the broken one tags["aART"] = ["new"] tags.save(filename) with open(filename, "rb") as h: self.assertFalse(b"wheeee" in h.read()) # add the broken one back and delete all tags including the broken one del tags["aART"] tags.save(filename) with open(filename, "rb") as h: self.assertTrue(b"wheeee" in h.read()) delete(filename) with open(filename, "rb") as h: self.assertFalse(b"wheeee" in h.read())
def test_covr_blank_format(self): data = Atom.render( b"data", b"\x00\x00\x00\x00" + b"\x00" * 4 + b"whee") covr = Atom.render(b"covr", data) tags = self.wrap_ilst(covr) self.failUnlessEqual( MP4Cover.FORMAT_JPEG, tags["covr"][0].imageformat)
def test_write_back_bad_atoms(self): # write a broken atom and try to load it data = Atom.render(b"datA", b"\x00\x00\x00\x01\x00\x00\x00\x00wheeee") data = Atom.render(b"aART", data) tags = self.wrap_ilst(data) self.assertFalse(tags) # save it into an existing mp4 original = os.path.join(DATA_DIR, "has-tags.m4a") filename = get_temp_copy(original) try: delete(filename) # it should still end up in the file tags.save(filename) with open(filename, "rb") as h: self.assertTrue(b"wheeee" in h.read()) # if we define our own aART throw away the broken one tags["aART"] = ["new"] tags.save(filename) with open(filename, "rb") as h: self.assertFalse(b"wheeee" in h.read()) # add the broken one back and delete all tags including # the broken one del tags["aART"] tags.save(filename) with open(filename, "rb") as h: self.assertTrue(b"wheeee" in h.read()) delete(filename) with open(filename, "rb") as h: self.assertFalse(b"wheeee" in h.read()) finally: os.unlink(filename)
def test_write_back_bad_atoms(self): # write a broken atom and try to load it data = Atom.render(b"datA", b"\x00\x00\x00\x01\x00\x00\x00\x00wheeee") data = Atom.render(b"aART", data) tags = self.wrap_ilst(data) self.assertFalse(tags) # save it into an existing mp4 original = os.path.join("tests", "data", "has-tags.m4a") fd, filename = mkstemp(suffix='.mp4') os.close(fd) shutil.copy(original, filename) delete(filename) # it should still end up in the file tags.save(filename) with open(filename, "rb") as h: self.assertTrue(b"wheeee" in h.read()) # if we define our own aART throw away the broken one tags["aART"] = ["new"] tags.save(filename) with open(filename, "rb") as h: self.assertFalse(b"wheeee" in h.read()) # add the broken one back and delete all tags including the broken one del tags["aART"] tags.save(filename) with open(filename, "rb") as h: self.assertTrue(b"wheeee" in h.read()) delete(filename) with open(filename, "rb") as h: self.assertFalse(b"wheeee" in h.read())
def test_parse_tmpo(self): for d, v in [(b"\x01", 1), (b"\x01\x02", 258), (b"\x01\x02\x03", 66051), (b"\x01\x02\x03\x04", 16909060), (b"\x01\x02\x03\x04\x05\x06\x07\x08", 72623859790382856)]: data = Atom.render(b"data", b"\x00\x00\x00\x15" + b"\x00\x00\x00\x00" + d) tmpo = Atom.render(b"tmpo", data) tags = self.wrap_ilst(tmpo) assert tags["tmpo"][0] == v
def test_parse_multiple_atoms(self): # while we don't write multiple values as multiple atoms # still read them # https://github.com/quodlibet/mutagen/issues/165 data = Atom.render(b"data", b"\x00\x00\x00\x01" + b"\x00" * 4 + b"foo") grp1 = Atom.render(b"\xa9grp", data) data = Atom.render(b"data", b"\x00\x00\x00\x01" + b"\x00" * 4 + b"bar") grp2 = Atom.render(b"\xa9grp", data) tags = self.wrap_ilst(grp1 + grp2) self.assertEqual(tags["\xa9grp"], [u"foo", u"bar"])
def test_mdhd_version_1(self, soun="soun"): mdhd = Atom.render("mdhd", ("\x01\x00\x00\x00" + "\x00" * 16 + "\x00\x00\x00\x02" + # 2 Hz "\x00\x00\x00\x00\x00\x00\x00\x10")) hdlr = Atom.render("hdlr", "\x00" * 8 + soun) mdia = Atom.render("mdia", mdhd + hdlr) trak = Atom.render("trak", mdia) moov = Atom.render("moov", trak) fileobj = StringIO(moov) atoms = Atoms(fileobj) info = MP4Info(atoms, fileobj) self.failUnlessEqual(info.length, 8)
def test_mdhd_version_1(self, soun=b"soun"): mdhd = Atom.render(b"mdhd", (b"\x01\x00\x00\x00" + b"\x00" * 16 + b"\x00\x00\x00\x02" + # 2 Hz b"\x00\x00\x00\x00\x00\x00\x00\x10")) hdlr = Atom.render(b"hdlr", b"\x00" * 8 + soun) mdia = Atom.render(b"mdia", mdhd + hdlr) trak = Atom.render(b"trak", mdia) moov = Atom.render(b"moov", trak) fileobj = cBytesIO(moov) atoms = Atoms(fileobj) info = MP4Info(atoms, fileobj) self.failUnlessEqual(info.length, 8)
def test_mdhd_version_1(self, soun=b"soun"): mdhd = Atom.render( b"mdhd", (b"\x01\x00\x00\x00" + b"\x00" * 16 + b"\x00\x00\x00\x02" + b"\x00\x00\x00\x00\x00\x00\x00\x10"), # 2 Hz ) hdlr = Atom.render(b"hdlr", b"\x00" * 8 + soun) mdia = Atom.render(b"mdia", mdhd + hdlr) trak = Atom.render(b"trak", mdia) moov = Atom.render(b"moov", trak) fileobj = cBytesIO(moov) atoms = Atoms(fileobj) info = MP4Info(atoms, fileobj) self.failUnlessEqual(info.length, 8)
def test_purl(self): # purl can have 0 or 1 flags (implicit or utf8) data = Atom.render(b"data", b"\x00\x00\x00\x01" + b"\x00" * 4 + b"foo") purl = Atom.render(b"purl", data) tags = self.wrap_ilst(purl) self.failUnlessEqual(tags["purl"], ["foo"]) data = Atom.render(b"data", b"\x00\x00\x00\x00" + b"\x00" * 4 + b"foo") purl = Atom.render(b"purl", data) tags = self.wrap_ilst(purl) self.failUnlessEqual(tags["purl"], ["foo"]) # invalid flag data = Atom.render(b"data", b"\x00\x00\x00\x03" + b"\x00" * 4 + b"foo") purl = Atom.render(b"purl", data) tags = self.wrap_ilst(purl) self.assertFalse("purl" in tags) self.assertTrue("purl" in tags._failed_atoms) # invalid utf8 data = Atom.render( b"data", b"\x00\x00\x00\x01" + b"\x00" * 4 + b"\xff") purl = Atom.render(b"purl", data) tags = self.wrap_ilst(purl) self.assertFalse("purl" in tags)
def test_no_audio_tracks(self): data = Atom.render(b"moov", Atom.render(b"udta", b"")) fileobj = BytesIO(data) audio = MP4(fileobj) assert audio.info assert audio.pprint() info = audio.info assert isinstance(info.bitrate, int) assert isinstance(info.length, float) assert isinstance(info.channels, int) assert isinstance(info.sample_rate, int) assert isinstance(info.bits_per_sample, int) assert isinstance(info.codec, str) assert isinstance(info.codec_description, str)
def test_purl(self): # purl can have 0 or 1 flags (implicit or utf8) data = Atom.render(b"data", b"\x00\x00\x00\x01" + b"\x00" * 4 + b"foo") purl = Atom.render(b"purl", data) tags = self.wrap_ilst(purl) self.failUnlessEqual(tags["purl"], ["foo"]) data = Atom.render(b"data", b"\x00\x00\x00\x00" + b"\x00" * 4 + b"foo") purl = Atom.render(b"purl", data) tags = self.wrap_ilst(purl) self.failUnlessEqual(tags["purl"], ["foo"]) # invalid flag data = Atom.render(b"data", b"\x00\x00\x00\x03" + b"\x00" * 4 + b"foo") purl = Atom.render(b"purl", data) tags = self.wrap_ilst(purl) self.assertFalse("purl" in tags) self.assertTrue("purl" in tags._failed_atoms) # invalid utf8 data = Atom.render(b"data", b"\x00\x00\x00\x01" + b"\x00" * 4 + b"\xff") purl = Atom.render(b"purl", data) tags = self.wrap_ilst(purl) self.assertFalse("purl" in tags)
def test_length_0_container(self): data = StringIO(struct.pack(">I4s", 0, "moov") + Atom.render("data", "whee")) atom = Atom(data) self.failUnlessEqual(len(atom.children), 1) self.failUnlessEqual(atom.length, 20) self.failUnlessEqual(atom.children[-1].length, 12)
def test_length_0_container(self): data = BytesIO( struct.pack(">I4s", 0, b"moov") + Atom.render(b"data", b"whee")) atom = Atom(data) self.failUnlessEqual(len(atom.children), 1) self.failUnlessEqual(atom.length, 20) self.failUnlessEqual(atom.children[-1].length, 12)
def test_render_too_big(self): class TooBig(str): def __len__(self): return 1L << 32 data = TooBig("test") try: len(data) except OverflowError: # Py_ssize_t is still only 32 bits on this system. self.failUnlessRaises(OverflowError, Atom.render, "data", data) else: data = Atom.render("data", data) self.failUnlessEqual(len(data), 4 + 4 + 8 + 4)
def test_render_too_big(self): class TooBig(bytes): def __len__(self): return 1 << 32 data = TooBig(b"test") try: len(data) except OverflowError: # Py_ssize_t is still only 32 bits on this system. self.failUnlessRaises(OverflowError, Atom.render, b"data", data) else: data = Atom.render(b"data", data) self.failUnlessEqual(len(data), 4 + 4 + 8 + 4)
def test_multiple_tracks(self): hdlr = Atom.render("hdlr", "\x00" * 8 + "whee") mdia = Atom.render("mdia", hdlr) trak1 = Atom.render("trak", mdia) mdhd = Atom.render("mdhd", ("\x01\x00\x00\x00" + "\x00" * 16 + "\x00\x00\x00\x02" + # 2 Hz "\x00\x00\x00\x00\x00\x00\x00\x10")) hdlr = Atom.render("hdlr", "\x00" * 8 + "soun") mdia = Atom.render("mdia", mdhd + hdlr) trak2 = Atom.render("trak", mdia) moov = Atom.render("moov", trak1 + trak2) fileobj = StringIO(moov) atoms = Atoms(fileobj) info = MP4Info(atoms, fileobj) self.failUnlessEqual(info.length, 8)
def test_multiple_tracks(self): hdlr = Atom.render(b"hdlr", b"\x00" * 8 + b"whee") mdia = Atom.render(b"mdia", hdlr) trak1 = Atom.render(b"trak", mdia) mdhd = Atom.render(b"mdhd", (b"\x01\x00\x00\x00" + b"\x00" * 16 + b"\x00\x00\x00\x02" + # 2 Hz b"\x00\x00\x00\x00\x00\x00\x00\x10")) hdlr = Atom.render(b"hdlr", b"\x00" * 8 + b"soun") mdia = Atom.render(b"mdia", mdhd + hdlr) trak2 = Atom.render(b"trak", mdia) moov = Atom.render(b"moov", trak1 + trak2) fileobj = cBytesIO(moov) atoms = Atoms(fileobj) info = MP4Info(atoms, fileobj) self.failUnlessEqual(info.length, 8)
def test_multiple_tracks(self): hdlr = Atom.render(b"hdlr", b"\x00" * 8 + b"whee") mdia = Atom.render(b"mdia", hdlr) trak1 = Atom.render(b"trak", mdia) mdhd = Atom.render( b"mdhd", (b"\x01\x00\x00\x00" + b"\x00" * 16 + b"\x00\x00\x00\x02" + b"\x00\x00\x00\x00\x00\x00\x00\x10"), # 2 Hz ) hdlr = Atom.render(b"hdlr", b"\x00" * 8 + b"soun") mdia = Atom.render(b"mdia", mdhd + hdlr) trak2 = Atom.render(b"trak", mdia) moov = Atom.render(b"moov", trak1 + trak2) fileobj = cBytesIO(moov) atoms = Atoms(fileobj) info = MP4Info(atoms, fileobj) self.failUnlessEqual(info.length, 8)
def test_multi_freeform(self): # merge multiple freeform tags with the same key mean = Atom.render(b"mean", b"\x00" * 4 + b"net.sacredchao.Mutagen") name = Atom.render(b"name", b"\x00" * 4 + b"foo") data = Atom.render(b"data", b"\x00\x00\x00\x01" + b"\x00" * 4 + b"bar") result = Atom.render(b"----", mean + name + data) data = Atom.render(b"data", b"\x00\x00\x00\x01" + b"\x00" * 4 + b"quux") result += Atom.render(b"----", mean + name + data) tags = self.wrap_ilst(result) values = tags["----:net.sacredchao.Mutagen:foo"] self.assertEqual(values[0], b"bar") self.assertEqual(values[1], b"quux")
def test_freeform_data(self): # https://github.com/quodlibet/mutagen/issues/103 key = "----:com.apple.iTunes:Encoding Params" value = (b"vers\x00\x00\x00\x01acbf\x00\x00\x00\x01brat\x00\x01\xf4" b"\x00cdcv\x00\x01\x05\x04") data = (b"\x00\x00\x00\x1cmean\x00\x00\x00\x00com.apple.iTunes\x00\x00" b"\x00\x1bname\x00\x00\x00\x00Encoding Params\x00\x00\x000data" b"\x00\x00\x00\x00\x00\x00\x00\x00vers\x00\x00\x00\x01acbf\x00" b"\x00\x00\x01brat\x00\x01\xf4\x00cdcv\x00\x01\x05\x04") tags = self.wrap_ilst(Atom.render(b"----", data)) v = tags[key][0] self.failUnlessEqual(v, value) self.failUnlessEqual(v.dataformat, AtomDataType.IMPLICIT) data = MP4Tags()._MP4Tags__render_freeform(key, v) v = self.wrap_ilst(data)[key][0] self.failUnlessEqual(v.dataformat, AtomDataType.IMPLICIT) data = MP4Tags()._MP4Tags__render_freeform(key, value) v = self.wrap_ilst(data)[key][0] self.failUnlessEqual(v.dataformat, AtomDataType.UTF8)
def test_freeform_data(self): # http://code.google.com/p/mutagen/issues/detail?id=103 key = b"----:com.apple.iTunes:Encoding Params" value = (b"vers\x00\x00\x00\x01acbf\x00\x00\x00\x01brat\x00\x01\xf4" b"\x00cdcv\x00\x01\x05\x04") data = (b"\x00\x00\x00\x1cmean\x00\x00\x00\x00com.apple.iTunes\x00\x00" b"\x00\x1bname\x00\x00\x00\x00Encoding Params\x00\x00\x000data" b"\x00\x00\x00\x00\x00\x00\x00\x00vers\x00\x00\x00\x01acbf\x00" b"\x00\x00\x01brat\x00\x01\xf4\x00cdcv\x00\x01\x05\x04") tags = self.wrap_ilst(Atom.render(b"----", data)) v = tags[key][0] self.failUnlessEqual(v, value) self.failUnlessEqual(v.dataformat, MP4FreeForm.FORMAT_DATA) data = MP4Tags()._MP4Tags__render_freeform(key, v) v = self.wrap_ilst(data)[key][0] self.failUnlessEqual(v.dataformat, MP4FreeForm.FORMAT_DATA) data = MP4Tags()._MP4Tags__render_freeform(key, value) v = self.wrap_ilst(data)[key][0] self.failUnlessEqual(v.dataformat, MP4FreeForm.FORMAT_TEXT)
def test_freeform_data(self): # http://code.google.com/p/mutagen/issues/detail?id=103 key = "----:com.apple.iTunes:Encoding Params" value = ("vers\x00\x00\x00\x01acbf\x00\x00\x00\x01brat\x00\x01\xf4" "\x00cdcv\x00\x01\x05\x04") data = ("\x00\x00\x00\x1cmean\x00\x00\x00\x00com.apple.iTunes\x00\x00" "\x00\x1bname\x00\x00\x00\x00Encoding Params\x00\x00\x000data" "\x00\x00\x00\x00\x00\x00\x00\x00vers\x00\x00\x00\x01acbf\x00" "\x00\x00\x01brat\x00\x01\xf4\x00cdcv\x00\x01\x05\x04") tags = self.wrap_ilst(Atom.render("----", data)) v = tags[key][0] self.failUnlessEqual(v, value) self.failUnlessEqual(v.dataformat, MP4FreeForm.FORMAT_DATA) data = MP4Tags()._MP4Tags__render_freeform(key, v) v = self.wrap_ilst(data)[key][0] self.failUnlessEqual(v.dataformat, MP4FreeForm.FORMAT_DATA) data = MP4Tags()._MP4Tags__render_freeform(key, value) v = self.wrap_ilst(data)[key][0] self.failUnlessEqual(v.dataformat, MP4FreeForm.FORMAT_TEXT)
def test_genre_too_big(self): data = Atom.render("data", "\x00" * 8 + "\x01\x00") genre = Atom.render("gnre", data) tags = self.wrap_ilst(genre) self.failIf("gnre" in tags) self.failIf("\xa9gen" in tags)
def test_bad_freeform(self): mean = Atom.render(b"mean", b"net.sacredchao.Mutagen") name = Atom.render(b"name", b"empty test key") bad_freeform = Atom.render(b"----", b"\x00" * 4 + mean + name) tags = self.wrap_ilst(bad_freeform) self.assertFalse(tags)
def test_bad_cprt(self): data = Atom.render(b"cprt", b"\x00\x00\x00#data\x00") tags = self.wrap_ilst(data) self.assertFalse(tags)
def test_bad_covr(self): data = Atom.render(b"foob", b"\x00\x00\x00\x0E" + b"\x00" * 4 + b"whee") covr = Atom.render(b"covr", data) tags = self.wrap_ilst(covr) self.assertFalse(tags)
def test_genre_too_big(self): data = Atom.render(b"data", b"\x00" * 8 + b"\x01\x00") genre = Atom.render(b"gnre", data) tags = self.wrap_ilst(genre) self.failIf("gnre" in tags) self.failIf("\xa9gen" in tags)
def test_genre(self): data = Atom.render(b"data", b"\x00" * 8 + b"\x00\x01") genre = Atom.render(b"gnre", data) tags = self.wrap_ilst(genre) self.failIf("gnre" in tags) self.failUnlessEqual(tags["\xa9gen"], ["Blues"])
def wrap_ilst(self, data): ilst = Atom.render(b"ilst", data) meta = Atom.render(b"meta", b"\x00" * 4 + ilst) data = Atom.render(b"moov", Atom.render(b"udta", meta)) fileobj = cBytesIO(data) return MP4Tags(Atoms(fileobj), fileobj)
def wrap_ilst(self, data): ilst = Atom.render("ilst", data) meta = Atom.render("meta", "\x00" * 4 + ilst) data = Atom.render("moov", Atom.render("udta", meta)) fileobj = StringIO(data) return MP4Tags(Atoms(fileobj), fileobj)
def test_genre(self): data = Atom.render("data", "\x00" * 8 + "\x00\x01") genre = Atom.render("gnre", data) tags = self.wrap_ilst(genre) self.failIf("gnre" in tags) self.failUnlessEqual(tags["\xa9gen"], ["Blues"])
def test_bad_freeform(self): mean = Atom.render("mean", "net.sacredchao.Mutagen") name = Atom.render("name", "empty test key") bad_freeform = Atom.render("----", "\x00" * 4 + mean + name) self.failUnlessRaises(MP4MetadataError, self.wrap_ilst, bad_freeform)
def test_bad_covr(self): data = Atom.render("foob", "\x00\x00\x00\x0E" + "\x00" * 4 + "whee") covr = Atom.render("covr", data) self.failUnlessRaises(MP4MetadataError, self.wrap_ilst, covr)
def test_strips_unknown_types(self): data = Atom.render("data", "\x00" * 8 + "whee") foob = Atom.render("foob", data) tags = self.wrap_ilst(foob) self.failIf(tags)
def test_no_tracks(self): moov = Atom.render(b"moov", b"") fileobj = BytesIO(moov) atoms = Atoms(fileobj) with self.assertRaises(MP4StreamInfoError): MP4Info(atoms, fileobj)
def wrap_ilst(self, data): ilst = Atom.render(b"ilst", data) meta = Atom.render(b"meta", b"\x00" * 4 + ilst) data = Atom.render(b"moov", Atom.render(b"udta", meta)) fileobj = BytesIO(data) return MP4Tags(Atoms(fileobj), fileobj)
def test_extra_trailing_data(self): data = StringIO(Atom.render("data", "whee") + "\x00\x00") self.failUnless(Atoms(data))
def test_empty_cpil(self): cpil = Atom.render(b"cpil", Atom.render(b"data", b"\x00" * 8)) tags = self.wrap_ilst(cpil) self.assertFalse("cpil" in tags)
def test_strips_bad_unknown_types(self): data = Atom.render(b"datA", b"\x00" * 8 + b"whee") foob = Atom.render(b"foob", data) tags = self.wrap_ilst(foob) self.failIf(tags)
def test_bad_text_data(self): data = Atom.render(b"datA", b"\x00\x00\x00\x01\x00\x00\x00\x00whee") data = Atom.render(b"aART", data) tags = self.wrap_ilst(data) self.assertFalse(tags)
def test_bad_text_data(self): data = Atom.render("datA", "\x00\x00\x00\x01\x00\x00\x00\x00whee") data = Atom.render("aART", data) self.failUnlessRaises(MP4MetadataError, self.wrap_ilst, data)
def test_extra_trailing_data(self): data = BytesIO(Atom.render(b"data", b"whee") + b"\x00\x00") self.failUnless(Atoms(data))
def test_empty_cpil(self): cpil = Atom.render("cpil", Atom.render("data", "\x00" * 8)) tags = self.wrap_ilst(cpil) self.failUnless("cpil" in tags) self.failIf(tags["cpil"])
def test_bad_covr(self): data = Atom.render( b"foob", b"\x00\x00\x00\x0E" + b"\x00" * 4 + b"whee") covr = Atom.render(b"covr", data) tags = self.wrap_ilst(covr) self.assertFalse(tags)