diff --git a/src/octoprint/filemanager/storage.py b/src/octoprint/filemanager/storage.py index 9f238cf9..273107ff 100644 --- a/src/octoprint/filemanager/storage.py +++ b/src/octoprint/filemanager/storage.py @@ -977,13 +977,29 @@ class LocalFileStorage(StorageInterface): # folder recursion elif os.path.isdir(entry_path) and recursive: - sub_result = self._list_folder(entry_path, filter=filter) - result[entry] = dict( + sub_result = self._list_folder(entry_path, filter=filter, recursive=recursive) + entry_data = dict( name=entry, type="folder", children=sub_result ) + if not filter or filter(entry, entry_data): + def get_size(start_path): + total_size = 0 + for dirpath, dirnames, filenames in os.walk(start_path): + for f in filenames: + fp = os.path.join(dirpath, f) + total_size += os.path.getsize(fp) + return total_size + + # only add folders passing the optional filter + extended_entry_data = dict() + extended_entry_data.update(entry_data) + extended_entry_data["size"] = get_size(entry_path) + + result[entry] = extended_entry_data + # TODO recreate links if we have metadata less entries # save metadata diff --git a/src/octoprint/server/api/files.py b/src/octoprint/server/api/files.py index 33ae4efd..2804f5d5 100644 --- a/src/octoprint/server/api/files.py +++ b/src/octoprint/server/api/files.py @@ -26,10 +26,16 @@ import psutil @api.route("/files", methods=["GET"]) def readGcodeFiles(): filter = None + recursive = False if "filter" in request.values: filter = request.values["filter"] - files = _getFileList(FileDestinations.LOCAL, filter=filter) + + if "recursive" in request.values: + recursive = request.values["recursive"] == 'true' + + files = _getFileList(FileDestinations.LOCAL, filter=filter, recursive=recursive) files.extend(_getFileList(FileDestinations.SDCARD)) + usage = psutil.disk_usage(settings().getBaseFolder("uploads")) return jsonify(files=files, free=usage.free, total=usage.total) @@ -39,7 +45,11 @@ def readGcodeFilesForOrigin(origin): if origin not in [FileDestinations.LOCAL, FileDestinations.SDCARD]: return make_response("Unknown origin: %s" % origin, 404) - files = _getFileList(origin) + recursive = False + if "recursive" in request.values: + recursive = request.values["recursive"] == 'true' + + files = _getFileList(origin, recursive=recursive) if origin == FileDestinations.LOCAL: usage = psutil.disk_usage(settings().getBaseFolder("uploads")) @@ -48,15 +58,24 @@ def readGcodeFilesForOrigin(origin): return jsonify(files=files) -def _getFileDetails(origin, filename): - files = _getFileList(origin) - for file in files: - if file["name"] == filename: - return file - return None +def _getFileDetails(origin, path): + files = _getFileList(origin, recursive=True) + path = path.split('/') + + def recursive_get_filedetails(files, path): + for file in files: + if file["name"] == path[0]: + if len(path) > 1: + return recursive_get_filedetails(file["children"], path[1:]) + else: + return file + + return None + + return recursive_get_filedetails(files, path) -def _getFileList(origin, filter=None): +def _getFileList(origin, filter=None, recursive=False): if origin == FileDestinations.SDCARD: sdFileList = printer.get_sd_files() @@ -78,45 +97,56 @@ def _getFileList(origin, filter=None): filter_func = None if filter: filter_func = lambda entry, entry_data: octoprint.filemanager.valid_file_type(entry, type=filter) - files = fileManager.list_files(origin, filter=filter_func, recursive=False)[origin].values() - for file in files: - file["origin"] = FileDestinations.LOCAL - if "analysis" in file and octoprint.filemanager.valid_file_type(file["name"], type="gcode"): - file["gcodeAnalysis"] = file["analysis"] - del file["analysis"] + files = fileManager.list_files(origin, filter=filter_func, recursive=recursive)[origin].values() - if "history" in file and octoprint.filemanager.valid_file_type(file["name"], type="gcode"): - # convert print log - history = file["history"] - del file["history"] - success = 0 - failure = 0 - last = None - for entry in history: - success += 1 if "success" in entry and entry["success"] else 0 - failure += 1 if "success" in entry and not entry["success"] else 0 - if not last or ("timestamp" in entry and "timestamp" in last and entry["timestamp"] > last["timestamp"]): - last = entry - if last: - prints = dict( - success=success, - failure=failure, - last=dict( - success=last["success"], - date=last["timestamp"] + def recursive_analysis(files, path): + for file in files: + file["origin"] = FileDestinations.LOCAL + + if file["type"] == "folder": + file["children"] = recursive_analysis(file["children"].values(), path + file["name"] + "/") + + if "analysis" in file and octoprint.filemanager.valid_file_type(file["name"], type="gcode"): + file["gcodeAnalysis"] = file["analysis"] + del file["analysis"] + + if "history" in file and octoprint.filemanager.valid_file_type(file["name"], type="gcode"): + # convert print log + history = file["history"] + del file["history"] + success = 0 + failure = 0 + last = None + for entry in history: + success += 1 if "success" in entry and entry["success"] else 0 + failure += 1 if "success" in entry and not entry["success"] else 0 + if not last or ("timestamp" in entry and "timestamp" in last and entry["timestamp"] > last["timestamp"]): + last = entry + if last: + prints = dict( + success=success, + failure=failure, + last=dict( + success=last["success"], + date=last["timestamp"] + ) ) - ) - if "printTime" in last: - prints["last"]["printTime"] = last["printTime"] - file["prints"] = prints + if "printTime" in last: + prints["last"]["printTime"] = last["printTime"] + file["prints"] = prints + + file.update({ + "refs": { + "resource": url_for(".readGcodeFile", target=FileDestinations.LOCAL, filename=path + file["name"], _external=True), + "download": url_for("index", _external=True) + "downloads/files/" + FileDestinations.LOCAL + "/" + path + file["name"] + } + }) + + return files + + files = recursive_analysis(files, "") - file.update({ - "refs": { - "resource": url_for(".readGcodeFile", target=FileDestinations.LOCAL, filename=file["name"], _external=True), - "download": url_for("index", _external=True) + "downloads/files/" + FileDestinations.LOCAL + "/" + file["name"] - } - }) return files @@ -166,24 +196,33 @@ def uploadGcodeFile(target): # determine current job currentFilename = None + currentFullPath = None currentOrigin = None currentJob = printer.get_current_job() if currentJob is not None and "file" in currentJob.keys(): currentJobFile = currentJob["file"] - if "name" in currentJobFile.keys() and "origin" in currentJobFile.keys(): - currentFilename = currentJobFile["name"] + if currentJobFile is not None and "name" in currentJobFile.keys() and "origin" in currentJobFile.keys() and currentJobFile["name"] is not None and currentJobFile["origin"] is not None: + currentPath, currentFilename = fileManager.sanitize(currentJobFile["origin"], currentJobFile["name"]) + currentFullPath = fileManager.join_path(target, currentPath, currentFilename) currentOrigin = currentJobFile["origin"] # determine future filename of file to be uploaded, abort if it can't be uploaded try: - futureFilename = fileManager.sanitize_name(FileDestinations.LOCAL, upload.filename) + futurePath, futureFilename = fileManager.sanitize(target, upload.filename) except: + futurePath = None futureFilename = None + if futureFilename is None: return make_response("Can not upload file %s, wrong format?" % upload.filename, 415) + if "path" in request.values: + futurePath = fileManager.sanitize_path(target, request.values["path"]) + + futureFullPath = fileManager.join_path(target, futurePath, futureFilename) + # prohibit overwriting currently selected file while it's being printed - if futureFilename == currentFilename and target == currentOrigin and printer.is_printing() or printer.is_paused(): + if futureFullPath == currentFullPath and target == currentOrigin and printer.is_printing() or printer.is_paused(): return make_response("Trying to overwrite file that is currently being printed: %s" % currentFilename, 409) def fileProcessingFinished(filename, absFilename, destination): @@ -212,7 +251,7 @@ def uploadGcodeFile(target): if octoprint.filemanager.valid_file_type(added_file, "gcode") and (selectAfterUpload or printAfterSelect or (currentFilename == filename and currentOrigin == destination)): printer.select_file(absFilename, destination == FileDestinations.SDCARD, printAfterSelect) - added_file = fileManager.add_file(FileDestinations.LOCAL, upload.filename, upload, allow_overwrite=True) + added_file = fileManager.add_file(FileDestinations.LOCAL, futureFullPath, upload, allow_overwrite=True) if added_file is None: return make_response("Could not upload the file %s" % upload.filename, 500) if octoprint.filemanager.valid_file_type(added_file, "stl"):