diff --git a/services/sofiia-console/app/main.py b/services/sofiia-console/app/main.py
index 15f908ec..2da7718d 100644
--- a/services/sofiia-console/app/main.py
+++ b/services/sofiia-console/app/main.py
@@ -1392,9 +1392,11 @@ async def api_aurora_compare(job_id: str) -> Dict[str, Any]:
"download_url": (result_file or {}).get("url"),
}
+ output_media_path: Optional[Path] = None
if result_file and output_dir:
out_path = Path(output_dir) / result_file["name"]
if out_path.exists():
+ output_media_path = out_path
after["file_size_mb"] = round(out_path.stat().st_size / (1024 * 1024), 2)
_probe = _ffprobe_quick(out_path)
if _probe:
@@ -1419,6 +1421,14 @@ async def api_aurora_compare(job_id: str) -> Dict[str, Any]:
"time_ms": step.get("time_ms"),
})
+ frame_preview = _aurora_ensure_compare_frame_preview(
+ job_id=job_id,
+ media_type=str(status.get("media_type") or ""),
+ input_path=Path(input_path) if input_path else None,
+ output_path=output_media_path,
+ output_dir=Path(output_dir) if output_dir else None,
+ )
+
return {
"job_id": job_id,
"status": status.get("status"),
@@ -1429,11 +1439,97 @@ async def api_aurora_compare(job_id: str) -> Dict[str, Any]:
"after": after,
"faces_detected": faces_total,
"enhance_steps": enhance_steps,
+ "frame_preview": frame_preview,
"folder_path": output_dir,
"input_path": input_path,
}
+def _aurora_extract_frame_preview(source: Path, target: Path, *, second: float = 1.0) -> bool:
+ """Write a JPEG preview frame for image/video sources."""
+ if not source.exists():
+ return False
+ target.parent.mkdir(parents=True, exist_ok=True)
+ ext = source.suffix.lower()
+ if ext in {".jpg", ".jpeg", ".png", ".webp", ".bmp", ".tif", ".tiff"}:
+ try:
+ target.write_bytes(source.read_bytes())
+ return True
+ except Exception:
+ return False
+
+ ffmpeg = [
+ "ffmpeg",
+ "-hide_banner",
+ "-loglevel",
+ "error",
+ "-y",
+ "-ss",
+ f"{max(0.0, float(second)):.3f}",
+ "-i",
+ str(source),
+ "-frames:v",
+ "1",
+ "-q:v",
+ "2",
+ str(target),
+ ]
+ try:
+ run = subprocess.run(ffmpeg, capture_output=True, text=True, timeout=20)
+ if run.returncode == 0 and target.exists() and target.stat().st_size > 0:
+ return True
+ except Exception:
+ pass
+
+ # Fallback for short videos / odd timestamps.
+ ffmpeg_fallback = ffmpeg[:]
+ ffmpeg_fallback[6] = "0.0"
+ try:
+ run = subprocess.run(ffmpeg_fallback, capture_output=True, text=True, timeout=20)
+ return run.returncode == 0 and target.exists() and target.stat().st_size > 0
+ except Exception:
+ return False
+
+
+def _aurora_ensure_compare_frame_preview(
+ *,
+ job_id: str,
+ media_type: str,
+ input_path: Optional[Path],
+ output_path: Optional[Path],
+ output_dir: Optional[Path],
+) -> Optional[Dict[str, Any]]:
+ if not output_dir or not output_dir.exists():
+ return None
+ if not input_path or not input_path.exists():
+ return None
+ if not output_path or not output_path.exists():
+ return None
+
+ before_name = "_compare_before.jpg"
+ after_name = "_compare_after.jpg"
+ before_path = output_dir / before_name
+ after_path = output_dir / after_name
+ ts = 1.0 if media_type == "video" else 0.0
+
+ if not before_path.exists() or before_path.stat().st_size == 0:
+ _aurora_extract_frame_preview(input_path, before_path, second=ts)
+ if not after_path.exists() or after_path.stat().st_size == 0:
+ _aurora_extract_frame_preview(output_path, after_path, second=ts)
+
+ if not before_path.exists() or not after_path.exists():
+ return None
+ if before_path.stat().st_size <= 0 or after_path.stat().st_size <= 0:
+ return None
+
+ quoted_job = quote(job_id, safe="")
+ return {
+ "timestamp_sec": ts,
+ "before_url": f"/api/aurora/files/{quoted_job}/{quote(before_name, safe='')}",
+ "after_url": f"/api/aurora/files/{quoted_job}/{quote(after_name, safe='')}",
+ }
+
+
def _ffprobe_quick(filepath: Path) -> Dict[str, Any]:
"""Quick ffprobe for resolution, codec, duration, fps, frame count."""
if not filepath.exists():
diff --git a/services/sofiia-console/static/index.html b/services/sofiia-console/static/index.html
new file mode 100644
index 00000000..a47b6a91
--- /dev/null
+++ b/services/sofiia-console/static/index.html
@@ -0,0 +1,8467 @@
+
+
+
+
+
+ SOFIIA — Control Console
+
+
+
+
+
+
+
+
SOFIIA
+
Network Control Panel · DAARION
+
+
+
+
+
+ Ключ знаходиться у файлі .env.console.local
+ або у змінній середовища SOFIIA_CONSOLE_API_KEY на сервері.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Media Forensics Upload
+
+
+
+
+
+
📂
+
Перетягніть файл або натисніть для вибору
+
Video: MP4/AVI/MOV · Audio: MP3/WAV/FLAC · Photo: JPG/PNG/TIFF
+
+
+
+
+ Файл—
+
+
+
+ Сервіс Aurorachecking...
+
+
+
+ ⚙ Export options
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Job Status
+
Job ID—
+
Статусidle
+
Етап—
+
Черга—
+
Минуло—
+
ETA—
+
FPS (confidence)—
+
Збереження—
+
+
+
+
+
+
+
+
Pre-analysis
+
Тип—
+
Обличчя0
+
Номерні знаки0
+
Якість—
+
Рекоменд. пріоритетbalanced
+
Рекомендації Aurora:
+
+
Параметри обробки:
+
+
+
+
+
+ Пріоритет: Обличчя
+ Номерні знаки
+
+
+
Баланс: рівномірно
+
+
Пресет швидкості:
+
+
+
+
+
+
+
+
+
+
+
+
Audio Analysis
+
Duration—
+
Sample rate—
+
Channels—
+
Codec—
+
Bitrate—
+
Suggested priorityspeech
+
Рекомендації Aurora:
+
+
+
+
+
Результат обробки
+
+
+
+
+
+
+ | Параметр |
+ До (input) |
+ Після (output) |
+
+
+
+
+
+
+
+
+
+ Виявлено облич0
+
+
+
+
Mode—
+
Input hash—
+
Digital signature—
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 🚗 Номерні знаки (ALPR)
+ 0
+
+
+
+
+
+
+
+
+ ✨ Kling AI — Generative Enhancement
+ not submitted
+
+
Паралельне покращення відео через генеративний AI (відео→відео + image-to-video)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Результат Kling AI:
+
+
+
+
+
+
+
+
Recent Jobs
+
+ loading...
+
+
+
+
+
+
Завантаження історії job...
+
+
+
+
+
Aurora Operator Chat
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ⚠️ Monitor відсутній на ноді:
+ — необхідний агент для кожної ноди
+
+
+
+
+
+
+
+
+
+ DEBUG
+ | fetch: —
+ | nodes: —
+ | items: —
+ | ok: —/—
+ | errors: —
+ |
+
+
+
+
+
+
+
+
+
Оберіть агента зі списку
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Тип:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ⬤ decision · agent_run · task · meeting
+ ○ archived/superseded
+
+
+
+
+
+ 🧹 Hygiene результат
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 📜 Дерево сесій (legacy tree view)
+
+
+
+
+
+
+
+
+
+
+
+
Governance операції
+
+
+
Результат
+
+
+
+
+
+
+
Інтеграції CTO
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
🖥 Network Control Panel
+
+
+
—
+
+
+
+ ⚠️ Обовʼязковий агент відсутній:
+
+
+
+ 🔀 Version drift: gateway і control-plane мають різні build_sha —
+
+
+
+
+
+
+
+
Memory Service
+
+
+
+
+
🧠 Статус Memory Service
+
Завантаження...
+
+
+
+
🎙️ Голосові налаштування
+
+ STT (розпізнавання)
+ whisper-large-v3-turbo
+
+
+ TTS (синтез мовлення)
+ edge-tts + macOS say
+
+
+
Голос TTS
+
+
+
Polina (uk-UA)
+
Ostap (uk-UA)
+
Milena (macOS)
+
Yuri (macOS)
+
+
+
+
+
+
+
🧪 Тест TTS
+
+
+
+
+
+
+
+
+
+
+
🎯 CTO Strategic Dashboard
+
+
+
+
+
+
+
+
+
+
+
+
📊 Стан зараз
+
+
+
+
🚀 Workflow Launcher
+
+
+
+
+
+
+
+
+
+
🧾 Lessons
+
+
+
+
+
+
+
+
+
+
🛡 Governance Gates
+
+
+
+
+
+
Оберіть проєкт і натисніть Preview
+
+
+
+
+
+
+
+
+
+
+
+
⚡ Відкриті сигнали
+
+
Немає даних — оберіть проєкт і натисніть «⚡ Сигнали»
+
+
+
🔴 Топ ризик-задачі
+
+
+
+
+
+
🕸 Mini Graph
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
✓ Mitigation Plan створено
+
+
+
+
+
+
+
+
📚 Playbooks
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
🧾 Lessons Learned
+
+
+
+
+
📌 Improvement Tasks
+
+
+
+
+
+
📈 Trend vs Previous
+
+
+
+
+
📄 Report
+
+
+
+
+
+
+
+
+
+
+
📜 Governance Audit Trail
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
🌐 Portfolio View
+
+
+
+
+
+
+
+
+
+
+
🌐
+
Портфель порожній
+
Немає метрик. Створи проєкт або згенеруй Snapshots для всіх проєктів.
+
+
+
+
+
+
+
+
+
+
📁 Проєкти
+
+
+
+ | Проєкт |
+ WIP |
+ Done |
+ [RISK] |
+ Runs |
+ Цикл |
+ Якість |
+ Updated |
+ Lesson |
+
+
+
+ | Завантаження... |
+
+
+
+
+
+
+
+
+
+ 📡 Portfolio Drift
+
+
+
+
+ Autopilot:
+ OFF
+
+
+
+
Натисніть ↻ для завантаження
+
+
+
+
⚡ Топ Сигнали
+
+
+
+
+
+
+
+
+
+
💰 Бюджет провайдерів AI
+
+
+
+
+
+
+
+
+
+
24 год
+
$0.00000
+
витрачено
+
+
+
7 днів
+
$0.00000
+
витрачено
+
+
+
30 днів
+
$0.00000
+
витрачено
+
+
+
+
+
+
+
+
+
+
🗂 Каталог моделей
+
+
+
+
+
Натисніть "Оновити" для завантаження
+
+
+
+
+
+
🧠 Тест Auto-Router (Cursor-style)
+
+
+
+
+
+
+
+
+
⚙️ Налаштування лімітів
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+