def load(path): if not os.path.isdir(os.path.join(path, ".soundclip")): raise FileNotFoundError("Path does not exist or not a soundclip project") with open(os.path.join(path, ".soundclip", "project.json"), "rt") as dbobj: content = dbobj.read() if not content: raise ProjectParserException({ "message": "The project is corrupted (project.json was empty)!", "path": path }) j = json.loads(content) name = j['name'] if 'name' in j else "Untitled Project" creator = j['creator'] if 'creator' in j else "" panic_fade_time = j['panicFadeTime'] if 'panicFadeTime' in j else 500 panic_hard_stop_time = j['panicHardStopTime'] if 'panicHardStopTime' in j else 1000 eps = j['discoveryEpsilon'] if 'discoveryEpsilon' in j else 5 last_hash = j['previousRevision'] if 'previousRevision' in j else None p = Project(name=name, creator=creator, root=path, cue_stacks=[], panic_fade_time=panic_fade_time, panic_hard_stop_time=panic_hard_stop_time, current_hash=sha(content), last_hash=last_hash, max_duration_discovery_difference=eps) if 'stacks' in j: for key in j['stacks']: p += CueStack.load(path, key, p) return p
def read(root, key, force_reload=False): """ Reads an object from the database, returning its json content. Like git, objects are keyed by the sha1 hash of their content. The first two bytes of the hash refer to the sub directory of the objects store, the remaining 40 bytes are the name of the file. :param root: The project root directory :param key: The checksum of the object to read :return: the json content of the specified object """ if key in __CACHE and not force_reload: logger.debug("Loading {0} from object cache".format(key)) return __CACHE[key] else: logger.debug("Cache-Miss: {0} not yet in object cache".format(key)) path = os.path.join(root, '.soundclip', 'objects', key[0:2], key[2:40]) logger.debug("Asked to load {0}, looking for {1}".format(key, path)) if not os.path.exists(path): raise FileNotFoundError("The specified object doesn't exist in the database!") with open(path, "rt") as dbobj: content = dbobj.read().strip() if not content: raise IllegalObjectException({ "message": "The object read from the database returned no content. There was probably an error storing it.", "key": key }) checksum = sha(content) if checksum != key: raise ChecksumMismatchException({ "message": "Cryptographic Checksum Mismatch. Was expecting {0}, got {1}".format(key, checksum), "key": key, "checksum": checksum }) obj = json.loads(content) __CACHE[key] = obj return obj
def write(root, d, current_hash): """ Writes an object to the database, returning its sha1 checksum. Like git, objects are keyed by the sha1 hash of their content. The first two bytes of the hash refer to the sub directory of the objects store, the remaining 40 bytes are the name of the file. :param root: The project root directory :param d: The dictionary to serialize :return: the sha1 checksum that refers to this project """ if 'previousRevision' not in d: d['previousRevision'] = '' s = json.dumps(d, sort_keys=True).strip() checksum = sha(s) logger.debug("Asked to store {0} (current has was {1}, checksum: {2})".format(s, current_hash, checksum)) path = os.path.join(root, '.soundclip', 'objects', checksum[0:2]) if not os.path.exists(path): os.makedirs(path) object_path = os.path.join(path, checksum[2:40]) # No need to write duplicate objects if checksum == current_hash and os.path.exists(object_path): logger.debug("{0} is already in the object store, skipping".format(checksum)) return checksum, d['previousRevision'] d['previousRevision'] = current_hash logger.debug("Writing {0}".format(object_path)) with open(object_path, "w") as f: f.write(s) f.write('\n') return checksum, current_hash