Spaces:
Build error
Build error
File size: 8,005 Bytes
5f7e640 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 | 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] |