use std::env;
use std::ffi::OsStr;
use std::fs;
use std::path::{Path, PathBuf};
pub mod logging;
pub mod traits;
pub mod tokenizer;
mod mimetypes;
use self::traits::Digits;
use self::tokenizer::TemplateToken as Token;
use tvdb;
#[derive(Clone, Debug)]
pub struct Arguments {
pub automatic: bool,
pub dry_run: bool,
pub log_changes: bool,
pub verbose: bool,
pub directory: String,
pub series_name: String,
pub season_number: usize,
pub episode_count: usize,
pub pad_length: usize,
pub template: Vec<Token>
}
impl Arguments {
pub fn get_targets(&self, directory: &str, episodes: &[PathBuf], episode_index: usize) -> Result<Vec<PathBuf>, String> {
let api = tvdb::Tvdb::new("0629B785CE550C8D");
let series_info = if self.template.contains(&Token::TVDB) {
match api.search(self.series_name.as_str(), "en") {
Ok(reply) => Some(reply),
Err(_) => { return Err(String::from("unable to get TVDB series information")); }
}
} else {
None
};
let mut output: Vec<PathBuf> = Vec::new();
let mut current_index = episode_index;
for file in episodes {
let tvdb_title = if self.template.contains(&Token::TVDB) {
let reply = series_info.clone().unwrap();
match api.episode(&reply[0], self.season_number as u32, current_index as u32) {
Ok(episode) => episode.episode_name,
Err(_) => { return Err(format!("episode '{}' does not exist", file.to_string_lossy())); }
}
} else {
String::new()
};
let new_destination = self.get_destination(Path::new(directory), file, current_index, &tvdb_title);
output.push(new_destination);
current_index += 1;
}
Ok(output)
}
pub fn get_destination(&self, directory: &Path, file: &Path, episode: usize, title: &str) -> PathBuf {
let mut destination = String::from(directory.to_str().unwrap());
destination.push('/');
let mut filename = String::new();
for pattern in self.template.clone() {
match pattern {
Token::Character(value) => filename.push(value),
Token::Series => filename.push_str(self.series_name.clone().as_str()),
Token::Season => filename.push_str(self.season_number.to_string().as_str()),
Token::Episode => filename.push_str(episode.to_padded_string('0', self.pad_length).as_str()),
Token::TVDB => filename.push_str(title)
}
}
filename = String::from(filename.trim()); filename = filename.replace("/", "-");
let extension = file.extension().unwrap_or_else(|| OsStr::new("")).to_str().unwrap_or("");
if !extension.is_empty() {
filename.push('.');
filename.push_str(extension);
}
destination.push_str(&filename);
PathBuf::from(destination)
}
}
pub fn shorten_path(path: &Path) -> PathBuf {
path.strip_prefix(&env::current_dir().unwrap())
.map(|value| PathBuf::from(".").join(value))
.unwrap_or(path.strip_prefix(&env::home_dir().unwrap()).ok()
.map_or_else(|| path.to_path_buf(), |value| PathBuf::from("~").join(value)))
}
pub fn derive_season_number(season: &Path) -> Option<usize> {
let mut directory_name = season.file_name().unwrap().to_str().unwrap().to_lowercase();
match directory_name.as_str() {
"season0" | "season 0" | "specials" => Some(0),
_ => {
directory_name = directory_name.replace("season", "");
directory_name = directory_name.replace(" ", "");
directory_name.parse::<usize>().ok()
}
}
}
pub fn get_seasons(directory: &str) -> Result<Vec<PathBuf>, &str> {
fs::read_dir(directory).ok().map_or(Err("unable to read directory"), |files| {
let mut seasons = Vec::new();
for entry in files {
let status = entry.ok().map_or(Some("tv-renamer: unable to get directory entry"), |entry| {
entry.metadata().ok().map_or(Some("tv-renamer: unable to get metadata"), |metadata| {
if metadata.is_dir() { seasons.push(entry.path()); }
None
})
});
if status.is_some() { return Err(status.unwrap()); }
}
seasons.sort_by(|a, b| a.to_string_lossy().cmp(&b.to_string_lossy()));
Ok(seasons)
})
}
pub fn get_episodes(directory: &str) -> Result<Vec<PathBuf>, &str> {
fs::read_dir(directory).ok().map_or(Err("tv-renamer: unable to read file"), |files| {
let video_extensions = try!(mimetypes::get_video_extensions());
let mut episodes = Vec::new();
for entry in files {
let status = entry.ok().map_or(Some("tv-renamer: unable to get file entry"), |entry| {
entry.metadata().ok().map_or(Some("tv-renamer: unable to get metadata"), |metadata| {
if metadata.is_file() {
for extension in &video_extensions {
if extension.as_str() == entry.path().extension().unwrap().to_str().unwrap() {
episodes.push(entry.path());
}
}
}
None
})
});
if status.is_some() { return Err(status.unwrap()); }
}
episodes.sort_by(|a, b| a.to_string_lossy().to_lowercase().cmp(&b.to_string_lossy().to_lowercase()));
Ok(episodes)
})
}
#[test]
fn test_derive_season_number() {
assert_eq!(derive_season_number(&Path::new("Specials")), Some(0));
assert_eq!(derive_season_number(&Path::new("Season 0")), Some(0));
assert_eq!(derive_season_number(&Path::new("Season 1")), Some(1));
assert_eq!(derive_season_number(&Path::new("season9")), Some(9));
assert_eq!(derive_season_number(&Path::new("Extras")), None);
}