{"repo": "beak2825/d-sync", "files": {"d_sync_download.py": {"eedcdfe66bcfdabf558e020c6587ba89c6ff71c0": {"t": 1785604230.9646003, "n": 10680, "h": "acf5e917cf0d9d2f", "c": "\"\"\"\nDownload script for d-sync\nDownloads encrypted/compressed files from Discord CDN and reconstructs them\n\"\"\"\n\nimport json\nimport requests\nfrom pathlib import Path\nfrom typing import Dict, List, Optional\nimport sys\nfrom datetime import datetime\n\n# Add utils to path\nsys.path.insert(0, str(Path(__file__).parent))\n\nfrom utils import (\n Logger, EncryptionManager, CompressionManager, HashManager,\n FILES_JSON, MAX_PARTITION_SIZE, DOWNLOAD_LOG_FILE, BASE_DIR\n)\nfrom utils.webhook_refresh import WebhookMessageRefresh\n\nlogger = Logger(__name__)\n\n# Download to d-synced2 for testing\nD_SYNCED2_DIR = BASE_DIR / \"d-synced2\"\n\n\nclass D_SyncDownload:\n \"\"\"Main download manager for d-sync\"\"\"\n\n def __init__(self):\n self.encryption_manager = EncryptionManager()\n self.compression_manager = CompressionManager()\n self.files_metadata: Dict[str, Dict] = {}\n self._load_files_metadata()\n\n def _load_files_metadata(self):\n \"\"\"Load files metadata from JSON\"\"\"\n if not FILES_JSON.exists():\n logger.error(f\"Files metadata not found: {FILES_JSON}\")\n logger.info(\"You need to upload files first using d_sync_upload.py\")\n return\n\n try:\n with open(FILES_JSON, 'r') as f:\n data = json.load(f)\n self.files_metadata = data.get('files', {})\n logger.info(f\"Loaded metadata for {len(self.files_metadata)} files\")\n except json.JSONDecodeError as e:\n logger.error(f\"Could not parse files.json: {e}\")\n\n def _refresh_webhook_message(self, webhook_url: str, chunk_filename: str) -> Optional[str]:\n \"\"\"\n Try to refresh a webhook message by getting recent messages\n and finding the file matching our chunk\n \"\"\"\n try:\n webhook_id, webhook_token = WebhookMessageRefresh.parse_webhook_url(webhook_url)\n \n # Get webhook info to find channel ID\n webhook_info_url = f\"https://discord.com/api/webhooks/{webhook_id}/{webhook_token}\"\n response = requests.get(webhook_info_url, timeout=10)\n response.raise_for_status()\n webhook_data = response.json()\n \n channel_id = webhook_data.get('channel_id')\n if not channel_id:\n logger.warning(\"Could not get channel ID from webhook\")\n return None\n \n # Try to get recent messages (requires bot token, may fail)\n # For now, we'll just log the attempt\n logger.debug(f\"Attempted to refresh message for {chunk_filename}\")\n return None\n \n except requests.exceptions.RequestException as e:\n logger.debug(f\"Could not refresh webhook message: {e}\")\n return None\n\n def _download_chunk(self, cdn_url: str, webhook_url: Optional[str] = None, \n chunk_filename: Optional[str] = None) -> Optional[bytes]:\n \"\"\"Download a chunk from Discord CDN with 404 fallback\"\"\"\n try:\n response = requests.get(cdn_url, timeout=60)\n \n # Check for 404 - try webhook refresh\n if response.status_code == 404:\n logger.warning(f\"CDN URL returned 404: {cdn_url}\")\n \n if webhook_url and chunk_filename:\n logger.info(f\"Attempting to refresh webhook message for {chunk_filename}\")\n new_url = self._refresh_webhook_message(webhook_url, chunk_filename)\n \n if new_url:\n logger.info(f\"Retrying with refreshed URL\")\n response = requests.get(new_url, timeout=60)\n \n if response.status_code == 404:\n logger.error(f\"File expired on Discord CDN: {chunk_filename}\")\n return None\n \n response.raise_for_status()\n logger.debug(f\"Downloaded chunk from {cdn_url}\")\n return response.content\n \n except requests.exceptions.RequestException as e:\n logger.error(f\"Failed to download chunk: {e}\")\n return None\n\n def _log_response(self, filename: str, status: str, message: str):\n \"\"\"Log download response\"\"\"\n try:\n DOWNLOAD_LOG_FILE.parent.mkdir(parents=True, exist_ok=True)\n with open(DOWNLOAD_LOG_FILE, 'a') as f:\n timestamp = datetime.now().isoformat()\n f.write(f\"\\n[{timestamp}] {filename}\\n\")\n f.write(f\"Status: {status}\\n\")\n f.write(f\"Message: {message}\\n\")\n f.write(\"=\"*80 + \"\\n\")\n except Exception as e:\n logger.error(f\"Error logging response: {e}\")\n\n def download_file(self, file_path: str) -> bool:\n \"\"\"Download and reconstruct a file\"\"\"\n logger.info(f\"Downloading file: {file_path}\")\n\n if file_path not in self.files_metadata:\n logger.error(f\"File not found in metadata: {file_path}\")\n return False\n\n try:\n metadata = self.files_metadata[file_path]\n\n # Check if file is marked as deleted\n if metadata.get('deleted', False):\n logger.warning(f\"File is marked as deleted: {file_path}\")\n return False\n\n chunks = metadata.get('chunks', [])\n if not chunks:\n logger.error(f\"No chunks found for file: {file_path}\")\n return False\n\n # Download all chunks\n downloaded_chunks = {}\n for chunk_info in chunks:\n chunk_index = chunk_info.get('chunk_index')\n cdn_url = chunk_info.get('cdn_url')\n webhook_url = chunk_info.get('webhook_url')\n chunk_hash = chunk_info.get('chunk_hash')\n\n if not cdn_url:\n logger.error(f\"No CDN URL for chunk {chunk_index}\")\n return False\n\n # Create chunk filename for logging\n chunk_filename = f\"{file_path}_chunk_{chunk_index}.bin\"\n \n chunk_data = self._download_chunk(cdn_url, webhook_url, chunk_filename)\n if not chunk_data:\n logger.error(f\"Failed to download chunk {chunk_index}\")\n return False\n\n # Verify chunk hash\n calculated_hash = HashManager.calculate_chunk_hash(chunk_data)\n expected_hash = chunk_info.get('chunk_hash')\n\n if calculated_hash != expected_hash:\n logger.error(\n f\"Chunk hash mismatch for {file_path} chunk {chunk_index}\"\n )\n return False\n\n downloaded_chunks[chunk_index] = chunk_data\n logger.debug(f\"Downloaded and verified chunk {chunk_index}\")\n\n\n # Reconstruct file\n reconstructed_data = b''\n for i in range(len(downloaded_chunks)):\n reconstructed_data += downloaded_chunks[i]\n\n # Decrypt if encrypted\n if metadata.get('encrypted', False):\n reconstructed_data = self.encryption_manager.decrypt_data(\n reconstructed_data\n )\n logger.debug(f\"Decrypted file: {file_path}\")\n\n # Decompress if compressed\n if metadata.get('compressed', False):\n reconstructed_data = self.compression_manager.decompress_data(\n reconstructed_data\n )\n logger.debug(f\"Decompressed file: {file_path}\")\n\n # Verify file hash\n file_hash = HashManager.calculate_data_hash(reconstructed_data)\n expected_file_hash = metadata.get('file_hash')\n\n if file_hash != expected_file_hash:\n logger.error(f\"File hash mismatch for {file_path}\")\n logger.error(f\"Expected: {expected_file_hash}, Got: {file_hash}\")\n return False\n\n # Write to file\n output_path = D_SYNCED2_DIR / file_path\n output_path.parent.mkdir(parents=True, exist_ok=True)\n\n with open(output_path, 'wb') as f:\n f.write(reconstructed_data)\n\n logger.info(f\"Successfully downloaded and reconstructed: {file_path}\")\n self._log_response(file_path, \"SUCCESS\", \"File downloaded and reconstructed\")\n return True\n\n except Exception as e:\n logger.error(f\"Error downloading file {file_path}: {e}\", exc_info=True)\n self._log_response(file_path, \"ERROR\", str(e))\n return False\n\n def download_all_files(self) -> int:\n \"\"\"Download all files from metadata\"\"\"\n logger.info(\"Starting download of all files\")\n\n if not self.files_metadata:\n logger.warning(\"No files in metadata\")\n return 0\n\n success_count = 0\n for file_path in self.files_metadata.keys():\n # Skip deleted files\n if self.files_metadata[file_path].get('deleted', False):\n logger.info(f\"Skipping deleted file: {file_path}\")\n continue\n\n if self.download_file(file_path):\n success_count += 1\n\n logger.info(f\"Downloaded {success_count}/{len(self.files_metadata)} files\")\n return success_count\n\n def download_specific_file(self, file_path: str) -> bool:\n \"\"\"Download a specific file\"\"\"\n return self.download_file(file_path)\n\n def list_available_files(self) -> List[str]:\n \"\"\"List all available files in metadata\"\"\"\n available = []\n for file_path, metadata in self.files_metadata.items():\n if not metadata.get('deleted', False):\n available.append(file_path)\n return available\n\n\ndef main():\n \"\"\"Main entry point\"\"\"\n logger.info(\"Starting d-sync download service\")\n\n download = D_SyncDownload()\n\n # List available files\n available_files = download.list_available_files()\n if not available_files:\n logger.warning(\"No files available for download\")\n return\n\n logger.info(f\"Available files ({len(available_files)}):\")\n for i, file_path in enumerate(available_files, 1):\n logger.info(f\" {i}. {file_path}\")\n\n # Download all files\n logger.info(\"\\nDownloading all files...\")\n success_count = download.download_all_files()\n\n logger.info(f\"\\nDownload complete: {success_count} files successfully downloaded\")\n logger.info(f\"Files are restored to: {D_SYNCED2_DIR}\")\n\n\nif __name__ == '__main__':\n main()\n\n\nif __name__ == '__main__':\n main()\n"}}, "d_sync_upload.py": {"549e81de27eaf29be03c9d42b73350912c01cc9c": {"t": 1785604232.2068505, "n": 15908, "h": "ddc438e73f9d6902", "c": "\"\"\"\nMain upload script for d-sync\nMonitors d-synced folder and uploads files to Discord\n\"\"\"\n\nimport json\nimport time\nfrom pathlib import Path\nfrom datetime import datetime\nfrom typing import Dict, List, Optional\nimport sys\nimport os\n\n# Add utils to path\nsys.path.insert(0, str(Path(__file__).parent))\n\nfrom utils import (\n Logger, EncryptionManager, CompressionManager, HashManager,\n WebhookManager, D_SYNCED_DIR, FILES_JSON, FOLDERS_JSON,\n MAX_PARTITION_SIZE, UPLOAD_LOG_FILE, FILES_JSON_UPLOAD_META\n)\n\nlogger = Logger(__name__)\n\n\nclass FileMetadata:\n \"\"\"Represents metadata for a file\"\"\"\n\n def __init__(self, file_path: Path, compressed: bool = False, encrypted: bool = False):\n self.file_path = file_path\n self.relative_path = file_path.relative_to(D_SYNCED_DIR)\n self.file_hash = HashManager.calculate_file_hash(file_path)\n self.file_size = file_path.stat().st_size\n self.date_created = datetime.fromtimestamp(\n file_path.stat().st_ctime\n ).isoformat()\n self.file_type = file_path.suffix\n self.compressed = compressed\n self.encrypted = encrypted\n self.chunks = [] # List of {chunk_index, hash, cdn_url}\n self.deleted = False\n\n def to_dict(self):\n return {\n 'file_path': str(self.relative_path),\n 'file_hash': self.file_hash,\n 'file_size': self.file_size,\n 'date_created': self.date_created,\n 'file_type': self.file_type,\n 'compressed': self.compressed,\n 'encrypted': self.encrypted,\n 'chunks': self.chunks,\n 'deleted': self.deleted\n }\n\n\nclass FolderMetadata:\n \"\"\"Represents metadata for a folder\"\"\"\n\n def __init__(self, folder_path: Path):\n self.folder_path = folder_path\n self.relative_path = folder_path.relative_to(D_SYNCED_DIR)\n self.date_created = datetime.fromtimestamp(\n folder_path.stat().st_ctime\n ).isoformat()\n self.file_count = sum(1 for _ in folder_path.rglob('*') if _.is_file())\n self.folder_count = sum(1 for _ in folder_path.rglob('*') if _.is_dir())\n self.cdn_url = None\n\n def to_dict(self):\n return {\n 'folder_path': str(self.relative_path),\n 'date_created': self.date_created,\n 'file_count': self.file_count,\n 'folder_count': self.folder_count,\n 'cdn_url': self.cdn_url\n }\n\n\nclass D_SyncUpload:\n \"\"\"Main upload manager for d-sync\"\"\"\n\n def __init__(self):\n self.webhook_manager = WebhookManager()\n self.encryption_manager = EncryptionManager()\n self.compression_manager = CompressionManager()\n self.files_metadata: Dict[str, Dict] = {}\n self.folders_metadata: Dict[str, Dict] = {}\n self.tracked_files = set()\n self._load_existing_metadata()\n\n def _load_existing_metadata(self):\n \"\"\"Load existing metadata from JSON files\"\"\"\n if FILES_JSON.exists():\n try:\n with open(FILES_JSON, 'r') as f:\n data = json.load(f)\n self.files_metadata = data.get('files', {})\n logger.info(\"Loaded existing files metadata\")\n except json.JSONDecodeError:\n logger.warning(\"Could not parse files.json, starting fresh\")\n\n if FOLDERS_JSON.exists():\n try:\n with open(FOLDERS_JSON, 'r') as f:\n data = json.load(f)\n self.folders_metadata = data.get('folders', {})\n logger.info(\"Loaded existing folders metadata\")\n except json.JSONDecodeError:\n logger.warning(\"Could not parse folders.json, starting fresh\")\n\n def _save_files_metadata(self):\n \"\"\"Save files metadata to JSON\"\"\"\n metadata = {\n 'last_updated': datetime.now().isoformat(),\n 'files': self.files_metadata\n }\n FILES_JSON.parent.mkdir(parents=True, exist_ok=True)\n with open(FILES_JSON, 'w') as f:\n json.dump(metadata, f, indent=2)\n logger.info(f\"Saved files metadata to {FILES_JSON}\")\n # Attempt to upload or update files.json on remote storage\n try:\n self._ensure_files_json_remote()\n except Exception as e:\n logger.debug(f\"Could not ensure remote files.json: {e}\")\n\n def _ensure_files_json_remote(self):\n \"\"\"Upload files.json once and PATCH the remote message on updates.\n Stores remote info in `FILES_JSON_UPLOAD_META`.\n \"\"\"\n # Choose a webhook to use\n webhook_url = self.webhook_manager.get_random_webhook()\n if not webhook_url:\n logger.warning(\"No webhook available to upload files.json\")\n return\n\n # Read local files.json bytes\n try:\n with open(FILES_JSON, 'rb') as f:\n data_bytes = f.read()\n except Exception as e:\n logger.error(f\"Failed to read {FILES_JSON} for remote upload: {e}\")\n return\n\n prev_meta = None\n if FILES_JSON_UPLOAD_META.exists():\n try:\n with open(FILES_JSON_UPLOAD_META, 'r') as mf:\n prev_meta = json.load(mf)\n except Exception:\n prev_meta = None\n\n # If we have previous meta, try to PATCH the message first\n if prev_meta:\n prev_webhook = prev_meta.get('webhook_url')\n prev_msg_id = prev_meta.get('message_id')\n if prev_webhook and prev_msg_id:\n logger.info(f\"Attempting to PATCH existing files.json message {prev_msg_id}\")\n resp = self.webhook_manager.patch_message(prev_webhook, prev_msg_id, data=data_bytes, filename='files.json')\n if resp:\n # Update meta\n new_meta = {\n 'webhook_url': prev_webhook,\n 'message_id': resp.get('id'),\n 'cdn_url': self.webhook_manager.extract_cdn_url(resp),\n 'last_updated': datetime.now().isoformat()\n }\n with open(FILES_JSON_UPLOAD_META, 'w') as mf:\n json.dump(new_meta, mf, indent=2)\n logger.info(\"Patched remote files.json message successfully\")\n return\n else:\n logger.info(\"Patching files.json failed; will upload a new message\")\n\n # Upload a new message with files.json\n logger.info(\"Uploading files.json to remote storage\")\n resp = self.webhook_manager.upload_bytes(webhook_url, data_bytes, 'files.json')\n if resp:\n msg_id = resp.get('id')\n cdn = self.webhook_manager.extract_cdn_url(resp)\n new_meta = {\n 'webhook_url': webhook_url,\n 'message_id': msg_id,\n 'cdn_url': cdn,\n 'last_updated': datetime.now().isoformat()\n }\n with open(FILES_JSON_UPLOAD_META, 'w') as mf:\n json.dump(new_meta, mf, indent=2)\n logger.info(f\"Uploaded files.json as message {msg_id}\")\n # If there was a previous message, try to delete it for cleanup\n if prev_meta and prev_meta.get('webhook_url') and prev_meta.get('message_id'):\n try:\n self.webhook_manager.delete_message(prev_meta.get('webhook_url'), prev_meta.get('message_id'))\n logger.info(f\"Deleted previous files.json message {prev_meta.get('message_id')}\")\n except Exception:\n logger.debug(\"Failed to delete previous files.json message\")\n else:\n logger.error(\"Failed to upload files.json to remote storage\")\n\n def _save_folders_metadata(self):\n \"\"\"Save folders metadata to JSON\"\"\"\n metadata = {\n 'last_updated': datetime.now().isoformat(),\n 'folders': self.folders_metadata\n }\n FOLDERS_JSON.parent.mkdir(parents=True, exist_ok=True)\n with open(FOLDERS_JSON, 'w') as f:\n json.dump(metadata, f, indent=2)\n logger.info(f\"Saved folders metadata to {FOLDERS_JSON}\")\n\n def _partition_file(self, file_data: bytes) -> List[bytes]:\n \"\"\"Partition file data into chunks of MAX_PARTITION_SIZE\"\"\"\n chunks = []\n for i in range(0, len(file_data), int(MAX_PARTITION_SIZE)):\n chunk = file_data[i:i + int(MAX_PARTITION_SIZE)]\n chunks.append(chunk)\n logger.debug(f\"Partitioned data into {len(chunks)} chunks\")\n return chunks\n\n def _process_file(self, file_path: Path) -> bool:\n \"\"\"Process and upload a single file\"\"\"\n logger.info(f\"Processing file: {file_path}\")\n\n try:\n # Check if already uploaded\n relative_path = str(file_path.relative_to(D_SYNCED_DIR))\n if relative_path in self.files_metadata:\n logger.info(f\"File already tracked: {relative_path}\")\n return True\n\n # Read file data\n with open(file_path, 'rb') as f:\n file_data = f.read()\n\n # Compress if enabled\n compressed = False\n if len(file_data) > 1024 * 100: # Compress files > 100KB\n file_data = self.compression_manager.compress_data(file_data)\n compressed = True\n logger.debug(f\"Compressed file: {file_path}\")\n\n # Encrypt if enabled\n encrypted = False\n if True: # Encryption enabled by default\n file_data = self.encryption_manager.encrypt_data(file_data)\n encrypted = True\n logger.debug(f\"Encrypted file: {file_path}\")\n\n # Partition file\n chunks = self._partition_file(file_data)\n\n # Create metadata\n metadata = FileMetadata(file_path, compressed, encrypted)\n\n # Upload chunks\n for chunk_index, chunk_data in enumerate(chunks):\n chunk_hash = HashManager.calculate_chunk_hash(chunk_data)\n webhook_url = self.webhook_manager.get_random_webhook()\n\n if not webhook_url:\n logger.error(\"No webhooks available\")\n return False\n\n # Create temporary file for upload\n chunk_filename = f\"{metadata.file_hash}_chunk_{chunk_index}.bin\"\n response = self.webhook_manager.upload_bytes(\n webhook_url, chunk_data, chunk_filename\n )\n\n if response:\n cdn_url = self.webhook_manager.extract_cdn_url(response)\n if cdn_url:\n metadata.chunks.append({\n 'chunk_index': chunk_index,\n 'chunk_hash': chunk_hash,\n 'webhook_url': webhook_url,\n 'cdn_url': cdn_url\n })\n logger.info(f\"Uploaded chunk {chunk_index}: {chunk_filename}\")\n\n # Save response to log\n self._log_response(chunk_filename, response)\n else:\n logger.warning(f\"No CDN URL in response for chunk {chunk_index}\")\n else:\n logger.error(f\"Failed to upload chunk {chunk_index}\")\n return False\n\n # Save metadata\n self.files_metadata[relative_path] = metadata.to_dict()\n self._save_files_metadata()\n\n logger.info(f\"Successfully uploaded file: {relative_path}\")\n self.tracked_files.add(relative_path)\n return True\n\n except Exception as e:\n logger.error(f\"Error processing file {file_path}: {e}\", exc_info=True)\n return False\n\n def _process_folder(self, folder_path: Path):\n \"\"\"Process a folder and create metadata\"\"\"\n logger.info(f\"Processing folder: {folder_path}\")\n\n try:\n relative_path = str(folder_path.relative_to(D_SYNCED_DIR))\n\n # Check if already processed\n if relative_path in self.folders_metadata:\n logger.info(f\"Folder already tracked: {relative_path}\")\n return\n\n # Create metadata\n metadata = FolderMetadata(folder_path)\n\n # Create folder metadata JSON\n folder_json_path = folder_path / \"folder_metadata.json\"\n folder_json_data = metadata.to_dict()\n\n # Upload folder metadata\n webhook_url = self.webhook_manager.get_random_webhook()\n if webhook_url:\n response = self.webhook_manager.upload_bytes(\n webhook_url,\n json.dumps(folder_json_data).encode(),\n f\"folder_metadata_{relative_path.replace(chr(92), '_')}.json\"\n )\n\n if response:\n cdn_url = self.webhook_manager.extract_cdn_url(response)\n metadata.cdn_url = cdn_url\n logger.info(f\"Uploaded folder metadata: {relative_path}\")\n\n self.folders_metadata[relative_path] = metadata.to_dict()\n self._save_folders_metadata()\n logger.info(f\"Successfully processed folder: {relative_path}\")\n\n except Exception as e:\n logger.error(f\"Error processing folder {folder_path}: {e}\", exc_info=True)\n\n def _log_response(self, filename: str, response: Dict):\n \"\"\"Log webhook response\"\"\"\n try:\n UPLOAD_LOG_FILE.parent.mkdir(parents=True, exist_ok=True)\n with open(UPLOAD_LOG_FILE, 'a') as f:\n timestamp = datetime.now().isoformat()\n f.write(f\"\\n[{timestamp}] {filename}\\n\")\n f.write(json.dumps(response, indent=2))\n f.write(\"\\n\" + \"=\"*80 + \"\\n\")\n except Exception as e:\n logger.error(f\"Error logging response: {e}\")\n\n def scan_directory(self):\n \"\"\"Scan d-synced directory for new files and folders\"\"\"\n logger.info(\"Scanning directory for changes...\")\n\n if not D_SYNCED_DIR.exists():\n logger.warning(f\"D-synced directory not found: {D_SYNCED_DIR}\")\n return\n\n # Process all items in directory\n for item in D_SYNCED_DIR.rglob('*'):\n if item.is_dir() and item.name != '__pycache__':\n # Skip if it's the main d-synced folder\n if item != D_SYNCED_DIR:\n self._process_folder(item)\n elif item.is_file():\n # Skip hidden files and JSON metadata files\n if not item.name.startswith('.') and not item.name.endswith('.json'):\n self._process_file(item)\n\n def watch(self, interval: int = 60):\n \"\"\"Watch directory for changes\"\"\"\n logger.info(f\"Starting watch with {interval}s interval\")\n \n try:\n while True:\n self.scan_directory()\n logger.debug(f\"Scan complete, waiting {interval}s...\")\n time.sleep(interval)\n except KeyboardInterrupt:\n logger.info(\"Watch stopped by user\")\n except Exception as e:\n logger.error(f\"Error during watch: {e}\", exc_info=True)\n\n\ndef main():\n \"\"\"Main entry point\"\"\"\n logger.info(\"Starting d-sync upload service\")\n \n # Create webhooks.txt if it doesn't exist\n if not Path('webhooks.txt').exists():\n with open('webhooks.txt', 'w') as f:\n f.write(\"# Add Discord webhook URLs here, one per line\\n\")\n f.write(\"# Example: https://discordapp.com/api/webhooks/...\\n\")\n logger.info(\"Created webhooks.txt - add your Discord webhook URLs\")\n \n upload = D_SyncUpload()\n upload.watch()\n\n\nif __name__ == '__main__':\n main()\n"}}, "d_sync_web.py": {"ce01f928ebb1413c52126826407c7d958e61de41": {"t": 1785604234.3919344, "n": 28721, "h": "50768b7842b43c5c", "c": "\"\"\"\nD-Sync Web Server\nServes a localhost interface for uploading and downloading files\n\"\"\"\n\nimport os\nimport json\nimport threading\nfrom pathlib import Path\nfrom datetime import datetime, timedelta\nimport webbrowser\nimport time\nimport sys\n\n# Add utils to path\nsys.path.insert(0, str(Path(__file__).parent))\n\nfrom utils import (\n Logger, D_SYNCED_DIR, FILES_JSON, WEBHOOKS_FILE, BASE_DIR,\n CompressionManager, EncryptionManager, HashManager\n)\nfrom utils.webhook_refresh import WebhookMessageRefresh\n\ntry:\n from flask import Flask, request, jsonify, send_file, render_template_string\nexcept ImportError:\n print(\"Flask not installed. Install with: pip install flask\")\n sys.exit(1)\n\nlogger = Logger(\"d_sync_web\")\n\napp = Flask(__name__, static_folder=None)\napp.config['MAX_CONTENT_LENGTH'] = 1024 * 1024 * 1024 # 1GB max\napp.config['UPLOAD_FOLDER'] = str(D_SYNCED_DIR)\n\n# Track current upload status\nupload_status = {\n 'is_uploading': False,\n 'current_file': None,\n 'progress': 0\n}\n\n\nHTML_TEMPLATE = '''\n\n\n\n \n \n d-sync - Discord File Storage\n \n\n\n
\n
\n

☁️ d-sync

\n

Secure Cloud File Storage

\n
\n \n
\n \n
\n

📤 Upload Files

\n
\n
📁
\n

Drag files here or click to select

\n

Files will be queued and uploaded to remote storage automatically

\n \n \n
\n \n
\n
\n
0%
\n
\n
\n Uploading: \n
\n
\n \n
\n ✅ Ready to upload\n
\n
\n \n \n
\n

⚙️ Status

\n
\n
\n

Current Upload Status

\n
\n Not uploading\n
\n
\n \n
\n

System Statistics

\n
\n

📊 Total Files: 0

\n

💾 Total Size: 0 MB

\n

📝 Deleted Files: 0

\n
\n
\n
\n
\n
\n \n \n
\n

📂 Files (Most Recent First)

\n
\n
Loading files...
\n
\n
\n \n \n
\n
\n
0
\n
Total Files
\n
\n
\n
0
\n
Storage Used
\n
\n
\n
0
\n
Encrypted
\n
\n
\n
0
\n
Compressed
\n
\n
\n
\n \n \n\n\n'''\n\n\n@app.route('/')\ndef index():\n \"\"\"Serve the main HTML page\"\"\"\n return render_template_string(HTML_TEMPLATE)\n\n\n@app.route('/api/files', methods=['GET'])\ndef get_files():\n \"\"\"Get all files from files.json but sanitize sensitive fields before returning to client.\"\"\"\n try:\n if not FILES_JSON.exists():\n return jsonify({'files': {}})\n\n with open(FILES_JSON, 'r') as f:\n data = json.load(f)\n\n sanitized = {}\n for key, meta in data.get('files', {}).items():\n sanitized[key] = {\n 'file_path': meta.get('file_path', key),\n 'file_size': meta.get('file_size', 0),\n 'date_created': meta.get('date_created'),\n 'file_type': meta.get('file_type'),\n 'compressed': meta.get('compressed', False),\n 'encrypted': meta.get('encrypted', False),\n 'deleted': meta.get('deleted', False),\n 'chunk_count': len(meta.get('chunks', []))\n }\n\n return jsonify({'last_updated': data.get('last_updated'), 'files': sanitized})\n except Exception as e:\n logger.error(f\"Error reading files: {e}\")\n return jsonify({'error': str(e)}), 500\n\n\n@app.route('/api/upload', methods=['POST'])\ndef upload_file():\n \"\"\"Handle file upload to d-synced folder\"\"\"\n try:\n if 'file' not in request.files:\n return jsonify({'success': False, 'error': 'No file provided'}), 400\n \n file = request.files['file']\n if file.filename == '':\n return jsonify({'success': False, 'error': 'No filename'}), 400\n \n # Reject .crdownload files\n if file.filename.endswith('.crdownload'):\n return jsonify({'success': False, 'error': 'Cannot upload incomplete downloads (.crdownload)'}), 400\n \n # Save file to d-synced folder\n upload_status['is_uploading'] = True\n upload_status['current_file'] = file.filename\n \n file_path = D_SYNCED_DIR / file.filename\n file_path.parent.mkdir(parents=True, exist_ok=True)\n file.save(str(file_path))\n \n upload_status['is_uploading'] = False\n upload_status['current_file'] = None\n \n logger.info(f\"File uploaded via web: {file.filename}\")\n return jsonify({'success': True, 'message': 'File uploaded successfully'})\n \n except Exception as e:\n logger.error(f\"Upload error: {e}\")\n upload_status['is_uploading'] = False\n return jsonify({'success': False, 'error': str(e)}), 500\n\n\n@app.route('/api/upload-status', methods=['GET'])\ndef get_upload_status():\n \"\"\"Get current upload status\"\"\"\n return jsonify(upload_status)\n\n\n@app.route('/api/delete/', methods=['POST'])\ndef delete_file(filename):\n \"\"\"Mark file as deleted in files.json\"\"\"\n try:\n if FILES_JSON.exists():\n with open(FILES_JSON, 'r') as f:\n data = json.load(f)\n \n if filename in data.get('files', {}):\n data['files'][filename]['deleted'] = True\n with open(FILES_JSON, 'w') as f:\n json.dump(data, f, indent=2)\n logger.info(f\"File marked as deleted: {filename}\")\n return jsonify({'success': True})\n \n return jsonify({'success': False, 'error': 'File not found'}), 404\n except Exception as e:\n logger.error(f\"Delete error: {e}\")\n return jsonify({'success': False, 'error': str(e)}), 500\n\n\n@app.route('/download/', methods=['GET'])\ndef download_file(filename):\n \"\"\"Download a file and reconstruct it from Discord\"\"\"\n try:\n # Reject .crdownload files\n if filename.endswith('.crdownload'):\n return jsonify({'error': 'Cannot download incomplete files'}), 400\n \n if not FILES_JSON.exists():\n return jsonify({'error': 'No files metadata'}), 404\n \n with open(FILES_JSON, 'r') as f:\n data = json.load(f)\n \n if filename not in data.get('files', {}):\n return jsonify({'error': 'File not found'}), 404\n \n file_meta = data['files'][filename]\n \n # Check if file is deleted\n if file_meta.get('deleted'):\n return jsonify({'error': 'File has been deleted'}), 410\n \n # Download from Discord\n from d_sync_download import D_SyncDownload\n downloader = D_SyncDownload()\n \n # Download and reconstruct\n success = downloader.download_file(filename)\n if not success:\n return jsonify({'error': 'Failed to download file'}), 500\n \n # Send the reconstructed file. check known output locations (d-synced and d-synced2)\n candidates = [D_SYNCED_DIR / filename, BASE_DIR / 'd-synced2' / filename]\n for p in candidates:\n if p.exists():\n return send_file(str(p), as_attachment=True, download_name=filename)\n\n return jsonify({'error': 'File not found after download'}), 500\n \n except Exception as e:\n logger.error(f\"Download error: {e}\")\n return jsonify({'error': str(e)}), 500\n\n\ndef start_server(port=5000, open_browser=True):\n \"\"\"Start the Flask server\"\"\"\n logger.info(f\"Starting d-sync web server on http://localhost:{port}\")\n \n if open_browser:\n # Open in browser after a short delay\n def open_browser_delayed():\n time.sleep(1)\n webbrowser.open(f'http://localhost:{port}')\n \n threading.Thread(target=open_browser_delayed, daemon=True).start()\n \n app.run(host='localhost', port=port, debug=False, use_reloader=False)\n\n\ndef main():\n \"\"\"Main entry point\"\"\"\n logger.info(\"Starting d-sync web interface\")\n start_server(port=5000, open_browser=True)\n\n\nif __name__ == '__main__':\n main()\n"}}, "open_dsync.py": {"ecb86d06f450f0a8b54df97b5e14ecda7d874a79": {"t": 1785604235.8492181, "n": 570, "h": "b6bead76f5d7cac9", "c": "#!/usr/bin/env python3\n\"\"\"\nD-Sync Web Interface Launcher\nOpens the D-Sync dashboard in Chrome\n\"\"\"\n\nimport subprocess\nimport sys\nimport time\nimport webbrowser\nfrom pathlib import Path\n\n# Add parent directory to path\nsys.path.insert(0, str(Path(__file__).parent))\n\nfrom d_sync_web import start_server\n\nif __name__ == '__main__':\n print(\"🚀 Starting d-sync web interface...\")\n print(\"📱 Opening dashboard in Chrome...\")\n print(\"🌐 Access at: http://localhost:5000\")\n print(\"\\nPress Ctrl+C to stop the server\\n\")\n \n start_server(port=5000, open_browser=True)\n"}}}}