Spaces:
Build error
Build error
Download mod_manager.py from smodusermc/MC: direct link, hf CLI and curl.
- Browser
- Download file 8.01 kB
-
https://huggingface.co/spaces/smodusermc/MC/resolve/main/mod_manager.py
- Command line
-
hf download hf://spaces/smodusermc/MC/mod_manager.py
-
curl -L -o mod_manager.py https://huggingface.co/spaces/smodusermc/MC/resolve/main/mod_manager.py
8.01 kB
| import os | |
| import shutil | |
| import hashlib | |
| import json | |
| import zipfile | |
| from pathlib import Path | |
| from datetime import datetime | |
| from typing import List, Dict, Optional | |
| from werkzeug.utils import secure_filename | |
| from config import config | |
| import humanize | |
| class ModManager: | |
| def __init__(self): | |
| self.mods_dir = config.MODS_DIR | |
| self.mods_data_file = config.SERVER_DIR / "mods_data.json" | |
| self.mods_data = self._load_data() | |
| self.allowed_extensions = {'.jar'} | |
| # Ensure mods directory exists | |
| self.mods_dir.mkdir(parents=True, exist_ok=True) | |
| def _load_data(self) -> Dict: | |
| """Load mods metadata""" | |
| if self.mods_data_file.exists(): | |
| try: | |
| with open(self.mods_data_file, 'r') as f: | |
| return json.load(f) | |
| except: | |
| pass | |
| return {'mods': {}} | |
| def _save_data(self): | |
| """Save mods metadata""" | |
| with open(self.mods_data_file, 'w') as f: | |
| json.dump(self.mods_data, f, indent=2) | |
| def _get_file_hash(self, filepath: Path) -> str: | |
| """Get MD5 hash of file""" | |
| hash_md5 = hashlib.md5() | |
| with open(filepath, 'rb') as f: | |
| for chunk in iter(lambda: f.read(4096), b''): | |
| hash_md5.update(chunk) | |
| return hash_md5.hexdigest() | |
| def get_installed_mods(self) -> List[Dict]: | |
| """Get list of installed mods""" | |
| mods = [] | |
| for file in self.mods_dir.iterdir(): | |
| if file.suffix.lower() == '.jar': | |
| mod_info = self._get_mod_info(file) | |
| mods.append(mod_info) | |
| return sorted(mods, key=lambda x: x['name'].lower()) | |
| def _get_mod_info(self, filepath: Path) -> Dict: | |
| """Extract mod information""" | |
| stat = filepath.stat() | |
| file_hash = self._get_file_hash(filepath) | |
| # Check if we have cached metadata | |
| cached = self.mods_data['mods'].get(file_hash, {}) | |
| # Try to read mod info from jar | |
| mod_name = filepath.stem | |
| mod_version = "Unknown" | |
| mod_description = "" | |
| mod_authors = [] | |
| try: | |
| with zipfile.ZipFile(filepath, 'r') as jar: | |
| # Try fabric.mod.json | |
| if 'fabric.mod.json' in jar.namelist(): | |
| with jar.open('fabric.mod.json') as f: | |
| mod_meta = json.load(f) | |
| mod_name = mod_meta.get('name', mod_name) | |
| mod_version = mod_meta.get('version', mod_version) | |
| mod_description = mod_meta.get('description', '') | |
| mod_authors = mod_meta.get('authors', []) | |
| if isinstance(mod_authors, list): | |
| mod_authors = [a if isinstance(a, str) else a.get('name', '') for a in mod_authors] | |
| except: | |
| pass | |
| # Check if mod is disabled | |
| is_disabled = filepath.name.endswith('.disabled') | |
| return { | |
| 'filename': filepath.name, | |
| 'name': mod_name, | |
| 'version': mod_version, | |
| 'description': mod_description, | |
| 'authors': mod_authors, | |
| 'size': humanize.naturalsize(stat.st_size), | |
| 'size_bytes': stat.st_size, | |
| 'modified': datetime.fromtimestamp(stat.st_mtime).isoformat(), | |
| 'hash': file_hash, | |
| 'enabled': not is_disabled, | |
| 'notes': cached.get('notes', '') | |
| } | |
| def upload_mod(self, file) -> Dict: | |
| """Upload a new mod""" | |
| if not file or not file.filename: | |
| return {'success': False, 'message': 'No file provided'} | |
| filename = secure_filename(file.filename) | |
| if not filename.endswith('.jar'): | |
| return {'success': False, 'message': 'Only .jar files are allowed'} | |
| filepath = self.mods_dir / filename | |
| # Check if file already exists | |
| if filepath.exists(): | |
| return {'success': False, 'message': f'Mod {filename} already exists'} | |
| try: | |
| file.save(str(filepath)) | |
| mod_info = self._get_mod_info(filepath) | |
| # Store metadata | |
| self.mods_data['mods'][mod_info['hash']] = { | |
| 'uploaded': datetime.now().isoformat(), | |
| 'original_name': filename, | |
| 'notes': '' | |
| } | |
| self._save_data() | |
| return { | |
| 'success': True, | |
| 'message': f'Mod {mod_info["name"]} uploaded successfully', | |
| 'mod': mod_info | |
| } | |
| except Exception as e: | |
| return {'success': False, 'message': str(e)} | |
| def delete_mod(self, filename: str) -> Dict: | |
| """Delete a mod""" | |
| filepath = self.mods_dir / secure_filename(filename) | |
| if not filepath.exists(): | |
| return {'success': False, 'message': 'Mod not found'} | |
| try: | |
| # Get hash before deleting | |
| file_hash = self._get_file_hash(filepath) | |
| filepath.unlink() | |
| # Remove from metadata | |
| if file_hash in self.mods_data['mods']: | |
| del self.mods_data['mods'][file_hash] | |
| self._save_data() | |
| return {'success': True, 'message': f'Mod {filename} deleted'} | |
| except Exception as e: | |
| return {'success': False, 'message': str(e)} | |
| def toggle_mod(self, filename: str) -> Dict: | |
| """Enable/disable a mod""" | |
| filename = secure_filename(filename) | |
| if filename.endswith('.disabled'): | |
| # Enable | |
| disabled_path = self.mods_dir / filename | |
| enabled_path = self.mods_dir / filename[:-9] # Remove .disabled | |
| if not disabled_path.exists(): | |
| return {'success': False, 'message': 'Mod not found'} | |
| disabled_path.rename(enabled_path) | |
| return {'success': True, 'message': f'Mod enabled: {enabled_path.name}', 'enabled': True} | |
| else: | |
| # Disable | |
| enabled_path = self.mods_dir / filename | |
| disabled_path = self.mods_dir / (filename + '.disabled') | |
| if not enabled_path.exists(): | |
| return {'success': False, 'message': 'Mod not found'} | |
| enabled_path.rename(disabled_path) | |
| return {'success': True, 'message': f'Mod disabled: {filename}', 'enabled': False} | |
| def get_mod_details(self, filename: str) -> Optional[Dict]: | |
| """Get detailed mod information""" | |
| filepath = self.mods_dir / secure_filename(filename) | |
| if filepath.exists(): | |
| return self._get_mod_info(filepath) | |
| return None | |
| def update_notes(self, filename: str, notes: str) -> Dict: | |
| """Update mod notes""" | |
| filepath = self.mods_dir / secure_filename(filename) | |
| if not filepath.exists(): | |
| return {'success': False, 'message': 'Mod not found'} | |
| file_hash = self._get_file_hash(filepath) | |
| if file_hash not in self.mods_data['mods']: | |
| self.mods_data['mods'][file_hash] = {} | |
| self.mods_data['mods'][file_hash]['notes'] = notes | |
| self._save_data() | |
| return {'success': True, 'message': 'Notes updated'} | |
| def get_total_size(self) -> str: | |
| """Get total size of all mods""" | |
| total = sum(f.stat().st_size for f in self.mods_dir.iterdir() if f.is_file()) | |
| return humanize.naturalsize(total) | |
| def export_mod_list(self) -> List[Dict]: | |
| """Export mod list for backup""" | |
| mods = self.get_installed_mods() | |
| return [{ | |
| 'name': m['name'], | |
| 'version': m['version'], | |
| 'filename': m['filename'], | |
| 'hash': m['hash'] | |
| } for m in mods] |