django and fastapi apps
This commit is contained in:
54
api/deps.py
Normal file
54
api/deps.py
Normal file
@@ -0,0 +1,54 @@
|
||||
"""
|
||||
FastAPI dependencies.
|
||||
|
||||
Provides database sessions, settings, and common dependencies.
|
||||
"""
|
||||
|
||||
import os
|
||||
from functools import lru_cache
|
||||
from typing import Generator
|
||||
|
||||
import django
|
||||
from django.conf import settings as django_settings
|
||||
|
||||
# Initialize Django
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mpr.settings")
|
||||
django.setup()
|
||||
|
||||
from mpr.media_assets.models import MediaAsset, TranscodeJob, TranscodePreset
|
||||
|
||||
|
||||
@lru_cache
|
||||
def get_settings():
|
||||
"""Get Django settings."""
|
||||
return django_settings
|
||||
|
||||
|
||||
def get_asset(asset_id: str) -> MediaAsset:
|
||||
"""Get asset by ID or raise 404."""
|
||||
from fastapi import HTTPException
|
||||
|
||||
try:
|
||||
return MediaAsset.objects.get(id=asset_id)
|
||||
except MediaAsset.DoesNotExist:
|
||||
raise HTTPException(status_code=404, detail="Asset not found")
|
||||
|
||||
|
||||
def get_preset(preset_id: str) -> TranscodePreset:
|
||||
"""Get preset by ID or raise 404."""
|
||||
from fastapi import HTTPException
|
||||
|
||||
try:
|
||||
return TranscodePreset.objects.get(id=preset_id)
|
||||
except TranscodePreset.DoesNotExist:
|
||||
raise HTTPException(status_code=404, detail="Preset not found")
|
||||
|
||||
|
||||
def get_job(job_id: str) -> TranscodeJob:
|
||||
"""Get job by ID or raise 404."""
|
||||
from fastapi import HTTPException
|
||||
|
||||
try:
|
||||
return TranscodeJob.objects.get(id=job_id)
|
||||
except TranscodeJob.DoesNotExist:
|
||||
raise HTTPException(status_code=404, detail="Job not found")
|
||||
56
api/main.py
Normal file
56
api/main.py
Normal file
@@ -0,0 +1,56 @@
|
||||
"""
|
||||
MPR FastAPI Application
|
||||
|
||||
Main entry point for the REST API.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Add project root to path
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
# Initialize Django before importing models
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mpr.settings")
|
||||
|
||||
import django
|
||||
|
||||
django.setup()
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
from api.routes import assets_router, jobs_router, presets_router, system_router
|
||||
|
||||
app = FastAPI(
|
||||
title="MPR API",
|
||||
description="Media Processor REST API",
|
||||
version="0.1.0",
|
||||
docs_url="/docs",
|
||||
redoc_url="/redoc",
|
||||
)
|
||||
|
||||
# CORS
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["http://mpr.local.ar", "http://localhost:5173"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# Routes
|
||||
app.include_router(system_router)
|
||||
app.include_router(assets_router)
|
||||
app.include_router(presets_router)
|
||||
app.include_router(jobs_router)
|
||||
|
||||
|
||||
@app.get("/")
|
||||
def root():
|
||||
"""API root."""
|
||||
return {
|
||||
"name": "MPR API",
|
||||
"version": "0.1.0",
|
||||
"docs": "/docs",
|
||||
}
|
||||
8
api/routes/__init__.py
Normal file
8
api/routes/__init__.py
Normal file
@@ -0,0 +1,8 @@
|
||||
"""API Routes."""
|
||||
|
||||
from .assets import router as assets_router
|
||||
from .jobs import router as jobs_router
|
||||
from .presets import router as presets_router
|
||||
from .system import router as system_router
|
||||
|
||||
__all__ = ["assets_router", "jobs_router", "presets_router", "system_router"]
|
||||
90
api/routes/assets.py
Normal file
90
api/routes/assets.py
Normal file
@@ -0,0 +1,90 @@
|
||||
"""
|
||||
Asset endpoints - media file registration and metadata.
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
|
||||
from api.deps import get_asset
|
||||
from api.schemas import AssetCreate, AssetResponse, AssetUpdate
|
||||
|
||||
router = APIRouter(prefix="/assets", tags=["assets"])
|
||||
|
||||
|
||||
@router.post("/", response_model=AssetResponse, status_code=201)
|
||||
def create_asset(data: AssetCreate):
|
||||
"""
|
||||
Register a media file as an asset.
|
||||
|
||||
The file must exist on disk. A probe task will be queued
|
||||
to extract metadata asynchronously.
|
||||
"""
|
||||
from pathlib import Path
|
||||
|
||||
from mpr.media_assets.models import MediaAsset
|
||||
|
||||
# Validate file exists
|
||||
path = Path(data.file_path)
|
||||
if not path.exists():
|
||||
raise HTTPException(status_code=400, detail="File not found")
|
||||
|
||||
# Create asset
|
||||
asset = MediaAsset.objects.create(
|
||||
filename=data.filename or path.name,
|
||||
file_path=str(path.absolute()),
|
||||
file_size=path.stat().st_size,
|
||||
)
|
||||
|
||||
# TODO: Queue probe task via gRPC/Celery
|
||||
|
||||
return asset
|
||||
|
||||
|
||||
@router.get("/", response_model=list[AssetResponse])
|
||||
def list_assets(
|
||||
status: Optional[str] = Query(None, description="Filter by status"),
|
||||
limit: int = Query(50, ge=1, le=100),
|
||||
offset: int = Query(0, ge=0),
|
||||
):
|
||||
"""List assets with optional filtering."""
|
||||
from mpr.media_assets.models import MediaAsset
|
||||
|
||||
qs = MediaAsset.objects.all()
|
||||
|
||||
if status:
|
||||
qs = qs.filter(status=status)
|
||||
|
||||
return list(qs[offset : offset + limit])
|
||||
|
||||
|
||||
@router.get("/{asset_id}", response_model=AssetResponse)
|
||||
def get_asset_detail(asset_id: UUID, asset=Depends(get_asset)):
|
||||
"""Get asset details."""
|
||||
return asset
|
||||
|
||||
|
||||
@router.patch("/{asset_id}", response_model=AssetResponse)
|
||||
def update_asset(asset_id: UUID, data: AssetUpdate, asset=Depends(get_asset)):
|
||||
"""Update asset metadata (comments, tags)."""
|
||||
update_fields = []
|
||||
|
||||
if data.comments is not None:
|
||||
asset.comments = data.comments
|
||||
update_fields.append("comments")
|
||||
|
||||
if data.tags is not None:
|
||||
asset.tags = data.tags
|
||||
update_fields.append("tags")
|
||||
|
||||
if update_fields:
|
||||
asset.save(update_fields=update_fields)
|
||||
|
||||
return asset
|
||||
|
||||
|
||||
@router.delete("/{asset_id}", status_code=204)
|
||||
def delete_asset(asset_id: UUID, asset=Depends(get_asset)):
|
||||
"""Delete an asset."""
|
||||
asset.delete()
|
||||
160
api/routes/jobs.py
Normal file
160
api/routes/jobs.py
Normal file
@@ -0,0 +1,160 @@
|
||||
"""
|
||||
Job endpoints - transcode/trim job management.
|
||||
"""
|
||||
|
||||
import json
|
||||
from typing import Optional
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
|
||||
from api.deps import get_asset, get_job, get_preset
|
||||
from api.schemas import JobCreate, JobResponse
|
||||
|
||||
router = APIRouter(prefix="/jobs", tags=["jobs"])
|
||||
|
||||
|
||||
@router.post("/", response_model=JobResponse, status_code=201)
|
||||
def create_job(data: JobCreate):
|
||||
"""
|
||||
Create a transcode or trim job.
|
||||
|
||||
- With preset_id: Full transcode using preset settings
|
||||
- Without preset_id but with trim_start/end: Trim only (stream copy)
|
||||
"""
|
||||
from mpr.media_assets.models import MediaAsset, TranscodeJob, TranscodePreset
|
||||
|
||||
# Get source asset
|
||||
try:
|
||||
source = MediaAsset.objects.get(id=data.source_asset_id)
|
||||
except MediaAsset.DoesNotExist:
|
||||
raise HTTPException(status_code=404, detail="Source asset not found")
|
||||
|
||||
if source.status != "ready":
|
||||
raise HTTPException(status_code=400, detail="Source asset is not ready")
|
||||
|
||||
# Get preset if specified
|
||||
preset = None
|
||||
preset_snapshot = {}
|
||||
if data.preset_id:
|
||||
try:
|
||||
preset = TranscodePreset.objects.get(id=data.preset_id)
|
||||
# Snapshot preset at job creation time
|
||||
preset_snapshot = {
|
||||
"name": preset.name,
|
||||
"container": preset.container,
|
||||
"video_codec": preset.video_codec,
|
||||
"video_bitrate": preset.video_bitrate,
|
||||
"video_crf": preset.video_crf,
|
||||
"video_preset": preset.video_preset,
|
||||
"resolution": preset.resolution,
|
||||
"framerate": preset.framerate,
|
||||
"audio_codec": preset.audio_codec,
|
||||
"audio_bitrate": preset.audio_bitrate,
|
||||
"audio_channels": preset.audio_channels,
|
||||
"audio_samplerate": preset.audio_samplerate,
|
||||
"extra_args": preset.extra_args,
|
||||
}
|
||||
except TranscodePreset.DoesNotExist:
|
||||
raise HTTPException(status_code=404, detail="Preset not found")
|
||||
|
||||
# Validate trim-only job
|
||||
if not preset and not data.trim_start and not data.trim_end:
|
||||
raise HTTPException(
|
||||
status_code=400, detail="Must specify preset_id or trim_start/trim_end"
|
||||
)
|
||||
|
||||
# Generate output filename
|
||||
output_filename = data.output_filename
|
||||
if not output_filename:
|
||||
from pathlib import Path
|
||||
|
||||
stem = Path(source.filename).stem
|
||||
ext = preset_snapshot.get("container", "mp4") if preset else "mp4"
|
||||
output_filename = f"{stem}_output.{ext}"
|
||||
|
||||
# Create job
|
||||
job = TranscodeJob.objects.create(
|
||||
source_asset=source,
|
||||
preset=preset,
|
||||
preset_snapshot=preset_snapshot,
|
||||
trim_start=data.trim_start,
|
||||
trim_end=data.trim_end,
|
||||
output_filename=output_filename,
|
||||
priority=data.priority or 0,
|
||||
)
|
||||
|
||||
# TODO: Submit job via gRPC
|
||||
|
||||
return job
|
||||
|
||||
|
||||
@router.get("/", response_model=list[JobResponse])
|
||||
def list_jobs(
|
||||
status: Optional[str] = Query(None, description="Filter by status"),
|
||||
source_asset_id: Optional[UUID] = Query(None),
|
||||
limit: int = Query(50, ge=1, le=100),
|
||||
offset: int = Query(0, ge=0),
|
||||
):
|
||||
"""List jobs with optional filtering."""
|
||||
from mpr.media_assets.models import TranscodeJob
|
||||
|
||||
qs = TranscodeJob.objects.all()
|
||||
|
||||
if status:
|
||||
qs = qs.filter(status=status)
|
||||
if source_asset_id:
|
||||
qs = qs.filter(source_asset_id=source_asset_id)
|
||||
|
||||
return list(qs[offset : offset + limit])
|
||||
|
||||
|
||||
@router.get("/{job_id}", response_model=JobResponse)
|
||||
def get_job_detail(job_id: UUID, job=Depends(get_job)):
|
||||
"""Get job details including progress."""
|
||||
return job
|
||||
|
||||
|
||||
@router.get("/{job_id}/progress")
|
||||
def get_job_progress(job_id: UUID, job=Depends(get_job)):
|
||||
"""Get real-time job progress."""
|
||||
return {
|
||||
"job_id": str(job.id),
|
||||
"status": job.status,
|
||||
"progress": job.progress,
|
||||
"current_frame": job.current_frame,
|
||||
"current_time": job.current_time,
|
||||
"speed": job.speed,
|
||||
}
|
||||
|
||||
|
||||
@router.post("/{job_id}/cancel", response_model=JobResponse)
|
||||
def cancel_job(job_id: UUID, job=Depends(get_job)):
|
||||
"""Cancel a pending or processing job."""
|
||||
if job.status not in ("pending", "processing"):
|
||||
raise HTTPException(
|
||||
status_code=400, detail=f"Cannot cancel job with status: {job.status}"
|
||||
)
|
||||
|
||||
# TODO: Cancel via gRPC
|
||||
|
||||
job.status = "cancelled"
|
||||
job.save(update_fields=["status"])
|
||||
|
||||
return job
|
||||
|
||||
|
||||
@router.post("/{job_id}/retry", response_model=JobResponse)
|
||||
def retry_job(job_id: UUID, job=Depends(get_job)):
|
||||
"""Retry a failed job."""
|
||||
if job.status != "failed":
|
||||
raise HTTPException(status_code=400, detail="Only failed jobs can be retried")
|
||||
|
||||
job.status = "pending"
|
||||
job.progress = 0
|
||||
job.error_message = None
|
||||
job.save(update_fields=["status", "progress", "error_message"])
|
||||
|
||||
# TODO: Resubmit via gRPC
|
||||
|
||||
return job
|
||||
100
api/routes/presets.py
Normal file
100
api/routes/presets.py
Normal file
@@ -0,0 +1,100 @@
|
||||
"""
|
||||
Preset endpoints - transcode configuration templates.
|
||||
"""
|
||||
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
|
||||
from api.deps import get_preset
|
||||
from api.schemas import PresetCreate, PresetResponse, PresetUpdate
|
||||
|
||||
router = APIRouter(prefix="/presets", tags=["presets"])
|
||||
|
||||
|
||||
@router.post("/", response_model=PresetResponse, status_code=201)
|
||||
def create_preset(data: PresetCreate):
|
||||
"""Create a custom preset."""
|
||||
from mpr.media_assets.models import TranscodePreset
|
||||
|
||||
preset = TranscodePreset.objects.create(
|
||||
name=data.name,
|
||||
description=data.description or "",
|
||||
container=data.container or "mp4",
|
||||
video_codec=data.video_codec or "libx264",
|
||||
video_bitrate=data.video_bitrate,
|
||||
video_crf=data.video_crf,
|
||||
video_preset=data.video_preset,
|
||||
resolution=data.resolution,
|
||||
framerate=data.framerate,
|
||||
audio_codec=data.audio_codec or "aac",
|
||||
audio_bitrate=data.audio_bitrate,
|
||||
audio_channels=data.audio_channels,
|
||||
audio_samplerate=data.audio_samplerate,
|
||||
extra_args=data.extra_args or [],
|
||||
is_builtin=False,
|
||||
)
|
||||
|
||||
return preset
|
||||
|
||||
|
||||
@router.get("/", response_model=list[PresetResponse])
|
||||
def list_presets(include_builtin: bool = True):
|
||||
"""List all presets."""
|
||||
from mpr.media_assets.models import TranscodePreset
|
||||
|
||||
qs = TranscodePreset.objects.all()
|
||||
|
||||
if not include_builtin:
|
||||
qs = qs.filter(is_builtin=False)
|
||||
|
||||
return list(qs)
|
||||
|
||||
|
||||
@router.get("/{preset_id}", response_model=PresetResponse)
|
||||
def get_preset_detail(preset_id: UUID, preset=Depends(get_preset)):
|
||||
"""Get preset details."""
|
||||
return preset
|
||||
|
||||
|
||||
@router.patch("/{preset_id}", response_model=PresetResponse)
|
||||
def update_preset(preset_id: UUID, data: PresetUpdate, preset=Depends(get_preset)):
|
||||
"""Update a custom preset. Builtin presets cannot be modified."""
|
||||
if preset.is_builtin:
|
||||
raise HTTPException(status_code=403, detail="Cannot modify builtin preset")
|
||||
|
||||
update_fields = []
|
||||
for field in [
|
||||
"name",
|
||||
"description",
|
||||
"container",
|
||||
"video_codec",
|
||||
"video_bitrate",
|
||||
"video_crf",
|
||||
"video_preset",
|
||||
"resolution",
|
||||
"framerate",
|
||||
"audio_codec",
|
||||
"audio_bitrate",
|
||||
"audio_channels",
|
||||
"audio_samplerate",
|
||||
"extra_args",
|
||||
]:
|
||||
value = getattr(data, field, None)
|
||||
if value is not None:
|
||||
setattr(preset, field, value)
|
||||
update_fields.append(field)
|
||||
|
||||
if update_fields:
|
||||
preset.save(update_fields=update_fields)
|
||||
|
||||
return preset
|
||||
|
||||
|
||||
@router.delete("/{preset_id}", status_code=204)
|
||||
def delete_preset(preset_id: UUID, preset=Depends(get_preset)):
|
||||
"""Delete a custom preset. Builtin presets cannot be deleted."""
|
||||
if preset.is_builtin:
|
||||
raise HTTPException(status_code=403, detail="Cannot delete builtin preset")
|
||||
|
||||
preset.delete()
|
||||
30
api/routes/system.py
Normal file
30
api/routes/system.py
Normal file
@@ -0,0 +1,30 @@
|
||||
"""
|
||||
System endpoints - health checks and FFmpeg capabilities.
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter
|
||||
|
||||
from core.ffmpeg import get_decoders, get_encoders, get_formats
|
||||
|
||||
router = APIRouter(prefix="/system", tags=["system"])
|
||||
|
||||
|
||||
@router.get("/health")
|
||||
def health_check():
|
||||
"""Health check endpoint."""
|
||||
return {"status": "healthy"}
|
||||
|
||||
|
||||
@router.get("/ffmpeg/codecs")
|
||||
def ffmpeg_codecs():
|
||||
"""Get available FFmpeg encoders and decoders."""
|
||||
return {
|
||||
"encoders": get_encoders(),
|
||||
"decoders": get_decoders(),
|
||||
}
|
||||
|
||||
|
||||
@router.get("/ffmpeg/formats")
|
||||
def ffmpeg_formats():
|
||||
"""Get available FFmpeg muxers and demuxers."""
|
||||
return get_formats()
|
||||
10
api/schemas/__init__.py
Normal file
10
api/schemas/__init__.py
Normal file
@@ -0,0 +1,10 @@
|
||||
"""API Schemas - GENERATED FILE"""
|
||||
|
||||
from .base import BaseSchema
|
||||
from .asset import AssetCreate, AssetUpdate, AssetResponse
|
||||
from .asset import AssetStatus
|
||||
from .preset import PresetCreate, PresetUpdate, PresetResponse
|
||||
from .job import JobCreate, JobUpdate, JobResponse
|
||||
from .job import JobStatus
|
||||
|
||||
__all__ = ["BaseSchema", "AssetCreate", "AssetUpdate", "AssetResponse", "AssetStatus", "PresetCreate", "PresetUpdate", "PresetResponse", "JobCreate", "JobUpdate", "JobResponse", "JobStatus"]
|
||||
70
api/schemas/asset.py
Normal file
70
api/schemas/asset.py
Normal file
@@ -0,0 +1,70 @@
|
||||
"""MediaAsset Schemas - GENERATED FILE"""
|
||||
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from typing import Any, Dict, List, Optional
|
||||
from uuid import UUID
|
||||
|
||||
from .base import BaseSchema
|
||||
|
||||
|
||||
class AssetStatus(str, Enum):
|
||||
PENDING = "pending"
|
||||
READY = "ready"
|
||||
ERROR = "error"
|
||||
|
||||
|
||||
class AssetCreate(BaseSchema):
|
||||
"""AssetCreate schema."""
|
||||
filename: str
|
||||
file_path: str
|
||||
file_size: Optional[int] = None
|
||||
duration: Optional[float] = None
|
||||
video_codec: Optional[str] = None
|
||||
audio_codec: Optional[str] = None
|
||||
width: Optional[int] = None
|
||||
height: Optional[int] = None
|
||||
framerate: Optional[float] = None
|
||||
bitrate: Optional[int] = None
|
||||
properties: Dict[str, Any]
|
||||
comments: str = ""
|
||||
tags: List[str]
|
||||
|
||||
class AssetUpdate(BaseSchema):
|
||||
"""AssetUpdate schema."""
|
||||
filename: Optional[str] = None
|
||||
file_path: Optional[str] = None
|
||||
status: Optional[AssetStatus] = None
|
||||
error_message: Optional[str] = None
|
||||
file_size: Optional[int] = None
|
||||
duration: Optional[float] = None
|
||||
video_codec: Optional[str] = None
|
||||
audio_codec: Optional[str] = None
|
||||
width: Optional[int] = None
|
||||
height: Optional[int] = None
|
||||
framerate: Optional[float] = None
|
||||
bitrate: Optional[int] = None
|
||||
properties: Optional[Dict[str, Any]] = None
|
||||
comments: Optional[str] = None
|
||||
tags: Optional[List[str]] = None
|
||||
|
||||
class AssetResponse(BaseSchema):
|
||||
"""AssetResponse schema."""
|
||||
id: UUID
|
||||
filename: str
|
||||
file_path: str
|
||||
status: AssetStatus = "AssetStatus.PENDING"
|
||||
error_message: Optional[str] = None
|
||||
file_size: Optional[int] = None
|
||||
duration: Optional[float] = None
|
||||
video_codec: Optional[str] = None
|
||||
audio_codec: Optional[str] = None
|
||||
width: Optional[int] = None
|
||||
height: Optional[int] = None
|
||||
framerate: Optional[float] = None
|
||||
bitrate: Optional[int] = None
|
||||
properties: Dict[str, Any]
|
||||
comments: str = ""
|
||||
tags: List[str]
|
||||
created_at: Optional[datetime] = None
|
||||
updated_at: Optional[datetime] = None
|
||||
8
api/schemas/base.py
Normal file
8
api/schemas/base.py
Normal file
@@ -0,0 +1,8 @@
|
||||
"""Pydantic Base Schema - GENERATED FILE"""
|
||||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
|
||||
class BaseSchema(BaseModel):
|
||||
"""Base schema with ORM mode."""
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
80
api/schemas/job.py
Normal file
80
api/schemas/job.py
Normal file
@@ -0,0 +1,80 @@
|
||||
"""TranscodeJob Schemas - GENERATED FILE"""
|
||||
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from typing import Any, Dict, List, Optional
|
||||
from uuid import UUID
|
||||
|
||||
from .base import BaseSchema
|
||||
|
||||
|
||||
class JobStatus(str, Enum):
|
||||
PENDING = "pending"
|
||||
PROCESSING = "processing"
|
||||
COMPLETED = "completed"
|
||||
FAILED = "failed"
|
||||
CANCELLED = "cancelled"
|
||||
|
||||
|
||||
class JobCreate(BaseSchema):
|
||||
"""JobCreate schema."""
|
||||
source_asset_id: UUID
|
||||
preset_id: Optional[UUID] = None
|
||||
preset_snapshot: Dict[str, Any]
|
||||
trim_start: Optional[float] = None
|
||||
trim_end: Optional[float] = None
|
||||
output_filename: str = ""
|
||||
output_path: Optional[str] = None
|
||||
output_asset_id: Optional[UUID] = None
|
||||
progress: float = 0.0
|
||||
current_frame: Optional[int] = None
|
||||
current_time: Optional[float] = None
|
||||
speed: Optional[str] = None
|
||||
celery_task_id: Optional[str] = None
|
||||
priority: int = 0
|
||||
started_at: Optional[datetime] = None
|
||||
completed_at: Optional[datetime] = None
|
||||
|
||||
class JobUpdate(BaseSchema):
|
||||
"""JobUpdate schema."""
|
||||
source_asset_id: Optional[UUID] = None
|
||||
preset_id: Optional[UUID] = None
|
||||
preset_snapshot: Optional[Dict[str, Any]] = None
|
||||
trim_start: Optional[float] = None
|
||||
trim_end: Optional[float] = None
|
||||
output_filename: Optional[str] = None
|
||||
output_path: Optional[str] = None
|
||||
output_asset_id: Optional[UUID] = None
|
||||
status: Optional[JobStatus] = None
|
||||
progress: Optional[float] = None
|
||||
current_frame: Optional[int] = None
|
||||
current_time: Optional[float] = None
|
||||
speed: Optional[str] = None
|
||||
error_message: Optional[str] = None
|
||||
celery_task_id: Optional[str] = None
|
||||
priority: Optional[int] = None
|
||||
started_at: Optional[datetime] = None
|
||||
completed_at: Optional[datetime] = None
|
||||
|
||||
class JobResponse(BaseSchema):
|
||||
"""JobResponse schema."""
|
||||
id: UUID
|
||||
source_asset_id: UUID
|
||||
preset_id: Optional[UUID] = None
|
||||
preset_snapshot: Dict[str, Any]
|
||||
trim_start: Optional[float] = None
|
||||
trim_end: Optional[float] = None
|
||||
output_filename: str = ""
|
||||
output_path: Optional[str] = None
|
||||
output_asset_id: Optional[UUID] = None
|
||||
status: JobStatus = "JobStatus.PENDING"
|
||||
progress: float = 0.0
|
||||
current_frame: Optional[int] = None
|
||||
current_time: Optional[float] = None
|
||||
speed: Optional[str] = None
|
||||
error_message: Optional[str] = None
|
||||
celery_task_id: Optional[str] = None
|
||||
priority: int = 0
|
||||
created_at: Optional[datetime] = None
|
||||
started_at: Optional[datetime] = None
|
||||
completed_at: Optional[datetime] = None
|
||||
66
api/schemas/preset.py
Normal file
66
api/schemas/preset.py
Normal file
@@ -0,0 +1,66 @@
|
||||
"""TranscodePreset Schemas - GENERATED FILE"""
|
||||
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from typing import Any, Dict, List, Optional
|
||||
from uuid import UUID
|
||||
|
||||
from .base import BaseSchema
|
||||
|
||||
|
||||
class PresetCreate(BaseSchema):
|
||||
"""PresetCreate schema."""
|
||||
name: str
|
||||
description: str = ""
|
||||
is_builtin: bool = False
|
||||
container: str = "mp4"
|
||||
video_codec: str = "libx264"
|
||||
video_bitrate: Optional[str] = None
|
||||
video_crf: Optional[int] = None
|
||||
video_preset: Optional[str] = None
|
||||
resolution: Optional[str] = None
|
||||
framerate: Optional[float] = None
|
||||
audio_codec: str = "aac"
|
||||
audio_bitrate: Optional[str] = None
|
||||
audio_channels: Optional[int] = None
|
||||
audio_samplerate: Optional[int] = None
|
||||
extra_args: List[str]
|
||||
|
||||
class PresetUpdate(BaseSchema):
|
||||
"""PresetUpdate schema."""
|
||||
name: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
is_builtin: Optional[bool] = None
|
||||
container: Optional[str] = None
|
||||
video_codec: Optional[str] = None
|
||||
video_bitrate: Optional[str] = None
|
||||
video_crf: Optional[int] = None
|
||||
video_preset: Optional[str] = None
|
||||
resolution: Optional[str] = None
|
||||
framerate: Optional[float] = None
|
||||
audio_codec: Optional[str] = None
|
||||
audio_bitrate: Optional[str] = None
|
||||
audio_channels: Optional[int] = None
|
||||
audio_samplerate: Optional[int] = None
|
||||
extra_args: Optional[List[str]] = None
|
||||
|
||||
class PresetResponse(BaseSchema):
|
||||
"""PresetResponse schema."""
|
||||
id: UUID
|
||||
name: str
|
||||
description: str = ""
|
||||
is_builtin: bool = False
|
||||
container: str = "mp4"
|
||||
video_codec: str = "libx264"
|
||||
video_bitrate: Optional[str] = None
|
||||
video_crf: Optional[int] = None
|
||||
video_preset: Optional[str] = None
|
||||
resolution: Optional[str] = None
|
||||
framerate: Optional[float] = None
|
||||
audio_codec: str = "aac"
|
||||
audio_bitrate: Optional[str] = None
|
||||
audio_channels: Optional[int] = None
|
||||
audio_samplerate: Optional[int] = None
|
||||
extra_args: List[str]
|
||||
created_at: Optional[datetime] = None
|
||||
updated_at: Optional[datetime] = None
|
||||
Reference in New Issue
Block a user