feat(api): season search, deck and grab endpoints

This commit is contained in:
Miguel Palhas
2026-08-23 16:49:42 +01:00
parent 42a0895975
commit 75bf6fb14e
3 changed files with 295 additions and 2 deletions
+32
View File
@@ -72,6 +72,8 @@ pub struct AppState {
pending_movie_commands: Arc<tokio::sync::Mutex<mpsc::Receiver<MovieCommand>>>,
episode_commands: mpsc::Sender<EpisodeCommand>,
pending_episode_commands: Arc<tokio::sync::Mutex<mpsc::Receiver<EpisodeCommand>>>,
season_commands: mpsc::Sender<SeasonCommand>,
pending_season_commands: Arc<tokio::sync::Mutex<mpsc::Receiver<SeasonCommand>>>,
}
/// Work explicitly requested through the movie API.
@@ -91,6 +93,17 @@ pub enum EpisodeCommand {
Grab { episode_id: i64, release_id: i64 },
}
/// Work explicitly requested through the season deck API (issue #125).
///
/// A season pack is one torrent for the whole season and the grab targets
/// `target_kind = 'season'`, so the manual action names a season — unlike
/// [`EpisodeCommand`], where intent lives at the leaf.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SeasonCommand {
Search { season_id: i64 },
Grab { season_id: i64, release_id: i64 },
}
impl AppState {
/// Build the state, including the shared HTTP client.
///
@@ -101,6 +114,7 @@ impl AppState {
let http = reqwest::Client::builder().timeout(PROBE_TIMEOUT).build()?;
let (movie_commands, pending_movie_commands) = mpsc::channel(64);
let (episode_commands, pending_episode_commands) = mpsc::channel(64);
let (season_commands, pending_season_commands) = mpsc::channel(64);
Ok(Self {
http,
upstreams: Arc::new(upstreams),
@@ -109,6 +123,8 @@ impl AppState {
pending_movie_commands: Arc::new(tokio::sync::Mutex::new(pending_movie_commands)),
episode_commands,
pending_episode_commands: Arc::new(tokio::sync::Mutex::new(pending_episode_commands)),
season_commands,
pending_season_commands: Arc::new(tokio::sync::Mutex::new(pending_season_commands)),
})
}
@@ -137,6 +153,15 @@ impl AppState {
self.pending_episode_commands.lock().await.recv().await
}
/// Wait for the next manual season action in the daemon's reconcile loop.
///
/// # Errors
///
/// If every sender has been dropped.
pub async fn next_season_command(&self) -> Option<SeasonCommand> {
self.pending_season_commands.lock().await.recv().await
}
pub(crate) fn http(&self) -> &reqwest::Client {
&self.http
}
@@ -162,4 +187,11 @@ impl AppState {
) -> Result<(), mpsc::error::TrySendError<EpisodeCommand>> {
self.episode_commands.try_send(command)
}
pub(crate) fn send_season_command(
&self,
command: SeasonCommand,
) -> Result<(), mpsc::error::TrySendError<SeasonCommand>> {
self.season_commands.try_send(command)
}
}