Skip to content

Commit 6304980

Browse files
committed
Enh(viewer): Parallel load all metadata.json
1 parent 8be3a0e commit 6304980

1 file changed

Lines changed: 47 additions & 22 deletions

File tree

codeclash/viewer/app.py

Lines changed: 47 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import json
99
import logging
1010
import shutil
11+
from concurrent.futures import ThreadPoolExecutor, as_completed
1112
from dataclasses import dataclass
1213
from pathlib import Path
1314
from typing import Any
@@ -194,10 +195,25 @@ def get_agent_info_from_metadata(metadata: Metadata) -> list[AgentInfo]:
194195
return agents
195196

196197

198+
def _load_game_metadata(folder_info: dict[str, Any]) -> dict[str, Any]:
199+
"""Helper function to load metadata for a single game folder in parallel"""
200+
item = Path(folder_info["full_path"])
201+
metadata = load_metadata(item)
202+
folder_info["round_info"] = metadata.round_count_info
203+
folder_info["models"] = metadata.models
204+
folder_info["game_name"] = metadata.game_name
205+
folder_info["created_timestamp"] = metadata.get_path("created_timestamp")
206+
return folder_info
207+
208+
197209
def find_all_game_folders(base_dir: Path) -> list[dict[str, Any]]:
198-
"""Recursively find all folders and mark which ones contain metadata.json"""
210+
"""Recursively find all folders and mark which ones contain metadata.json
211+
212+
Uses parallel loading to speed up metadata.json reading for many game folders.
213+
"""
199214
all_folders = []
200215
game_folders = set() # Track which folders are actual game folders
216+
game_folder_infos = [] # Folders that need metadata loaded
201217

202218
def scan_directory(directory: Path, relative_path: str = ""):
203219
if not directory.exists() or not directory.is_dir():
@@ -207,33 +223,23 @@ def scan_directory(directory: Path, relative_path: str = ""):
207223
for item in directory.iterdir():
208224
if item.is_dir():
209225
current_relative = relative_path + "/" + item.name if relative_path else item.name
210-
211226
depth = current_relative.count("/")
212227

213228
# Check if this directory is a game folder
214229
if is_game_folder(item):
215-
# Load metadata once
216-
metadata = load_metadata(item)
217-
round_info = metadata.round_count_info
218-
models = metadata.models
219-
game_name = metadata.game_name
220-
created_timestamp = metadata.get_path("created_timestamp")
221230
game_folders.add(current_relative)
222-
all_folders.append(
223-
{
224-
"name": current_relative,
225-
"full_path": str(item),
226-
"round_info": round_info, # Now stores (completed, total) tuple or None
227-
"models": models,
228-
"game_name": game_name,
229-
"created_timestamp": created_timestamp,
230-
"is_game": True,
231-
"depth": depth,
232-
"parent": relative_path if relative_path else None,
233-
}
234-
)
231+
# Store minimal info, will load metadata in parallel later
232+
folder_info = {
233+
"name": current_relative,
234+
"full_path": str(item),
235+
"is_game": True,
236+
"depth": depth,
237+
"parent": relative_path if relative_path else None,
238+
}
239+
all_folders.append(folder_info)
240+
game_folder_infos.append(folder_info)
235241
else:
236-
# Add as intermediate folder if it has game folders in subdirectories
242+
# Add as intermediate folder
237243
all_folders.append(
238244
{
239245
"name": current_relative,
@@ -254,8 +260,27 @@ def scan_directory(directory: Path, relative_path: str = ""):
254260
# Skip directories we can't access
255261
pass
256262

263+
# First pass: scan directory structure
257264
scan_directory(base_dir)
258265

266+
# Second pass: load metadata.json files in parallel
267+
if game_folder_infos:
268+
with ThreadPoolExecutor(max_workers=min(32, len(game_folder_infos))) as executor:
269+
futures = {
270+
executor.submit(_load_game_metadata, folder_info): folder_info for folder_info in game_folder_infos
271+
}
272+
for future in as_completed(futures):
273+
try:
274+
future.result() # Updates folder_info in place
275+
except Exception as e:
276+
# If metadata loading fails, set default values
277+
folder_info = futures[future]
278+
folder_info["round_info"] = None
279+
folder_info["models"] = []
280+
folder_info["game_name"] = ""
281+
folder_info["created_timestamp"] = None
282+
logger.warning(f"Failed to load metadata for {folder_info['name']}: {e}")
283+
259284
# Filter out intermediate folders that don't lead to any game folders
260285
filtered_folders = []
261286
for folder in sorted(all_folders, key=lambda x: x["name"]):

0 commit comments

Comments
 (0)