feat: Jellyfin library refresh on import (#93)
This commit was merged in pull request #93.
This commit is contained in:
@@ -25,6 +25,7 @@ use arr_db::Db;
|
||||
use arr_dl::TransmissionClient;
|
||||
use arr_probe::Prober;
|
||||
|
||||
use crate::jellyfin::JellyfinClient;
|
||||
use crate::reconcile::{Action, ActionFuture, Outcome};
|
||||
|
||||
/// A failure during one import tick.
|
||||
@@ -73,6 +74,7 @@ enum ProbeOutcome {
|
||||
pub struct ImportAction {
|
||||
transmission: TransmissionClient,
|
||||
prober: Prober,
|
||||
jellyfin: JellyfinClient,
|
||||
/// Probe results by path, kept across ticks. The reconcile lane cancels
|
||||
/// the whole action after its 25 s budget while one probe alone may take
|
||||
/// up to 60 s, so without this a large multi-file torrent would restart
|
||||
@@ -99,10 +101,11 @@ struct PendingImport {
|
||||
|
||||
impl ImportAction {
|
||||
#[must_use]
|
||||
pub fn new(transmission: TransmissionClient, prober: Prober) -> Self {
|
||||
pub fn new(transmission: TransmissionClient, prober: Prober, jellyfin: JellyfinClient) -> Self {
|
||||
Self {
|
||||
transmission,
|
||||
prober,
|
||||
jellyfin,
|
||||
probed: std::sync::Arc::new(tokio::sync::Mutex::new(HashMap::new())),
|
||||
}
|
||||
}
|
||||
@@ -288,6 +291,7 @@ impl ImportAction {
|
||||
|
||||
record_import(database, pending, &feature, waiver.as_ref(), &destination).await?;
|
||||
self.forget_probes(&paths).await;
|
||||
self.refresh_jellyfin().await;
|
||||
let path_text = destination.to_string_lossy().into_owned();
|
||||
tracing::info!(
|
||||
grab_id = pending.grab_id,
|
||||
@@ -304,6 +308,15 @@ impl ImportAction {
|
||||
)))
|
||||
}
|
||||
|
||||
/// §7.5: the filesystem watcher misses the just-hardlinked file. A
|
||||
/// failure to reach Jellyfin must not fail the import, which has already
|
||||
/// succeeded.
|
||||
async fn refresh_jellyfin(&self) {
|
||||
if let Err(error) = self.jellyfin.refresh().await {
|
||||
tracing::warn!(%error, "jellyfin refresh failed");
|
||||
}
|
||||
}
|
||||
|
||||
/// §5.7 hard fail: blacklist the release, fail the grab, reopen the gap.
|
||||
/// The torrent is deliberately untouched (§7.3).
|
||||
async fn hard_fail(
|
||||
@@ -545,7 +558,7 @@ mod tests {
|
||||
use std::path::PathBuf;
|
||||
|
||||
use serde_json::json;
|
||||
use wiremock::matchers::method;
|
||||
use wiremock::matchers::{method, path};
|
||||
use wiremock::{Mock, MockServer, ResponseTemplate};
|
||||
|
||||
use super::*;
|
||||
@@ -583,6 +596,7 @@ mod tests {
|
||||
library: PathBuf,
|
||||
action: ImportAction,
|
||||
_server: MockServer,
|
||||
jellyfin_server: MockServer,
|
||||
}
|
||||
|
||||
/// An `ffprobe` stand-in: canned JSON for media, a `tty` document for the
|
||||
@@ -667,8 +681,20 @@ mod tests {
|
||||
.mount(&server)
|
||||
.await;
|
||||
|
||||
let jellyfin_server = MockServer::start().await;
|
||||
Mock::given(method("POST"))
|
||||
.and(path("/Library/Refresh"))
|
||||
.respond_with(ResponseTemplate::new(204))
|
||||
.mount(&jellyfin_server)
|
||||
.await;
|
||||
|
||||
let prober = Prober::new().with_binary(fake_ffprobe(dir.path(), media_json));
|
||||
let action = ImportAction::new(TransmissionClient::new(&server.uri()).unwrap(), prober);
|
||||
let jellyfin = JellyfinClient::new(jellyfin_server.uri(), None).unwrap();
|
||||
let action = ImportAction::new(
|
||||
TransmissionClient::new(&server.uri()).unwrap(),
|
||||
prober,
|
||||
jellyfin,
|
||||
);
|
||||
|
||||
Harness {
|
||||
_dir: dir,
|
||||
@@ -677,6 +703,7 @@ mod tests {
|
||||
library,
|
||||
action,
|
||||
_server: server,
|
||||
jellyfin_server,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -725,6 +752,12 @@ mod tests {
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(movie_state, "available");
|
||||
|
||||
assert_eq!(
|
||||
h.jellyfin_server.received_requests().await.unwrap().len(),
|
||||
1,
|
||||
"§7.5: one refresh call at the end of a successful import"
|
||||
);
|
||||
}
|
||||
|
||||
/// §5.3 through §5.7: Profile 5 is a hard fail — blacklisted, grab
|
||||
@@ -948,6 +981,7 @@ mod tests {
|
||||
let action = ImportAction::new(
|
||||
TransmissionClient::new("http://127.0.0.1:1").unwrap(),
|
||||
Prober::new().with_binary(&script),
|
||||
JellyfinClient::new("http://127.0.0.1:1", None).unwrap(),
|
||||
);
|
||||
let paths = vec![media];
|
||||
|
||||
@@ -992,6 +1026,7 @@ mod tests {
|
||||
let action = ImportAction::new(
|
||||
TransmissionClient::new("http://127.0.0.1:1").unwrap(),
|
||||
Prober::new().with_binary(&script),
|
||||
JellyfinClient::new("http://127.0.0.1:1", None).unwrap(),
|
||||
);
|
||||
let paths = vec![media];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user