import os import mimetypes from flask import Flask, render_template, request, jsonify, send_file, abort app = Flask(__name__) # Configuration IGNORE_FOLDERS = {'.git', '__pycache__', 'node_modules', '$RECYCLE.BIN', 'System Volume Information'} def get_file_info(filepath): """Helper to extract file details.""" try: stats = os.stat(filepath) mime_type, _ = mimetypes.guess_type(filepath) return { "name": os.path.basename(filepath), "path": filepath, "size": stats.st_size, "is_dir": os.path.isdir(filepath), "mime": mime_type or "application/octet-stream" } except PermissionError: return None @app.route('/') def index(): """Serves the main UI.""" # Default to current working directory # start_path = os.getcwd() return render_template('index.html', start_path='/toor') @app.route('/api/scan') def scan_files(): """ Scans a directory. Query Params: - path: The root path to scan. - recursive: 'true' to scan subfolders, 'false' for just top level. """ root_path = request.args.get('path', '.') recursive = request.args.get('recursive', 'false') == 'true' if not os.path.exists(root_path): return jsonify({"error": "Path does not exist"}), 404 file_list = [] try: if recursive: # Recursive walk (Limit to avoid freezing on massive drives) file_count = 0 MAX_FILES = 2000 for root, dirs, files in os.walk(root_path): # Filter out ignored folders dirs[:] = [d for d in dirs if d not in IGNORE_FOLDERS] for name in files: if file_count >= MAX_FILES: break full_path = os.path.join(root, name) info = get_file_info(full_path) if info: file_list.append(info) file_count += 1 if file_count >= MAX_FILES: break else: # Standard directory listing (Non-recursive) print(f"Scanning {root_path}") with os.scandir(root_path) as entries: for entry in entries: info = get_file_info(entry.path) if info: file_list.append(info) except Exception as e: return jsonify({"error": str(e)}), 500 # Sort: Directories first, then files file_list.sort(key=lambda x: (not x['is_dir'], x['name'].lower())) return jsonify({"files": file_list, "current_path": root_path}) @app.route('/api/download') def download_file(): """Downloads a specific file.""" filepath = request.args.get('path') if not filepath or not os.path.exists(filepath): return abort(404) return send_file(filepath, as_attachment=True) @app.route('/api/preview') def preview_file(): """Serves file content for preview (images/audio).""" filepath = request.args.get('path') if not filepath or not os.path.exists(filepath): return abort(404) return send_file(filepath, as_attachment=False) @app.route('/api/delete', methods=['DELETE']) def delete_file(): """Deletes a file.""" filepath = request.args.get('path') if not filepath or not os.path.exists(filepath): return jsonify({"error": "File not found"}), 404 try: if os.path.isdir(filepath): os.rmdir(filepath) # Only removes empty dirs for safety else: os.remove(filepath) return jsonify({"success": True}) except Exception as e: return jsonify({"error": str(e)}), 500 @app.route('/api/delete_multiple', methods=['DELETE']) def delete_multiple_files(): """Deletes multiple files.""" data = request.get_json() paths = data.get('paths', []) if not paths: return jsonify({"error": "No file paths provided"}), 400 errors = [] success_count = 0 for path in paths: if not path or not os.path.exists(path): errors.append({"path": path, "error": "File not found"}) continue try: if os.path.isdir(path): errors.append({"path": path, "error": "Cannot delete directories"}) else: os.remove(path) success_count += 1 except Exception as e: errors.append({"path": path, "error": str(e)}) if not errors: return jsonify({"success": True, "message": f"{success_count} files deleted."}) else: return jsonify({"success": False, "error": "Some files could not be deleted", "details": errors}), 500 if __name__ == '__main__': # Run on localhost print("Starting File Explorer on http://127.0.0.1:5005") app.run(debug=True, port=5005, host='0.0.0.0')