use crate::prelude::*;
use dirs::{cache_dir, config_dir, data_dir};
const KNOWN_INDEXERS: [Indexer; 3] = [Indexer::Red, Indexer::Ops, Indexer::Pth];
#[injectable]
pub struct PathManager {
pub(crate) shared_options: Ref<SharedOptions>,
pub(crate) cache_options: Ref<CacheOptions>,
pub(crate) file_options: Ref<FileOptions>,
}
impl PathManager {
#[must_use]
pub fn default_config_path() -> PathBuf {
if is_docker() {
return PathBuf::from("/config.yml");
}
config_dir()
.expect("config directory should be determinable")
.join(APP_NAME)
.join("config.yml")
}
#[must_use]
pub fn default_cache_dir() -> PathBuf {
if is_docker() {
return PathBuf::from("/cache");
}
cache_dir()
.expect("cache directory should be determinable")
.join(APP_NAME)
}
#[must_use]
pub fn default_reports_dir() -> PathBuf {
Self::default_output_dir().join("reports")
}
#[must_use]
pub fn default_output_dir() -> PathBuf {
if is_docker() {
return PathBuf::from("/output");
}
data_dir()
.expect("data directory should be determinable")
.join(APP_NAME)
.join("output")
}
#[must_use]
pub fn get_cache_dir(&self) -> PathBuf {
self.cache_options.path()
}
#[must_use]
pub fn get_source_torrent_path(&self, torrent_id: u32) -> PathBuf {
let indexer = self.shared_options.get_indexer();
self.get_cache_dir()
.join("torrents")
.join(format!("{torrent_id}.{}.torrent", indexer.as_lowercase()))
}
#[must_use]
pub fn get_output_dir(&self) -> PathBuf {
self.shared_options.output_path()
}
#[must_use]
pub fn get_spectrogram_dir(&self, source: &Source) -> PathBuf {
self.get_output_dir()
.join(SpectrogramName::get(&source.metadata))
}
#[must_use]
pub fn get_transcode_target_dir(&self, source: &Source, target: TargetFormat) -> PathBuf {
self.get_output_dir()
.join(TranscodeName::get(&source.metadata, target))
}
#[must_use]
pub fn get_transcode_path(
&self,
source: &Source,
target: TargetFormat,
flac: &FlacFile,
) -> PathBuf {
let extension = target.get_file_extension();
let rename_tracks = self.file_options.rename_tracks;
let (base_name, sub_dir) = if rename_tracks && flac.disc_context.is_some() {
(
flac.renamed_file_stem(),
flac.renamed_sub_dir().unwrap_or_default(),
)
} else {
(flac.file_name.clone(), flac.sub_dir.clone())
};
self.get_transcode_target_dir(source, target)
.join(sub_dir)
.join(format!("{base_name}.{extension}"))
}
#[must_use]
pub fn get_torrent_path(&self, source: &Source, target: TargetFormat) -> PathBuf {
let indexer = self.shared_options.get_indexer();
self.get_torrent_path_for_indexer(source, target, indexer)
}
#[must_use]
fn get_torrent_path_for_indexer(
&self,
source: &Source,
target: TargetFormat,
indexer: Indexer,
) -> PathBuf {
let mut filename = TranscodeName::get(&source.metadata, target);
filename.push('.');
filename.push_str(indexer.as_lowercase());
filename.push_str(".torrent");
self.get_output_dir().join(filename)
}
pub async fn get_or_duplicate_existing_torrent_path(
&self,
source: &Source,
target: TargetFormat,
) -> Result<Option<PathBuf>, Failure<TorrentCreateAction>> {
let target_path = self.get_torrent_path(source, target);
if target_path.is_file() {
return Ok(Some(target_path));
}
let fallback_path = self.find_fallback_torrent(source, target);
let Some(fallback_path) = fallback_path else {
return Ok(None);
};
let announce_url = self.shared_options.announce_url.clone();
let indexer = self.shared_options.get_indexer();
TorrentCreator::duplicate(&fallback_path, &target_path, announce_url, indexer).await?;
Ok(Some(target_path))
}
fn find_fallback_torrent(&self, source: &Source, target: TargetFormat) -> Option<PathBuf> {
let current_indexer = self.shared_options.get_indexer();
for indexer in KNOWN_INDEXERS {
if indexer == current_indexer {
continue;
}
let path = self.get_torrent_path_for_indexer(source, target, indexer);
if path.is_file() {
return Some(path);
}
}
None
}
}