use crate::prelude::*;
#[injectable]
pub(crate) struct CrossCommand {
cross: Ref<Option<CrossServices>>,
cross_config_options: Ref<CrossConfigOptions>,
cross_options: Ref<CrossOptions>,
injector: Ref<TorrentInjector>,
qbit_cross_options: Ref<QbitCrossOptions>,
qbit_options: Ref<QbitOptions>,
source_provider: Ref<SourceProvider>,
torrent_files: Ref<TorrentFileProvider>,
}
impl CrossCommand {
pub(crate) async fn execute_cli(&self) -> Result<bool, Failure<CrossCommandAction>> {
self.validate()?;
let source = match self
.source_provider
.get_from_options_without_content()
.await
{
Ok(Ok(source)) => source,
Ok(Err(issue)) => {
warn!("{} for cross seeding unknown", "Unsuitable".bold());
warn!("{issue}");
return Ok(false);
}
Err(e) => return Err(Failure::new(CrossCommandAction::GetSource, e)),
};
let main_torrent = self
.torrent_files
.get(source.torrent.id)
.await
.map_err(Failure::wrap(CrossCommandAction::DownloadSourceTorrent))?;
let cross = self
.cross
.as_ref()
.as_ref()
.expect("cross config validated above so CrossServices must be Some");
let cross_id = cross
.checker
.execute(&main_torrent, &source)
.await
.map_err(Failure::wrap(CrossCommandAction::CheckCrossIndexer))?;
let Some(cross_id) = cross_id else {
info!("{} cross seed for {source}", "No".bold());
return Ok(false);
};
if self.cross_options.dry_run {
info!(
"{} inject cross seed {cross_id} for {source}",
"Would".bold()
);
return Ok(true);
}
let cross_path = cross
.torrent_files
.get(cross_id)
.await
.map_err(Failure::wrap(CrossCommandAction::DownloadCrossTorrent))?;
if let Some(target_dir) = &self.cross_options.copy_cross_torrent_to {
self.injector
.copy_torrent(&cross_path, target_dir)
.await
.map_err(Failure::wrap(CrossCommandAction::CopyTorrent))?;
}
if self.qbit_cross_options.qbit_cross {
let add_options = self.qbit_cross_options.to_add_torrent_options();
self.injector
.inject_qbit(&cross_path, add_options)
.await
.map_err(Failure::wrap(CrossCommandAction::InjectTorrent))?;
info!("{} cross seed {cross_id} for {source}", "Injected".bold());
} else {
info!("{} cross seed {cross_id} for {source}", "Found".bold());
}
Ok(true)
}
fn validate(&self) -> Result<(), Failure<CrossCommandAction>> {
let mut validator = OptionsValidator::new();
validator.check_set("cross_config", &self.cross_config_options.cross_config);
if self.qbit_cross_options.qbit_cross {
self.qbit_options.validate_connection(&mut validator);
}
if !self.cross_options.dry_run
&& !self.qbit_cross_options.qbit_cross
&& self.cross_options.copy_cross_torrent_to.is_none()
{
validator.push(OptionIssue::required_one_of(&[
"qbit_cross",
"copy_cross_torrent_to",
"dry_run",
]));
}
validator.check_or(CrossCommandAction::ValidateOptions)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, ThisError)]
pub(crate) enum CrossCommandAction {
#[error("validate options")]
ValidateOptions,
#[error("get source")]
GetSource,
#[error("download source torrent file")]
DownloadSourceTorrent,
#[error("check cross indexer")]
CheckCrossIndexer,
#[error("download cross torrent file")]
DownloadCrossTorrent,
#[error("inject torrent")]
InjectTorrent,
#[error("copy torrent file")]
CopyTorrent,
}