pinakes/crates/pinakes-server/src/routes/transcode.rs
NotAShelf 9d58927cb4
pinakes-server: add utoipa annotations to all routes; fix tests
Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: I28cf5b7b7cff8e90e123d624d97cf9656a6a6964
2026-03-22 22:04:51 +03:00

121 lines
3.5 KiB
Rust

use axum::{
Json,
extract::{Path, Query, State},
};
use pinakes_core::model::MediaId;
use uuid::Uuid;
use crate::{
dto::{CreateTranscodeRequest, PaginationParams, TranscodeSessionResponse},
error::ApiError,
state::AppState,
};
#[utoipa::path(
post,
path = "/api/v1/media/{id}/transcode",
tag = "transcode",
params(("id" = Uuid, Path, description = "Media item ID")),
request_body = CreateTranscodeRequest,
responses(
(status = 200, description = "Transcode job submitted"),
(status = 400, description = "Bad request"),
(status = 401, description = "Unauthorized"),
(status = 500, description = "Internal server error"),
),
security(("bearer_auth" = []))
)]
pub async fn start_transcode(
State(state): State<AppState>,
Path(id): Path<Uuid>,
Json(req): Json<CreateTranscodeRequest>,
) -> Result<Json<serde_json::Value>, ApiError> {
if req.profile.is_empty() || req.profile.len() > 255 {
return Err(ApiError::bad_request("profile must be 1-255 bytes"));
}
let job_id = state
.job_queue
.submit(pinakes_core::jobs::JobKind::Transcode {
media_id: MediaId(id),
profile: req.profile,
})
.await;
Ok(Json(serde_json::json!({"job_id": job_id.to_string()})))
}
#[utoipa::path(
get,
path = "/api/v1/transcode/{id}",
tag = "transcode",
params(("id" = Uuid, Path, description = "Transcode session ID")),
responses(
(status = 200, description = "Transcode session details", body = TranscodeSessionResponse),
(status = 401, description = "Unauthorized"),
(status = 404, description = "Not found"),
),
security(("bearer_auth" = []))
)]
pub async fn get_session(
State(state): State<AppState>,
Path(id): Path<Uuid>,
) -> Result<Json<TranscodeSessionResponse>, ApiError> {
let session = state.storage.get_transcode_session(id).await?;
Ok(Json(TranscodeSessionResponse::from(session)))
}
#[utoipa::path(
get,
path = "/api/v1/transcode",
tag = "transcode",
responses(
(status = 200, description = "List of transcode sessions", body = Vec<TranscodeSessionResponse>),
(status = 401, description = "Unauthorized"),
),
security(("bearer_auth" = []))
)]
pub async fn list_sessions(
State(state): State<AppState>,
Query(params): Query<PaginationParams>,
) -> Result<Json<Vec<TranscodeSessionResponse>>, ApiError> {
let _ = params; // reserved for future filtering
let sessions = state.storage.list_transcode_sessions(None).await?;
Ok(Json(
sessions
.into_iter()
.map(TranscodeSessionResponse::from)
.collect(),
))
}
#[utoipa::path(
delete,
path = "/api/v1/transcode/{id}",
tag = "transcode",
params(("id" = Uuid, Path, description = "Transcode session ID")),
responses(
(status = 200, description = "Transcode session cancelled"),
(status = 401, description = "Unauthorized"),
(status = 404, description = "Not found"),
),
security(("bearer_auth" = []))
)]
pub async fn cancel_session(
State(state): State<AppState>,
Path(id): Path<Uuid>,
) -> Result<Json<serde_json::Value>, ApiError> {
if let Some(transcode_service) = &state.transcode_service {
transcode_service
.cancel_transcode(id, &state.storage)
.await?;
} else {
state
.storage
.update_transcode_status(
id,
pinakes_core::transcode::TranscodeStatus::Cancelled,
0.0,
)
.await?;
}
Ok(Json(serde_json::json!({"cancelled": true})))
}