major refactor

This commit is contained in:
2026-03-13 01:07:02 -03:00
parent eaaf2ad60c
commit 3eeedebb15
61 changed files with 441 additions and 242 deletions

View File

@@ -1,5 +1,5 @@
/**
* API client for FastAPI backend
* GraphQL API client
*/
import type {
@@ -8,34 +8,51 @@ import type {
TranscodeJob,
CreateJobRequest,
SystemStatus,
WorkerStatus,
} from "./types";
const API_BASE = "/api";
const GRAPHQL_URL = "/api/graphql";
async function request<T>(path: string, options?: RequestInit): Promise<T> {
const response = await fetch(`${API_BASE}${path}`, {
headers: {
"Content-Type": "application/json",
},
...options,
async function gql<T>(query: string, variables?: Record<string, unknown>): Promise<T> {
const response = await fetch(GRAPHQL_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ query, variables }),
});
if (!response.ok) {
const error = await response.text();
throw new Error(`API error: ${response.status} - ${error}`);
const json = await response.json();
if (json.errors?.length) {
throw new Error(json.errors[0].message);
}
return response.json();
return json.data as T;
}
// Assets
export async function getAssets(): Promise<MediaAsset[]> {
return request("/assets/");
const data = await gql<{ assets: MediaAsset[] }>(`
query {
assets {
id filename file_path status error_message file_size duration
video_codec audio_codec width height framerate bitrate
properties comments tags created_at updated_at
}
}
`);
return data.assets;
}
export async function getAsset(id: string): Promise<MediaAsset> {
return request(`/assets/${id}`);
const data = await gql<{ asset: MediaAsset }>(`
query($id: UUID!) {
asset(id: $id) {
id filename file_path status error_message file_size duration
video_codec audio_codec width height framerate bitrate
properties comments tags created_at updated_at
}
}
`, { id });
return data.asset;
}
export async function scanMediaFolder(): Promise<{
@@ -44,43 +61,95 @@ export async function scanMediaFolder(): Promise<{
skipped: number;
files: string[];
}> {
return request("/assets/scan", {
method: "POST",
});
const data = await gql<{ scan_media_folder: { found: number; registered: number; skipped: number; files: string[] } }>(`
mutation {
scan_media_folder { found registered skipped files }
}
`);
return data.scan_media_folder;
}
// Presets
export async function getPresets(): Promise<TranscodePreset[]> {
return request("/presets/");
const data = await gql<{ presets: TranscodePreset[] }>(`
query {
presets {
id name description is_builtin container
video_codec video_bitrate video_crf video_preset resolution framerate
audio_codec audio_bitrate audio_channels audio_samplerate
extra_args created_at updated_at
}
}
`);
return data.presets;
}
// Jobs
export async function getJobs(): Promise<TranscodeJob[]> {
return request("/jobs/");
const data = await gql<{ jobs: TranscodeJob[] }>(`
query {
jobs {
id source_asset_id preset_id preset_snapshot trim_start trim_end
output_filename output_path output_asset_id status progress
current_frame current_time speed error_message celery_task_id
execution_arn priority created_at started_at completed_at
}
}
`);
return data.jobs;
}
export async function getJob(id: string): Promise<TranscodeJob> {
return request(`/jobs/${id}`);
const data = await gql<{ job: TranscodeJob }>(`
query($id: UUID!) {
job(id: $id) {
id source_asset_id preset_id preset_snapshot trim_start trim_end
output_filename output_path output_asset_id status progress
current_frame current_time speed error_message celery_task_id
execution_arn priority created_at started_at completed_at
}
}
`, { id });
return data.job;
}
export async function createJob(data: CreateJobRequest): Promise<TranscodeJob> {
return request("/jobs/", {
method: "POST",
body: JSON.stringify(data),
export async function createJob(req: CreateJobRequest): Promise<TranscodeJob> {
const data = await gql<{ create_job: TranscodeJob }>(`
mutation($input: CreateJobInput!) {
create_job(input: $input) {
id source_asset_id status output_filename progress created_at
}
}
`, {
input: {
source_asset_id: req.source_asset_id,
preset_id: req.preset_id,
trim_start: req.trim_start,
trim_end: req.trim_end,
output_filename: req.output_filename ?? null,
priority: req.priority ?? 0,
},
});
return data.create_job;
}
export async function cancelJob(id: string): Promise<TranscodeJob> {
return request(`/jobs/${id}/cancel`, {
method: "POST",
});
const data = await gql<{ cancel_job: TranscodeJob }>(`
mutation($id: UUID!) {
cancel_job(id: $id) {
id status
}
}
`, { id });
return data.cancel_job;
}
// System
export async function getSystemStatus(): Promise<SystemStatus> {
return request("/system/status");
}
export async function getWorkerStatus(): Promise<WorkerStatus> {
return request("/system/worker");
const data = await gql<{ system_status: SystemStatus }>(`
query {
system_status { status version }
}
`);
return data.system_status;
}