You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
144 lines
3.9 KiB
144 lines
3.9 KiB
from app.db import get_db |
|
from pathlib import Path |
|
|
|
def get_tenant_row(db, tenant): |
|
cur = db.cursor() |
|
cur.execute( |
|
"SELECT id, storage_root FROM tenants WHERE slug = %s LIMIT 1", |
|
(tenant,) |
|
) |
|
row = cur.fetchone() |
|
if not row: |
|
return None |
|
return row |
|
|
|
def get_device_row(db, device_id): |
|
cur = db.cursor() |
|
cur.execute( |
|
"SELECT id, device_name, relative_path FROM devices WHERE id = %s LIMIT 1", |
|
(device_id,) |
|
) |
|
row = cur.fetchone() |
|
if not row: |
|
return None |
|
return row |
|
|
|
def resolve_source_relative_path(storage_root, device_relative_path, input_filename): |
|
base = Path(storage_root) / device_relative_path |
|
if not base.exists(): |
|
raise FileNotFoundError(f"Device base path not found: {base}") |
|
|
|
candidates = [] |
|
|
|
for p in base.rglob("*"): |
|
if not p.is_file(): |
|
continue |
|
name = p.name |
|
if name == input_filename or name.endswith("__" + input_filename): |
|
candidates.append(p) |
|
|
|
if not candidates: |
|
raise FileNotFoundError( |
|
f"Could not locate source file for {input_filename} under {base}" |
|
) |
|
|
|
candidates.sort(key=lambda p: p.stat().st_mtime, reverse=True) |
|
chosen = candidates[0] |
|
|
|
rel = chosen.relative_to(Path(storage_root)) |
|
return str(rel) |
|
|
|
def create_video_job(tenant, device_id, input_filename, profile="default"): |
|
db = get_db() |
|
|
|
tenant_row = get_tenant_row(db, tenant) |
|
if not tenant_row: |
|
raise Exception(f"Tenant not found: {tenant}") |
|
|
|
device_row = get_device_row(db, device_id) |
|
if not device_row: |
|
raise Exception(f"Device not found: {device_id}") |
|
|
|
tenant_id = tenant_row["id"] |
|
storage_root = tenant_row["storage_root"] |
|
device_relative_path = device_row["relative_path"] |
|
|
|
source_relative_path = resolve_source_relative_path( |
|
storage_root, |
|
device_relative_path, |
|
input_filename |
|
) |
|
|
|
cur = db.cursor() |
|
cur.execute( |
|
""" |
|
INSERT INTO video_jobs ( |
|
tenant_id, |
|
device_id, |
|
source_file_id, |
|
source_relative_path, |
|
source_original_filename, |
|
requested_profile, |
|
requested_gpu_preference, |
|
status, |
|
progress_percent |
|
) VALUES (%s, %s, NULL, %s, %s, %s, 'auto', 'queued', 0) |
|
""", |
|
(tenant_id, device_id, source_relative_path, input_filename, profile) |
|
) |
|
db.commit() |
|
return cur.lastrowid |
|
|
|
def list_jobs_for_tenant(tenant): |
|
db = get_db() |
|
|
|
tenant_row = get_tenant_row(db, tenant) |
|
if not tenant_row: |
|
return [] |
|
|
|
tenant_id = tenant_row["id"] |
|
|
|
cur = db.cursor() |
|
cur.execute( |
|
""" |
|
SELECT |
|
id, |
|
device_id, |
|
source_original_filename, |
|
requested_profile, |
|
status, |
|
progress_percent, |
|
assigned_processor, |
|
output_relative_path, |
|
error_message, |
|
created_at, |
|
started_at, |
|
completed_at |
|
FROM video_jobs |
|
WHERE tenant_id = %s |
|
ORDER BY id DESC |
|
LIMIT 100 |
|
""", |
|
(tenant_id,) |
|
) |
|
|
|
rows = cur.fetchall() |
|
|
|
out = [] |
|
for r in rows: |
|
out.append({ |
|
"id": r["id"], |
|
"device_id": r["device_id"], |
|
"filename": r["source_original_filename"], |
|
"profile": r["requested_profile"], |
|
"status": r["status"], |
|
"progress_percent": r["progress_percent"], |
|
"assigned_processor": r["assigned_processor"], |
|
"output_relative_path": r["output_relative_path"], |
|
"error_message": r["error_message"], |
|
"created_at": str(r["created_at"]) if r["created_at"] is not None else None, |
|
"started_at": str(r["started_at"]) if r["started_at"] is not None else None, |
|
"completed_at": str(r["completed_at"]) if r["completed_at"] is not None else None, |
|
}) |
|
|
|
return out
|
|
|