executor abstraction, graphene to strawberry

This commit is contained in:
2026-03-12 23:27:34 -03:00
parent 4e9d731cff
commit eaaf2ad60c
13 changed files with 796 additions and 276 deletions

View File

@@ -1,5 +1,5 @@
"""
GraphQL API using graphene, mounted on FastAPI/Starlette.
GraphQL API using strawberry, served via FastAPI.
Primary API for MPR — all client interactions go through GraphQL.
Uses Django ORM directly for data access.
@@ -7,8 +7,11 @@ Types are generated from schema/ via modelgen — see api/schema/graphql.py.
"""
import os
from typing import List, Optional
from uuid import UUID
import graphene
import strawberry
from strawberry.types import Info
from api.schema.graphql import (
CreateJobInput,
@@ -22,7 +25,6 @@ from api.schema.graphql import (
)
from core.storage import BUCKET_IN, list_objects
# Media extensions (same as assets route)
VIDEO_EXTS = {".mp4", ".mkv", ".avi", ".mov", ".webm", ".flv", ".wmv", ".m4v"}
AUDIO_EXTS = {".mp3", ".wav", ".flac", ".aac", ".ogg", ".m4a"}
MEDIA_EXTS = VIDEO_EXTS | AUDIO_EXTS
@@ -33,23 +35,15 @@ MEDIA_EXTS = VIDEO_EXTS | AUDIO_EXTS
# ---------------------------------------------------------------------------
class Query(graphene.ObjectType):
assets = graphene.List(
MediaAssetType,
status=graphene.String(),
search=graphene.String(),
)
asset = graphene.Field(MediaAssetType, id=graphene.UUID(required=True))
jobs = graphene.List(
TranscodeJobType,
status=graphene.String(),
source_asset_id=graphene.UUID(),
)
job = graphene.Field(TranscodeJobType, id=graphene.UUID(required=True))
presets = graphene.List(TranscodePresetType)
system_status = graphene.Field(SystemStatusType)
def resolve_assets(self, info, status=None, search=None):
@strawberry.type
class Query:
@strawberry.field
def assets(
self,
info: Info,
status: Optional[str] = None,
search: Optional[str] = None,
) -> List[MediaAssetType]:
from mpr.media_assets.models import MediaAsset
qs = MediaAsset.objects.all()
@@ -57,9 +51,10 @@ class Query(graphene.ObjectType):
qs = qs.filter(status=status)
if search:
qs = qs.filter(filename__icontains=search)
return qs
return list(qs)
def resolve_asset(self, info, id):
@strawberry.field
def asset(self, info: Info, id: UUID) -> Optional[MediaAssetType]:
from mpr.media_assets.models import MediaAsset
try:
@@ -67,7 +62,13 @@ class Query(graphene.ObjectType):
except MediaAsset.DoesNotExist:
return None
def resolve_jobs(self, info, status=None, source_asset_id=None):
@strawberry.field
def jobs(
self,
info: Info,
status: Optional[str] = None,
source_asset_id: Optional[UUID] = None,
) -> List[TranscodeJobType]:
from mpr.media_assets.models import TranscodeJob
qs = TranscodeJob.objects.all()
@@ -75,9 +76,10 @@ class Query(graphene.ObjectType):
qs = qs.filter(status=status)
if source_asset_id:
qs = qs.filter(source_asset_id=source_asset_id)
return qs
return list(qs)
def resolve_job(self, info, id):
@strawberry.field
def job(self, info: Info, id: UUID) -> Optional[TranscodeJobType]:
from mpr.media_assets.models import TranscodeJob
try:
@@ -85,13 +87,15 @@ class Query(graphene.ObjectType):
except TranscodeJob.DoesNotExist:
return None
def resolve_presets(self, info):
@strawberry.field
def presets(self, info: Info) -> List[TranscodePresetType]:
from mpr.media_assets.models import TranscodePreset
return TranscodePreset.objects.all()
return list(TranscodePreset.objects.all())
def resolve_system_status(self, info):
return {"status": "ok", "version": "0.1.0"}
@strawberry.field
def system_status(self, info: Info) -> SystemStatusType:
return SystemStatusType(status="ok", version="0.1.0")
# ---------------------------------------------------------------------------
@@ -99,13 +103,10 @@ class Query(graphene.ObjectType):
# ---------------------------------------------------------------------------
class ScanMediaFolder(graphene.Mutation):
class Arguments:
pass
Output = ScanResultType
def mutate(self, info):
@strawberry.type
class Mutation:
@strawberry.mutation
def scan_media_folder(self, info: Info) -> ScanResultType:
from mpr.media_assets.models import MediaAsset
objects = list_objects(BUCKET_IN, extensions=MEDIA_EXTS)
@@ -135,14 +136,8 @@ class ScanMediaFolder(graphene.Mutation):
files=registered,
)
class CreateJob(graphene.Mutation):
class Arguments:
input = CreateJobInput(required=True)
Output = TranscodeJobType
def mutate(self, info, input):
@strawberry.mutation
def create_job(self, info: Info, input: CreateJobInput) -> TranscodeJobType:
from pathlib import Path
from mpr.media_assets.models import MediaAsset, TranscodeJob, TranscodePreset
@@ -186,9 +181,8 @@ class CreateJob(graphene.Mutation):
priority=input.priority or 0,
)
# Dispatch
executor_mode = os.environ.get("MPR_EXECUTOR", "local")
if executor_mode == "lambda":
if executor_mode in ("lambda", "gcp"):
from task.executor import get_executor
get_executor().run(
@@ -217,14 +211,8 @@ class CreateJob(graphene.Mutation):
return job
class CancelJob(graphene.Mutation):
class Arguments:
id = graphene.UUID(required=True)
Output = TranscodeJobType
def mutate(self, info, id):
@strawberry.mutation
def cancel_job(self, info: Info, id: UUID) -> TranscodeJobType:
from mpr.media_assets.models import TranscodeJob
try:
@@ -239,14 +227,8 @@ class CancelJob(graphene.Mutation):
job.save(update_fields=["status"])
return job
class RetryJob(graphene.Mutation):
class Arguments:
id = graphene.UUID(required=True)
Output = TranscodeJobType
def mutate(self, info, id):
@strawberry.mutation
def retry_job(self, info: Info, id: UUID) -> TranscodeJobType:
from mpr.media_assets.models import TranscodeJob
try:
@@ -263,15 +245,8 @@ class RetryJob(graphene.Mutation):
job.save(update_fields=["status", "progress", "error_message"])
return job
class UpdateAsset(graphene.Mutation):
class Arguments:
id = graphene.UUID(required=True)
input = UpdateAssetInput(required=True)
Output = MediaAssetType
def mutate(self, info, id, input):
@strawberry.mutation
def update_asset(self, info: Info, id: UUID, input: UpdateAssetInput) -> MediaAssetType:
from mpr.media_assets.models import MediaAsset
try:
@@ -292,14 +267,8 @@ class UpdateAsset(graphene.Mutation):
return asset
class DeleteAsset(graphene.Mutation):
class Arguments:
id = graphene.UUID(required=True)
Output = DeleteResultType
def mutate(self, info, id):
@strawberry.mutation
def delete_asset(self, info: Info, id: UUID) -> DeleteResultType:
from mpr.media_assets.models import MediaAsset
try:
@@ -310,17 +279,8 @@ class DeleteAsset(graphene.Mutation):
raise Exception("Asset not found")
class Mutation(graphene.ObjectType):
scan_media_folder = ScanMediaFolder.Field()
create_job = CreateJob.Field()
cancel_job = CancelJob.Field()
retry_job = RetryJob.Field()
update_asset = UpdateAsset.Field()
delete_asset = DeleteAsset.Field()
# ---------------------------------------------------------------------------
# Schema
# ---------------------------------------------------------------------------
schema = graphene.Schema(query=Query, mutation=Mutation)
schema = strawberry.Schema(query=Query, mutation=Mutation)