Add campaign layer, GPU model caching, file browser cleanup

- Campaigns: new table, CRUD API, React context + provider
- Sessions: scoped to campaigns, paths under campaigns/{id}/
- File browser: scoped per campaign, removed copy/paste/autoPlay
- Sidebar: campaign selector dropdown at top
- Transcribe: GPU model cached/released via job counter
- Jobs: status text updates dynamically in real-time
- Auto-redirect: blocked when summarize job is active
This commit is contained in:
KansaiGaijin
2026-07-10 21:41:29 +12:00
parent db19cfd05e
commit bc7ac32de3
19 changed files with 702 additions and 182 deletions

View File

@@ -38,8 +38,13 @@ def _row_to_dict(row) -> dict:
@router.get("")
def list_sessions():
rows = db.get_conn().execute("SELECT * FROM sessions ORDER BY created_at DESC").fetchall()
def list_sessions(campaign_id: str = None):
if campaign_id:
rows = db.get_conn().execute(
"SELECT * FROM sessions WHERE campaign_id = ? ORDER BY created_at DESC", (campaign_id,)
).fetchall()
else:
rows = db.get_conn().execute("SELECT * FROM sessions ORDER BY created_at DESC").fetchall()
sessions = []
for r in rows:
s = _row_to_dict(r)
@@ -108,24 +113,38 @@ def _run_transcription(session_id: str, video_path: Path, audio_path: Path, tran
)
job_id = db.create_job(session_id, "transcribe")
jobs.submit(job_id, run)
jobs.submit(job_id, run, requires_gpu=True)
return job_id
@router.post("")
async def create_session(name: str = Form(...), file: UploadFile = File(None), upload_path: str = Form(None)):
async def create_session(
name: str = Form(...),
file: UploadFile = File(None),
upload_path: str = Form(None),
campaign_id: str = Form(None),
):
session_id = db.new_id()
if campaign_id:
upload_dir = config.campaign_dir(campaign_id)
audio_dir = config.campaign_audio_dir(campaign_id)
transcript_dir = config.campaign_transcript_dir(campaign_id)
else:
upload_dir = config.UPLOAD_DIR
audio_dir = config.AUDIO_DIR
transcript_dir = config.TRANSCRIPT_DIR
if upload_path:
src = Path(upload_path)
if not src.exists():
raise HTTPException(400, f"Uploaded file not found at {upload_path}")
safe_filename = os.path.basename(src)
video_path = config.UPLOAD_DIR / f"{session_id}_{safe_filename}"
video_path = upload_dir / f"{session_id}_{safe_filename}"
shutil.move(str(src), str(video_path))
elif file:
safe_filename = os.path.basename(file.filename or f"{session_id}")
video_path = config.UPLOAD_DIR / f"{session_id}_{safe_filename}"
video_path = upload_dir / f"{session_id}_{safe_filename}"
with open(video_path, "wb") as buffer:
shutil.copyfileobj(file.file, buffer)
else:
@@ -133,13 +152,13 @@ async def create_session(name: str = Form(...), file: UploadFile = File(None), u
with db.tx() as conn:
conn.execute(
"INSERT INTO sessions (id, name, original_filename, video_path, status, created_at) "
"VALUES (?, ?, ?, ?, 'uploaded', ?)",
(session_id, name, safe_filename, str(video_path), db.now()),
"INSERT INTO sessions (id, name, original_filename, video_path, campaign_id, status, created_at) "
"VALUES (?, ?, ?, ?, ?, 'uploaded', ?)",
(session_id, name, safe_filename, str(video_path), campaign_id, db.now()),
)
audio_path = config.AUDIO_DIR / f"{session_id}.wav"
transcript_path = config.TRANSCRIPT_DIR / f"{session_id}.json"
audio_path = audio_dir / f"{session_id}.wav"
transcript_path = transcript_dir / f"{session_id}.json"
job_id = _run_transcription(session_id, video_path, audio_path, transcript_path)
return {"session_id": session_id, "job_id": job_id}
@@ -153,8 +172,13 @@ def retry_transcription(session_id: str):
raise HTTPException(404, "Session not found")
session = _row_to_dict(row)
video_path = Path(session["video_path"])
audio_path = config.AUDIO_DIR / f"{session_id}.wav"
transcript_path = config.TRANSCRIPT_DIR / f"{session_id}.json"
campaign_id = session.get("campaign_id")
if campaign_id:
audio_path = config.campaign_audio_dir(campaign_id) / f"{session_id}.wav"
transcript_path = config.campaign_transcript_dir(campaign_id) / f"{session_id}.json"
else:
audio_path = config.AUDIO_DIR / f"{session_id}.wav"
transcript_path = config.TRANSCRIPT_DIR / f"{session_id}.json"
job_id = _run_transcription(session_id, video_path, audio_path, transcript_path)
return {"job_id": job_id}