def test_make_v1_from_tyer(self): self.assertEquals( MakeID3v1({"TDRC": TDRC(text="2010-10-10")}), MakeID3v1({"TYER": TYER(text="2010")})) self.assertEquals( ParseID3v1(MakeID3v1({"TDRC": TDRC(text="2010-10-10")})), ParseID3v1(MakeID3v1({"TYER": TYER(text="2010")})))
def test_v1_not_v11(self): self.id3["TRCK"] = TRCK(encoding=0, text="32") tag = MakeID3v1(self.id3) self.failUnless(32, ParseID3v1(tag)["TRCK"]) del (self.id3["TRCK"]) tag = MakeID3v1(self.id3) tag = tag[:125] + b' ' + tag[-1:] self.failIf("TRCK" in ParseID3v1(tag))
def test_load_v1_v2_tcon_translate(self): tags = ID3() tags.add(TCON(text=["12"])) v1_data = MakeID3v1(tags) filename = get_temp_copy(self.empty) try: tags = ID3() tags.save(filename=filename, v1=0) with open(filename, "ab") as h: h.write(v1_data) tags = ID3(filename, load_v1=True) assert tags["TCON"][0] == "Other" tags = ID3(filename, load_v1=False) assert "TCON" not in tags finally: os.unlink(filename)
insert_bytes(f, outsize - insize, insize + 10) f.seek(0) f.write(data) try: f.seek(-128, 2) except IOError, err: from errno import EINVAL if err.errno != EINVAL: raise f.seek(0, 2) # ensure read won't get "TAG" if f.read(3) == "TAG": f.seek(-128, 2) if v1 > 0: f.write(MakeID3v1(self)) else: f.truncate() elif v1 == 2: f.seek(0, 2) f.write(MakeID3v1(self)) finally: f.close() def __save_frame(self, frame, v2): flags = 0 if self.PEDANTIC and isinstance(frame, TextFrame): if len(str(frame)) == 0: return '' framedata = frame._writeData()
def test_v1_genre(self): tag = {} tag["TCON"] = TCON(encoding=0, text="Pop") v1tag = MakeID3v1(tag) self.failUnlessEqual(ParseID3v1(v1tag)["TCON"].genres, ["Pop"])
def test_invalid_track(self): tag = {} tag["TRCK"] = TRCK(encoding=0, text="not a number") v1tag = MakeID3v1(tag) self.failIf("TRCK" in ParseID3v1(v1tag))
def test_make_from_empty(self): empty = b'TAG' + b'\x00' * 124 + b'\xff' self.assertEquals(MakeID3v1({}), empty) self.assertEquals(MakeID3v1({'TCON': TCON()}), empty) self.assertEquals(MakeID3v1({'COMM': COMM(encoding=0, text="")}), empty)
def test_roundtrip(self): frames = {} for key in ["TIT2", "TALB", "TPE1", "TDRC"]: frames[key] = self.id3[key] self.assertEquals(ParseID3v1(MakeID3v1(frames)), frames)
def test_long(self): s = MakeID3v1(dict(TDRC="123456789")) self.failUnlessEqual(len(s), 128) tag = ParseID3v1(s) self.failUnlessEqual(tag["TDRC"], "1234")
def test_short(self): s = MakeID3v1(dict(TDRC="1")) self.failUnlessEqual(len(s), 128) tag = ParseID3v1(s) self.failUnlessEqual(tag["TDRC"], "0001")
def test_empty(self): s = MakeID3v1(dict(TDRC="")) self.failUnlessEqual(len(s), 128) tag = ParseID3v1(s) self.failIf("TDRC" in tag)
def test_none(self): s = MakeID3v1(dict()) self.failUnlessEqual(len(s), 128) tag = ParseID3v1(s) self.failIf("TDRC" in tag)
if (insize < outsize): insert_bytes(f, outsize-insize, insize+10) f.seek(0) f.write(data) try: f.seek(-128, 2) except IOError, err: from errno import EINVAL if err.errno != EINVAL: raise f.seek(0, 2) # ensure read won't get "TAG" if f.read(3) == "TAG": f.seek(-128, 2) if v1 > 0: f.write(MakeID3v1(self)) else: f.truncate() elif v1 == 2: f.seek(0, 2) f.write(MakeID3v1(self)) finally: f.close() def __save_frame(self, frame, v2): flags = 0 if self.PEDANTIC and isinstance(frame, TextFrame): if len(str(frame)) == 0: return '' framedata = frame._writeData() if v2 == 3: bits=8 else: bits=7
def save(self, filename=None, v1=1, v2_version=4): """Save changes to a file. If no filename is given, the one most recently loaded is used. Keyword arguments: v1 -- if 0, ID3v1 tags will be removed if 1, ID3v1 tags will be updated but not added if 2, ID3v1 tags will be created and/or updated v2_version -- version of ID3v2 tags (3 or 4). By default Mutagen saves ID3v2.4 tags. If you want to save ID3v2.3 tags, you must call method update_to_v23 before saving the file. The lack of a way to update only an ID3v1 tag is intentional. """ # Sort frames by 'importance' order = ["TIT2", "TPE1", "TRCK", "TALB", "TPOS", "TDRC", "TCON"] order = dict(zip(order, range(len(order)))) last = len(order) frames = self.items() frames.sort(lambda a, b: cmp(order.get(a[0][:4], last), order.get(b[0][:4], last))) framedata = [ self.__save_frame(frame, v2_version) for (key, frame) in frames ] framedata.extend( [data for data in self.unknown_frames if len(data) > 10]) if not framedata: try: self.delete(filename) except EnvironmentError as err: from errno import ENOENT if err.errno != ENOENT: raise return framedata = ''.join(framedata) framesize = len(framedata) if filename is None: filename = self.filename try: f = open(filename, 'rb+') except IOError as err: from errno import ENOENT if err.errno != ENOENT: raise f = open(filename, 'ab') # create, then reopen f = open(filename, 'rb+') try: idata = f.read(10) try: id3, vmaj, vrev, flags, insize = unpack('>3sBBB4s', idata) except struct.error: id3, insize = '', 0 insize = BitPaddedInt(insize) if id3 != 'ID3': insize = -10 if insize >= framesize: outsize = insize else: outsize = (framesize + 1023) & ~0x3FF framedata += '\x00' * (outsize - framesize) framesize = BitPaddedInt.to_str(outsize, width=4) flags = 0 header = pack('>3sBBB4s', 'ID3', v2_version, 0, flags, framesize) data = header + framedata if (insize < outsize): insert_bytes(f, outsize - insize, insize + 10) f.seek(0) f.write(data) try: f.seek(-128, 2) except IOError as err: from errno import EINVAL if err.errno != EINVAL: raise f.seek(0, 2) # ensure read won't get "TAG" if f.read(3) == "TAG": f.seek(-128, 2) if v1 > 0: f.write(MakeID3v1(self)) else: f.truncate() elif v1 == 2: f.seek(0, 2) f.write(MakeID3v1(self)) finally: f.close()
def save(self, filename=None, v1=0): """Save changes to a file. If no filename is given, the one most recently loaded is used. Keyword arguments: v1 -- if 0, ID3v1 tags will be removed if 1, ID3v1 tags will be updated but not added if 2, ID3v1 tags will be created and/or updated The lack of a way to update only an ID3v1 tag is intentional. """ # Sort frames by 'importance' order = ["TIT2", "TPE1", "TRCK", "TALB", "TPOS", "TDRC", "TCON"] order = dict(zip(order, range(len(order)))) last = len(order) frames = self.items() frames.sort(lambda a, b: cmp(order.get(a[0][:4], last), order.get(b[0][:4], last))) framedata = [self.__save_frame(frame) for (key, frame) in frames] framedata.extend([data for data in self.unknown_frames if len(data) > 10]) framedata = ''.join(framedata) framesize = len(framedata) if filename is None: filename = self.filename f = open(filename, 'rb+') try: idata = f.read(10) try: id3, vmaj, vrev, flags, insize = struct.unpack('>3sBBB4s', idata) except struct.error: id3, insize = '', 0 insize = BitPaddedInt(insize) if id3 != 'ID3': insize = -10 if insize >= framesize: outsize = insize else: outsize = (framesize + 1023) & ~0x3FF framedata += '\x00' * (outsize - framesize) framesize = BitPaddedInt.to_str(outsize, width=4) flags = 0 header = struct.pack('>3sBBB4s', 'ID3', 4, 0, flags, framesize) data = header + framedata if (insize < outsize): insert_bytes(f, outsize-insize, insize+10) f.seek(0) try: f.seek(-128, 2) except IOError, err: if err.errno != EINVAL: raise f.seek(0, 2) # ensure read won't get "TAG" if f.read(3) == "TAG": f.seek(-128, 2) if v1 > 0: f.write(MakeID3v1(self)) else: f.truncate() elif v1 == 2: f.seek(0, 2) f.write(MakeID3v1(self))