diff --git a/app/routers/home.py b/app/routers/home.py index 3d5e293..5b19d37 100644 --- a/app/routers/home.py +++ b/app/routers/home.py @@ -43,11 +43,15 @@ async def home(request: Request): try: from ..services.recipe_service import RecipeService from ..dependencies import get_redis_client, get_cache_manager + import logging + logger = logging.getLogger(__name__) recipe_service = RecipeService(get_redis_client(), get_cache_manager()) recipes = await recipe_service.list_recipes(user.actor_id) stats["recipes"] = len(recipes) - except Exception: - pass + logger.info(f"Home page: found {len(recipes)} recipes for {user.actor_id}") + except Exception as e: + import logging + logging.getLogger(__name__).error(f"Failed to get recipe count: {e}") try: from ..services.run_service import RunService from ..dependencies import get_redis_client, get_cache_manager diff --git a/app/routers/runs.py b/app/routers/runs.py index eda4cda..1e4e219 100644 --- a/app/routers/runs.py +++ b/app/routers/runs.py @@ -182,6 +182,7 @@ async def get_run( # Build artifacts list from output and inputs artifacts = [] + output_media_type = None if run.get("output_hash"): # Detect media type using magic bytes output_hash = run["output_hash"] @@ -192,6 +193,7 @@ async def get_run( if cache_path and cache_path.exists(): simple_type = detect_media_type(cache_path) media_type = type_to_mime(simple_type) + output_media_type = media_type except Exception: pass artifacts.append({ @@ -263,6 +265,7 @@ async def get_run( artifacts=artifacts, run_inputs=run_inputs, dag_elements=dag_elements, + output_media_type=output_media_type, active_tab="runs", ) @@ -308,6 +311,46 @@ async def list_runs( if wants_json(request): return {"runs": runs, "offset": offset, "limit": limit, "has_more": has_more} + # Add media info for inline previews (only for HTML) + cache_manager = get_cache_manager() + from ..services.run_service import detect_media_type + + def type_to_mime(simple_type: str) -> str: + if simple_type == "video": + return "video/mp4" + elif simple_type == "image": + return "image/jpeg" + elif simple_type == "audio": + return "audio/mpeg" + return None + + for run in runs: + # Add output media info + if run.get("output_hash"): + try: + cache_path = cache_manager.get_by_content_hash(run["output_hash"]) + if cache_path and cache_path.exists(): + simple_type = detect_media_type(cache_path) + run["output_media_type"] = type_to_mime(simple_type) + except Exception: + pass + + # Add input media info (first 3 inputs) + input_previews = [] + inputs = run.get("inputs", []) + if isinstance(inputs, list): + for input_hash in inputs[:3]: + preview = {"hash": input_hash, "media_type": None} + try: + cache_path = cache_manager.get_by_content_hash(input_hash) + if cache_path and cache_path.exists(): + simple_type = detect_media_type(cache_path) + preview["media_type"] = type_to_mime(simple_type) + except Exception: + pass + input_previews.append(preview) + run["input_previews"] = input_previews + templates = get_templates(request) return render(templates, "runs/list.html", request, runs=runs, diff --git a/app/services/run_service.py b/app/services/run_service.py index 749755f..207e5e9 100644 --- a/app/services/run_service.py +++ b/app/services/run_service.py @@ -50,7 +50,10 @@ def detect_media_type(cache_path: Path) -> str: # Video signatures if header[:4] == b'\x1a\x45\xdf\xa3': # WebM/MKV return "video" - if len(header) > 8 and header[4:8] == b'ftyp': # MP4/MOV + if len(header) > 8 and header[4:8] == b'ftyp': # MP4/MOV/M4A + # Check for audio-only M4A + if len(header) > 11 and header[8:12] in (b'M4A ', b'm4a '): + return "audio" return "video" if header[:4] == b'RIFF' and len(header) > 12 and header[8:12] == b'AVI ': # AVI return "video" @@ -65,6 +68,16 @@ def detect_media_type(cache_path: Path) -> str: if header[:4] == b'RIFF' and len(header) > 12 and header[8:12] == b'WEBP': # WebP return "image" + # Audio signatures + if header[:3] == b'ID3' or header[:2] == b'\xff\xfb': # MP3 + return "audio" + if header[:4] == b'fLaC': # FLAC + return "audio" + if header[:4] == b'OggS': # Ogg (could be audio or video, assume audio) + return "audio" + if header[:4] == b'RIFF' and len(header) > 12 and header[8:12] == b'WAVE': # WAV + return "audio" + return "unknown" diff --git a/app/templates/runs/_run_card.html b/app/templates/runs/_run_card.html index a987fce..88e03db 100644 --- a/app/templates/runs/_run_card.html +++ b/app/templates/runs/_run_card.html @@ -23,7 +23,7 @@ {{ run.created_at }} -