From 5cdd41676ad088de7bb792fd1f00c0fc15dd12d1 Mon Sep 17 00:00:00 2001 From: Sebastian Hugentobler Date: Tue, 21 Mar 2017 16:55:19 +0100 Subject: [PATCH] Add operating system and language filtering. Fixes #1 --- README.md | 12 ++++++++++++ src/gog.rs | 43 +++++++++++++++++++++++++++++++++++-------- src/main.rs | 40 +++++++++++++++++++++++++++++----------- src/models.rs | 22 ++++++++++++++++++++++ 4 files changed, 98 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index f152fb5..c45e09c 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,8 @@ about content hashes. If you want to see the information log while running set `RUST_LOG=info`. +--- + ``` gog-sync ``` @@ -33,8 +35,18 @@ gog-sync Normal invocation, uses the current working directory as storage if not configured otherwise. +--- + ``` gog-sync -s ~/Downloads/games ``` Overwrite the default or configured storage path. + +--- + +``` +gog-sync -l english -o linux,windows +``` + +Only sync english installers and only for linux and windows systems. diff --git a/src/gog.rs b/src/gog.rs index 1027c08..d1f8ed6 100644 --- a/src/gog.rs +++ b/src/gog.rs @@ -107,7 +107,11 @@ impl<'a> Gog<'a> { /// Syncs the contents of a gog account with a local folder. /// Uses a hash to figure out whether something has changed. - pub fn sync(&mut self, storage_path: &str) -> Result<(), GogError> { + pub fn sync(&mut self, + storage_path: &str, + os_filters: &Vec, + language_filters: &Vec) + -> Result<(), GogError> { let configfiles = ConfigFiles::new(); let mut config: Config = match configfiles.load("config.json") { Ok(value) => value, @@ -119,6 +123,8 @@ impl<'a> Gog<'a> { games: HashMap::new(), installers: HashMap::new(), extras: HashMap::new(), + os_filters: os_filters.clone(), + language_filters: language_filters.clone(), } } }; @@ -131,7 +137,7 @@ impl<'a> Gog<'a> { None => u64::min_value(), }; - let game = self.get_game(game_id)?; + let game = self.get_game(game_id, &os_filters, &language_filters)?; let game_hash = models::get_hash(&game); if game_hash_saved == game_hash { @@ -147,6 +153,7 @@ impl<'a> Gog<'a> { let mut key_file = File::create(&key_path)?; key_file.write_all(game.cd_key.as_bytes())? } + for installer in game.installers { let installer_hash_saved = match config.installers.get(&installer.manual_url) { Some(value) => value.clone(), @@ -174,10 +181,11 @@ impl<'a> Gog<'a> { } for extra in game.extras { - let extra_hash_saved = match config.installers.get(&extra.manual_url) { + let extra_hash_saved = match config.extras.get(&extra.manual_url) { Some(value) => value.clone(), None => u64::min_value(), }; + let extra_hash = models::get_hash(&extra); if extra_hash_saved == extra_hash { @@ -270,6 +278,8 @@ impl<'a> Gog<'a> { continue; } + // The numbers in this list are excluded because they refer to + // favourites, promotions and such. if [1, 2, 3, 4, 5].contains(&game_id_parsed) { continue; } @@ -280,7 +290,11 @@ impl<'a> Gog<'a> { return Ok(game_ids); } - fn get_game(&mut self, game_id: u64) -> Result { + fn get_game(&mut self, + game_id: u64, + os_filters: &Vec, + language_filters: &Vec) + -> Result { let game_uri = self.game_uri(game_id); let response = self.http_client.get(game_uri.as_str())?; @@ -298,17 +312,30 @@ impl<'a> Gog<'a> { } let installer_language = match language[0].as_str() { - Some(value) => value, - None => "", + Some(value) => value.to_lowercase(), + None => String::default(), }; - if installer_language == "" { + if installer_language.is_empty() { error!("Skipping a language for {}", game.title); continue; } + if !language_filters.is_empty() && !language_filters.contains(&installer_language) { + info!("Skipping {} for {}", &installer_language, game.title); + continue; + } + for systems in language[1].as_object() { for system in systems.keys() { + if !os_filters.is_empty() && !os_filters.contains(system) { + info!("Skipping {} {} for {}", + &installer_language, + system, + game.title); + continue; + } + for real_downloads in systems.get(system) { for real_download in real_downloads.as_array() { for download in real_download { @@ -326,7 +353,7 @@ impl<'a> Gog<'a> { .as_str() .unwrap_or("")), os: system.clone(), - language: String::from(installer_language), + language: installer_language.clone(), }; game.installers.push(installer); diff --git a/src/main.rs b/src/main.rs index 10ca845..96b4e43 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,7 +9,10 @@ //! -V, --version Prints version information //! //! OPTIONS: -//! -s, --storage Sets the download folder (defaults to the working directory). +//! -l, --language Only sync files for this comma seperated list of languages. +//! -o, --os Only sync files for this comma seperated list of operating systems. +//! Valid values are 'linux', 'mac' and 'windows'. +//! -s, --storage Sets the download folder (defaults to the working directory). //! ``` extern crate chrono; @@ -35,7 +38,6 @@ use clap::{Arg, App}; use gog::Gog; use http::Http; use models::Config; -use std::collections::HashMap; fn main() { env_logger::init().unwrap(); @@ -50,19 +52,25 @@ fn main() { .value_name("FOLDER") .help("Sets the download folder (defaults to the working directory).") .takes_value(true)) + .arg(Arg::with_name("os") + .short("o") + .long("os") + .value_name("FILTER") + .help("Only sync files for this comma seperated list of operating systems.\n\ + Valid values are 'linux', 'mac' and 'windows'.") + .takes_value(true)) + .arg(Arg::with_name("language") + .short("l") + .long("language") + .value_name("FILTER") + .help("Only sync files for this comma seperated list of languages.") + .takes_value(true)) .get_matches(); let configfiles = ConfigFiles::new(); let config: Config = match configfiles.load("config.json") { Ok(value) => value, - Err(_) => { - Config { - storage: String::from("."), - games: HashMap::new(), - installers: HashMap::new(), - extras: HashMap::new(), - } - } + Err(_) => Config::new(), }; let download_folder: &str = match matches.value_of("storage") { @@ -70,8 +78,18 @@ fn main() { None => config.storage.as_str(), }; + let os_filters: Vec = match matches.value_of("os") { + Some(value) => value.split(',').map(String::from).collect(), + None => config.os_filters, + }; + + let language_filters: Vec = match matches.value_of("language") { + Some(value) => value.split(',').map(String::from).collect(), + None => config.language_filters, + }; + let mut http_client = Http::new(); let mut gog = Gog::new(&mut http_client); gog.login().unwrap(); - gog.sync(download_folder).unwrap(); + gog.sync(download_folder, &os_filters, &language_filters).unwrap(); } diff --git a/src/models.rs b/src/models.rs index c41289a..9b2ab09 100644 --- a/src/models.rs +++ b/src/models.rs @@ -30,6 +30,7 @@ fn timestamp() -> i64 { } #[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] pub struct Config { pub storage: String, #[serde(default = "default_map")] @@ -38,12 +39,33 @@ pub struct Config { pub installers: HashMap, #[serde(default = "default_map")] pub extras: HashMap, + #[serde(default = "default_list")] + pub os_filters: Vec, + #[serde(default = "default_list")] + pub language_filters: Vec, } fn default_map() -> HashMap { HashMap::new() } +fn default_list() -> Vec { + Vec::new() +} + +impl Config { + pub fn new() -> Config { + Config { + storage: String::from("."), + games: HashMap::new(), + installers: HashMap::new(), + extras: HashMap::new(), + os_filters: Vec::new(), + language_filters: Vec::new(), + } + } +} + #[derive(Hash)] #[derive(Serialize, Deserialize)] #[serde(rename_all = "camelCase")]