|
34 | 34 | import FreeSimpleGUI as sg |
35 | 35 | from pathlib import Path |
36 | 36 | from typing import Dict, List, Optional, Tuple |
| 37 | +import json |
37 | 38 |
|
38 | 39 |
|
39 | 40 | class ArtCropData: |
@@ -229,16 +230,168 @@ def filter_crops_by_art_id( |
229 | 230 | return [entry for entry in crop_data if art_id.zfill(6) in entry.path] |
230 | 231 |
|
231 | 232 |
|
| 233 | +def extract_art_id_from_path(path: str) -> Optional[str]: |
| 234 | + """ |
| 235 | + Extract ArtId from a crop path. |
| 236 | + Path format: Assets/Core/CardArt/001000/001155_AIF |
| 237 | +
|
| 238 | + Args: |
| 239 | + path: The crop entry path |
| 240 | +
|
| 241 | + Returns: |
| 242 | + ArtId as string, or None if not found |
| 243 | + """ |
| 244 | + try: |
| 245 | + # Split the path and look for the numeric part |
| 246 | + parts = path.split("/") |
| 247 | + art_id = parts[-1].split("_")[0].lstrip("0") |
| 248 | + return art_id |
| 249 | + except Exception as e: |
| 250 | + print(f"Error extracting ArtId from path {path}: {e}") |
| 251 | + return None |
| 252 | + |
| 253 | + |
| 254 | +def save_crop_change_to_json(entry: ArtCropData, changes_file_path: str) -> bool: |
| 255 | + """ |
| 256 | + Save crop change to the changes.json file based on ArtId. |
| 257 | +
|
| 258 | + Args: |
| 259 | + entry: The crop entry that was modified |
| 260 | + changes_file_path: Path to the changes.json file |
| 261 | +
|
| 262 | + Returns: |
| 263 | + True if successful, False otherwise |
| 264 | + """ |
| 265 | + try: |
| 266 | + # Extract ArtId from path |
| 267 | + art_id = extract_art_id_from_path(entry.path) |
| 268 | + if not art_id: |
| 269 | + print(f"Could not extract ArtId from path: {entry.path}") |
| 270 | + return False |
| 271 | + |
| 272 | + # Load existing changes |
| 273 | + if os.path.exists(changes_file_path): |
| 274 | + with open(changes_file_path, "r") as f: |
| 275 | + changes_data = json.load(f) |
| 276 | + else: |
| 277 | + changes_data = {} |
| 278 | + |
| 279 | + # Initialize crops structure if not exists |
| 280 | + if "crops" not in changes_data: |
| 281 | + changes_data["crops"] = {} |
| 282 | + |
| 283 | + # Store crop change under ArtId |
| 284 | + if art_id not in changes_data["crops"]: |
| 285 | + changes_data["crops"][art_id] = [] |
| 286 | + |
| 287 | + # Create crop entry dict |
| 288 | + crop_dict = { |
| 289 | + "path": entry.path, |
| 290 | + "format": entry.format_type, |
| 291 | + "x": entry.x, |
| 292 | + "y": entry.y, |
| 293 | + "z": entry.z, |
| 294 | + "w": entry.w, |
| 295 | + "generated": entry.generated, |
| 296 | + } |
| 297 | + |
| 298 | + # Check if this crop already exists (by path and format) |
| 299 | + existing_crops = changes_data["crops"][art_id] |
| 300 | + existing_index = None |
| 301 | + for i, crop in enumerate(existing_crops): |
| 302 | + if crop["path"] == entry.path and crop["format"] == entry.format_type: |
| 303 | + existing_index = i |
| 304 | + break |
| 305 | + |
| 306 | + if existing_index is not None: |
| 307 | + # Update existing |
| 308 | + existing_crops[existing_index] = crop_dict |
| 309 | + else: |
| 310 | + # Add new |
| 311 | + existing_crops.append(crop_dict) |
| 312 | + |
| 313 | + # Save back to file |
| 314 | + with open(changes_file_path, "w") as f: |
| 315 | + json.dump(changes_data, f, indent=4) |
| 316 | + |
| 317 | + return True |
| 318 | + |
| 319 | + except Exception as e: |
| 320 | + print(f"Error saving crop change to JSON: {e}") |
| 321 | + return False |
| 322 | + |
| 323 | + |
| 324 | +def remove_crop_change_from_json(entry: ArtCropData, changes_file_path: str) -> bool: |
| 325 | + """ |
| 326 | + Remove crop change from the changes.json file. |
| 327 | +
|
| 328 | + Args: |
| 329 | + entry: The crop entry to remove |
| 330 | + changes_file_path: Path to the changes.json file |
| 331 | +
|
| 332 | + Returns: |
| 333 | + True if successful, False otherwise |
| 334 | + """ |
| 335 | + try: |
| 336 | + # Extract ArtId from path |
| 337 | + art_id = extract_art_id_from_path(entry.path) |
| 338 | + if not art_id: |
| 339 | + return False |
| 340 | + |
| 341 | + # Load existing changes |
| 342 | + if not os.path.exists(changes_file_path): |
| 343 | + return True # Nothing to remove |
| 344 | + |
| 345 | + with open(changes_file_path, "r") as f: |
| 346 | + changes_data = json.load(f) |
| 347 | + |
| 348 | + # Check if crops section exists |
| 349 | + if "crops" not in changes_data or art_id not in changes_data["crops"]: |
| 350 | + return True # Nothing to remove |
| 351 | + |
| 352 | + # Remove the matching crop entry |
| 353 | + changes_data["crops"][art_id] = [ |
| 354 | + crop |
| 355 | + for crop in changes_data["crops"][art_id] |
| 356 | + if not (crop["path"] == entry.path and crop["format"] == entry.format_type) |
| 357 | + ] |
| 358 | + |
| 359 | + # Clean up empty entries |
| 360 | + if not changes_data["crops"][art_id]: |
| 361 | + del changes_data["crops"][art_id] |
| 362 | + |
| 363 | + if not changes_data["crops"]: |
| 364 | + del changes_data["crops"] |
| 365 | + |
| 366 | + # Save back to file |
| 367 | + with open(changes_file_path, "w") as f: |
| 368 | + json.dump(changes_data, f, indent=4) |
| 369 | + |
| 370 | + return True |
| 371 | + |
| 372 | + except Exception as e: |
| 373 | + print(f"Error removing crop change from JSON: {e}") |
| 374 | + return False |
| 375 | + |
| 376 | + |
232 | 377 | def create_crop_editor_window( |
233 | | - database_file_path: str, database_cursor: sqlite3.Cursor |
| 378 | + database_file_path: str, |
| 379 | + database_cursor: sqlite3.Cursor, |
| 380 | + changes_file_path: str = None, |
234 | 381 | ) -> None: |
235 | 382 | """ |
236 | 383 | Create and display the crop editor window. |
237 | 384 |
|
238 | 385 | Args: |
239 | 386 | database_file_path: Path to the Raw_CardDatabase file |
240 | 387 | database_cursor: SQLite cursor for querying card data |
| 388 | + changes_file_path: Path to the changes.json file (optional) |
241 | 389 | """ |
| 390 | + # Set default changes file path if not provided |
| 391 | + if changes_file_path is None: |
| 392 | + user_config_directory = Path.home() / ".mtga_swapper" |
| 393 | + changes_file_path = str(user_config_directory / "changes.json") |
| 394 | + |
242 | 395 | # Determine the crop database path |
243 | 396 | db_dir = os.path.dirname(database_file_path) |
244 | 397 | crop_db_path = None |
@@ -453,6 +606,9 @@ def load_entry_to_edit(entry: ArtCropData): |
453 | 606 |
|
454 | 607 | # Update in the database (without committing yet) |
455 | 608 | if update_crop_entry(crop_cursor, entry, commit=False): |
| 609 | + # Save to changes.json |
| 610 | + save_crop_change_to_json(entry, changes_file_path) |
| 611 | + |
456 | 612 | # Update the display |
457 | 613 | update_crop_table(filtered_crops) |
458 | 614 |
|
@@ -588,6 +744,9 @@ def load_entry_to_edit(entry: ArtCropData): |
588 | 744 | new_entry.to_tuple(), |
589 | 745 | ) |
590 | 746 |
|
| 747 | + # Save to changes.json |
| 748 | + save_crop_change_to_json(new_entry, changes_file_path) |
| 749 | + |
591 | 750 | # Add to our data structures |
592 | 751 | crop_data.append(new_entry) |
593 | 752 | filtered_crops.append(new_entry) |
@@ -650,6 +809,9 @@ def load_entry_to_edit(entry: ArtCropData): |
650 | 809 | (entry.path, entry.format_type), |
651 | 810 | ) |
652 | 811 |
|
| 812 | + # Remove from changes.json |
| 813 | + remove_crop_change_from_json(entry, changes_file_path) |
| 814 | + |
653 | 815 | # Remove from our data structures |
654 | 816 | crop_data.remove(entry) |
655 | 817 | filtered_crops.remove(entry) |
|
0 commit comments