class MusicHash(object): subatoms = [['moov'], ['trak', 'udta'], ['mdia', 'meta'], ['minf', 'ilst'], ['stbl'], ['stsd'], []] atoms_to_hash = ['mdat', 'oboe', 'apID'] def __init__(self, data): self.data = data self.hash_func = md5.new() self._hash = None def hash__get(self): if self._hash != None: return self._hash return self.calculate_hash() hash = property(hash__get) def calculate_hash(self): if data[4:12] == 'ftypM4A ': self.hash_m4a() else: self.hash_default() self._hash = self.hash_func.hexdigest() return self._hash def hash_default(self): self.hash_func.update(self.data) def hash_m4a(self): self.drm = False self.parse_data(self.data, 0) def parse_data(self, data, depth): loc = 0 while loc < len(data): size = struct.unpack('>L', data[loc:loc+4])[0] if size == 0: atom = data[loc+4:loc+8] subdata = data[loc+8:] break atom = data[loc+4:loc+8] subdata = data[loc+8:loc+size] if atom in self.subatoms[depth]: if atom == 'meta': self.parse_data(subdata[4:], depth+1) elif atom == 'stsd': self.parse_data(subdata[8:], depth +1) else: self.parse_data(subdata, depth + 1) if atom == 'drms': self.drm = True if atom in self.atoms_to_hash: if atom == 'apID' and not self.drm: self.hash_func.update(subdata) elif atom != 'apID': self.hash_func.update(data[loc+4:loc+4+size]) loc += size