otb-cloud secure encrypted backups
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.
 
 
 
 
 

197 lines
6.0 KiB

import time
import subprocess
from datetime import datetime
from pathlib import Path
from app import create_app
from app.db import get_db
from app.services.gpu_select import select_processor, release
INTEL_DEV = "/dev/dri/renderD129"
AMD_DEV = "/dev/dri/renderD128"
def run_ffmpeg(cmd):
return subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
def build_absolute_source_path(db, job):
with db.cursor() as cur:
cur.execute(
"SELECT storage_root FROM tenants WHERE id = %s",
(job["tenant_id"],)
)
tenant_row = cur.fetchone()
if not tenant_row:
raise RuntimeError(f"Tenant id {job['tenant_id']} not found")
storage_root = tenant_row["storage_root"]
return str(Path(storage_root) / job["source_relative_path"])
def process_job(db, job):
job_id = job["id"]
src = build_absolute_source_path(db, job)
profile = job["requested_profile"]
processor = select_processor()
device = INTEL_DEV if processor == "intel" else AMD_DEV
output = str(Path(src).with_name(Path(src).stem + "_processed.mp4"))
if profile == "portrait_web":
vf = "format=nv12,hwupload,scale_vaapi=w=720:h=1280:force_original_aspect_ratio=decrease"
else:
vf = "format=nv12,hwupload,scale_vaapi=w=1280:h=720:force_original_aspect_ratio=decrease"
if processor in ("intel", "amd"):
cmd = [
"ffmpeg", "-hide_banner", "-y",
"-vaapi_device", device,
"-i", src,
"-vf", vf,
"-c:v", "h264_vaapi",
"-b:v", "3M",
"-maxrate", "3M",
"-bufsize", "6M",
"-c:a", "aac", "-b:a", "128k",
output
]
else:
cmd = [
"ffmpeg", "-hide_banner", "-y",
"-i", src,
"-c:v", "libx264",
"-preset", "medium",
"-crf", "23",
"-c:a", "aac", "-b:a", "128k",
output
]
start = datetime.utcnow()
try:
result = run_ffmpeg(cmd)
end = datetime.utcnow()
with db.cursor() as cur:
if result.returncode == 0:
rel_output = None
try:
with db.cursor() as cur2:
cur2.execute(
"SELECT storage_root FROM tenants WHERE id = %s",
(job["tenant_id"],)
)
tenant_row = cur2.fetchone()
if tenant_row:
rel_output = str(Path(output).relative_to(Path(tenant_row["storage_root"])))
except Exception:
rel_output = output
cur.execute(
"""
UPDATE video_jobs
SET status='complete',
assigned_processor=%s,
output_relative_path=%s,
progress_percent=100,
started_at=COALESCE(started_at, %s),
completed_at=%s,
log_excerpt=%s,
error_message=NULL
WHERE id=%s
""",
(
processor,
rel_output or output,
start,
end,
(result.stderr or "")[:1000],
job_id,
),
)
else:
cur.execute(
"""
UPDATE video_jobs
SET status='failed',
error_message=%s,
log_excerpt=%s,
completed_at=%s
WHERE id=%s
""",
(
"ffmpeg failed",
(result.stderr or "")[:4000],
end,
job_id,
),
)
db.commit()
except Exception as e:
with db.cursor() as cur:
cur.execute(
"""
UPDATE video_jobs
SET status='failed',
error_message=%s,
completed_at=UTC_TIMESTAMP()
WHERE id=%s
""",
(str(e)[:1000], job_id),
)
db.commit()
finally:
if processor in ("intel", "amd"):
release(processor)
def run_worker():
app = create_app()
with app.app_context():
print("video worker started", flush=True)
while True:
try:
db = get_db()
try:
db.rollback()
except Exception:
pass
with db.cursor() as cur:
cur.execute(
"""
SELECT *
FROM video_jobs
WHERE status='queued'
ORDER BY id ASC
LIMIT 1
"""
)
job = cur.fetchone()
if job:
print(
f"worker picked job id={job['id']} source={job['source_relative_path']}",
flush=True
)
cur.execute(
"""
UPDATE video_jobs
SET status='processing',
started_at=COALESCE(started_at, UTC_TIMESTAMP()),
progress_percent=5
WHERE id=%s
""",
(job["id"],),
)
db.commit()
process_job(db, job)
except Exception as e:
print(f"worker loop error: {e}", flush=True)
time.sleep(5)