Files
arr/crates/arr-compat/src/system.rs
T
naps62-yolo 38bd4102bb
ci / web (push) Successful in 55s
ci / rust (push) Successful in 1m36s
e2e / e2e (push) Successful in 1m24s
Sonarr v3 shim for Jellyseerr (#84)
2026-08-22 23:33:09 +01:00

85 lines
2.6 KiB
Rust

//! The four endpoints Jellyseerr calls before it will save a server: status,
//! root folders, quality profiles and tags.
use axum::extract::State;
use axum::Json;
use crate::error::CompatError;
use crate::model::{QualityProfile, RootFolder, SystemStatus, Tag};
use crate::CompatState;
/// `GET /api/v3/system/status` — Jellyseerr's "Test" button.
pub(crate) async fn status() -> Json<SystemStatus> {
Json(SystemStatus::default())
}
/// `GET /api/v3/rootfolder` — the real roots (§5.1).
pub(crate) async fn root_folders(
State(state): State<CompatState>,
) -> Result<Json<Vec<RootFolder>>, CompatError> {
let roots = state.database().list_roots().await?;
Ok(Json(
roots
.into_iter()
.map(|root| RootFolder {
id: root.id,
path: root.path,
accessible: true,
free_space: 0,
unmapped_folders: Vec::new(),
})
.collect(),
))
}
/// `GET /api/v3/qualityprofile` — one fake profile per root (§5.1).
pub(crate) async fn quality_profiles(
State(state): State<CompatState>,
) -> Result<Json<Vec<QualityProfile>>, CompatError> {
let roots = state.database().list_roots().await?;
Ok(Json(
roots
.into_iter()
.map(|root| QualityProfile {
id: root.id,
name: root.policy_name,
upgrade_allowed: false,
cutoff: root.id,
items: Vec::new(),
min_format_score: 0,
cutoff_format_score: 0,
format_items: Vec::new(),
})
.collect(),
))
}
/// Sonarr v3 still exposes language profiles. The real language rules are
/// attached to roots, so these are the same harmless root-backed fictions as
/// quality profiles.
pub(crate) async fn language_profiles(
State(state): State<CompatState>,
) -> Result<Json<Vec<QualityProfile>>, CompatError> {
quality_profiles(State(state)).await
}
/// `GET /api/v3/tag` — the owners from §4.3, which is what Radarr's tags map
/// onto here. Jellyseerr can then attach one to a request and the notification
/// routing follows.
pub(crate) async fn tags(State(state): State<CompatState>) -> Result<Json<Vec<Tag>>, CompatError> {
let owners = sqlx::query!(
r#"SELECT id AS "id!: i64", name AS "label!: String" FROM owners ORDER BY name"#
)
.fetch_all(state.pool())
.await?;
Ok(Json(
owners
.into_iter()
.map(|owner| Tag {
id: owner.id,
label: owner.label,
})
.collect(),
))
}