118 lines
3.9 KiB
Rust
118 lines
3.9 KiB
Rust
use axum::Json;
|
|
use axum::extract::{Path, State};
|
|
use axum::http::{StatusCode, header};
|
|
use axum::response::IntoResponse;
|
|
use chrono::Utc;
|
|
|
|
use api_types::{
|
|
CurrentBroadcastResponse, ScheduleHistoryEntry, ScheduleResponse, SlotResponse,
|
|
};
|
|
use application::schedule::{
|
|
GenerateScheduleCommand, GetCurrentBroadcastQuery, GetEpgQuery, GetSourceQuery,
|
|
};
|
|
use domain::DomainError;
|
|
use domain::value_objects::ChannelId;
|
|
|
|
use crate::errors::AppError;
|
|
use crate::extractors::CurrentUser;
|
|
use crate::state::AppState;
|
|
|
|
pub async fn generate_schedule(
|
|
State(state): State<AppState>,
|
|
CurrentUser(_user): CurrentUser,
|
|
Path(id): Path<uuid::Uuid>,
|
|
) -> Result<Json<ScheduleResponse>, AppError> {
|
|
let cmd = GenerateScheduleCommand { channel_id: id };
|
|
let schedule = application::schedule::generate::execute(&state.schedule_deps, cmd).await?;
|
|
Ok(Json(ScheduleResponse::from(schedule)))
|
|
}
|
|
|
|
pub async fn get_active_schedule(
|
|
State(state): State<AppState>,
|
|
CurrentUser(_user): CurrentUser,
|
|
Path(id): Path<uuid::Uuid>,
|
|
) -> Result<axum::response::Response, AppError> {
|
|
let channel_id = ChannelId::from(id);
|
|
match state.schedule_query.find_active(channel_id, Utc::now()).await? {
|
|
Some(schedule) => Ok(Json(ScheduleResponse::from(schedule)).into_response()),
|
|
None => Ok(StatusCode::NO_CONTENT.into_response()),
|
|
}
|
|
}
|
|
|
|
pub async fn get_current_broadcast(
|
|
State(state): State<AppState>,
|
|
Path(id): Path<uuid::Uuid>,
|
|
) -> Result<axum::response::Response, AppError> {
|
|
let query = GetCurrentBroadcastQuery { channel_id: id };
|
|
match application::schedule::get_current_broadcast::execute(&state.schedule_deps, query).await?
|
|
{
|
|
Some(result) => {
|
|
let slot_response = SlotResponse::from(result.broadcast.slot().clone());
|
|
Ok(Json(CurrentBroadcastResponse {
|
|
slot: slot_response,
|
|
offset_secs: result.broadcast.offset_secs(),
|
|
})
|
|
.into_response())
|
|
}
|
|
None => Ok(StatusCode::NO_CONTENT.into_response()),
|
|
}
|
|
}
|
|
|
|
pub async fn get_epg(
|
|
State(state): State<AppState>,
|
|
Path(id): Path<uuid::Uuid>,
|
|
) -> Result<Json<Vec<SlotResponse>>, AppError> {
|
|
let query = GetEpgQuery { channel_id: id };
|
|
let slots = application::schedule::get_epg::execute(&state.schedule_deps, query).await?;
|
|
Ok(Json(slots.into_iter().map(SlotResponse::from).collect()))
|
|
}
|
|
|
|
pub async fn get_stream(
|
|
State(state): State<AppState>,
|
|
Path(id): Path<uuid::Uuid>,
|
|
) -> Result<axum::response::Response, AppError> {
|
|
let query = GetSourceQuery { channel_id: id };
|
|
match application::schedule::get_source::execute(&state.schedule_deps, query).await? {
|
|
Some(uri) => Ok(Json(uri).into_response()),
|
|
None => Ok(StatusCode::NO_CONTENT.into_response()),
|
|
}
|
|
}
|
|
|
|
pub async fn list_schedule_history(
|
|
State(state): State<AppState>,
|
|
CurrentUser(_user): CurrentUser,
|
|
Path(id): Path<uuid::Uuid>,
|
|
) -> Result<Json<Vec<ScheduleHistoryEntry>>, AppError> {
|
|
let channel_id = ChannelId::from(id);
|
|
let history = state.schedule_query.list_schedule_history(channel_id).await?;
|
|
Ok(Json(
|
|
history
|
|
.into_iter()
|
|
.map(ScheduleHistoryEntry::from)
|
|
.collect(),
|
|
))
|
|
}
|
|
|
|
pub async fn export_ical(
|
|
State(state): State<AppState>,
|
|
Path(id): Path<uuid::Uuid>,
|
|
) -> Result<axum::response::Response, AppError> {
|
|
let channel = state
|
|
.channel_query
|
|
.find_by_id(id.into())
|
|
.await?
|
|
.ok_or_else(|| AppError(DomainError::NotFound(format!("Channel {id} not found"))))?;
|
|
|
|
let ical = domain::generate_ical(channel.name(), channel.timezone(), channel.schedule_config());
|
|
let disposition = format!("attachment; filename=\"{}.ics\"", channel.name());
|
|
|
|
Ok((
|
|
[
|
|
(header::CONTENT_TYPE, "text/calendar; charset=utf-8".to_string()),
|
|
(header::CONTENT_DISPOSITION, disposition),
|
|
],
|
|
ical,
|
|
)
|
|
.into_response())
|
|
}
|