Merge branch '1-filters' into 'master'

Add operating system and language filtering.

Closes #1

See merge request !1
This commit is contained in:
Sebastian Hugentobler 2017-03-21 16:01:21 +00:00
commit 768e0a1408
4 changed files with 98 additions and 19 deletions

View File

@ -26,6 +26,8 @@ about content hashes.
If you want to see the information log while running set `RUST_LOG=info`. If you want to see the information log while running set `RUST_LOG=info`.
---
``` ```
gog-sync gog-sync
``` ```
@ -33,8 +35,18 @@ gog-sync
Normal invocation, uses the current working directory as storage if not configured Normal invocation, uses the current working directory as storage if not configured
otherwise. otherwise.
---
``` ```
gog-sync -s ~/Downloads/games gog-sync -s ~/Downloads/games
``` ```
Overwrite the default or configured storage path. 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.

View File

@ -107,7 +107,11 @@ impl<'a> Gog<'a> {
/// Syncs the contents of a gog account with a local folder. /// Syncs the contents of a gog account with a local folder.
/// Uses a hash to figure out whether something has changed. /// 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<String>,
language_filters: &Vec<String>)
-> Result<(), GogError> {
let configfiles = ConfigFiles::new(); let configfiles = ConfigFiles::new();
let mut config: Config = match configfiles.load("config.json") { let mut config: Config = match configfiles.load("config.json") {
Ok(value) => value, Ok(value) => value,
@ -119,6 +123,8 @@ impl<'a> Gog<'a> {
games: HashMap::new(), games: HashMap::new(),
installers: HashMap::new(), installers: HashMap::new(),
extras: 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(), 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); let game_hash = models::get_hash(&game);
if game_hash_saved == game_hash { if game_hash_saved == game_hash {
@ -147,6 +153,7 @@ impl<'a> Gog<'a> {
let mut key_file = File::create(&key_path)?; let mut key_file = File::create(&key_path)?;
key_file.write_all(game.cd_key.as_bytes())? key_file.write_all(game.cd_key.as_bytes())?
} }
for installer in game.installers { for installer in game.installers {
let installer_hash_saved = match config.installers.get(&installer.manual_url) { let installer_hash_saved = match config.installers.get(&installer.manual_url) {
Some(value) => value.clone(), Some(value) => value.clone(),
@ -174,10 +181,11 @@ impl<'a> Gog<'a> {
} }
for extra in game.extras { 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(), Some(value) => value.clone(),
None => u64::min_value(), None => u64::min_value(),
}; };
let extra_hash = models::get_hash(&extra); let extra_hash = models::get_hash(&extra);
if extra_hash_saved == extra_hash { if extra_hash_saved == extra_hash {
@ -270,6 +278,8 @@ impl<'a> Gog<'a> {
continue; 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) { if [1, 2, 3, 4, 5].contains(&game_id_parsed) {
continue; continue;
} }
@ -280,7 +290,11 @@ impl<'a> Gog<'a> {
return Ok(game_ids); return Ok(game_ids);
} }
fn get_game(&mut self, game_id: u64) -> Result<Game, GogError> { fn get_game(&mut self,
game_id: u64,
os_filters: &Vec<String>,
language_filters: &Vec<String>)
-> Result<Game, GogError> {
let game_uri = self.game_uri(game_id); let game_uri = self.game_uri(game_id);
let response = self.http_client.get(game_uri.as_str())?; 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() { let installer_language = match language[0].as_str() {
Some(value) => value, Some(value) => value.to_lowercase(),
None => "", None => String::default(),
}; };
if installer_language == "" { if installer_language.is_empty() {
error!("Skipping a language for {}", game.title); error!("Skipping a language for {}", game.title);
continue; 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 systems in language[1].as_object() {
for system in systems.keys() { 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_downloads in systems.get(system) {
for real_download in real_downloads.as_array() { for real_download in real_downloads.as_array() {
for download in real_download { for download in real_download {
@ -326,7 +353,7 @@ impl<'a> Gog<'a> {
.as_str() .as_str()
.unwrap_or("")), .unwrap_or("")),
os: system.clone(), os: system.clone(),
language: String::from(installer_language), language: installer_language.clone(),
}; };
game.installers.push(installer); game.installers.push(installer);

View File

@ -9,7 +9,10 @@
//! -V, --version Prints version information //! -V, --version Prints version information
//! //!
//! OPTIONS: //! OPTIONS:
//! -s, --storage <FOLDER> Sets the download folder (defaults to the working directory). //! -l, --language <FILTER> Only sync files for this comma seperated list of languages.
//! -o, --os <FILTER> Only sync files for this comma seperated list of operating systems.
//! Valid values are 'linux', 'mac' and 'windows'.
//! -s, --storage <FOLDER> Sets the download folder (defaults to the working directory).
//! ``` //! ```
extern crate chrono; extern crate chrono;
@ -35,7 +38,6 @@ use clap::{Arg, App};
use gog::Gog; use gog::Gog;
use http::Http; use http::Http;
use models::Config; use models::Config;
use std::collections::HashMap;
fn main() { fn main() {
env_logger::init().unwrap(); env_logger::init().unwrap();
@ -50,19 +52,25 @@ fn main() {
.value_name("FOLDER") .value_name("FOLDER")
.help("Sets the download folder (defaults to the working directory).") .help("Sets the download folder (defaults to the working directory).")
.takes_value(true)) .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(); .get_matches();
let configfiles = ConfigFiles::new(); let configfiles = ConfigFiles::new();
let config: Config = match configfiles.load("config.json") { let config: Config = match configfiles.load("config.json") {
Ok(value) => value, Ok(value) => value,
Err(_) => { Err(_) => Config::new(),
Config {
storage: String::from("."),
games: HashMap::new(),
installers: HashMap::new(),
extras: HashMap::new(),
}
}
}; };
let download_folder: &str = match matches.value_of("storage") { let download_folder: &str = match matches.value_of("storage") {
@ -70,8 +78,18 @@ fn main() {
None => config.storage.as_str(), None => config.storage.as_str(),
}; };
let os_filters: Vec<String> = match matches.value_of("os") {
Some(value) => value.split(',').map(String::from).collect(),
None => config.os_filters,
};
let language_filters: Vec<String> = 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 http_client = Http::new();
let mut gog = Gog::new(&mut http_client); let mut gog = Gog::new(&mut http_client);
gog.login().unwrap(); gog.login().unwrap();
gog.sync(download_folder).unwrap(); gog.sync(download_folder, &os_filters, &language_filters).unwrap();
} }

View File

@ -30,6 +30,7 @@ fn timestamp() -> i64 {
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Config { pub struct Config {
pub storage: String, pub storage: String,
#[serde(default = "default_map")] #[serde(default = "default_map")]
@ -38,12 +39,33 @@ pub struct Config {
pub installers: HashMap<String, u64>, pub installers: HashMap<String, u64>,
#[serde(default = "default_map")] #[serde(default = "default_map")]
pub extras: HashMap<String, u64>, pub extras: HashMap<String, u64>,
#[serde(default = "default_list")]
pub os_filters: Vec<String>,
#[serde(default = "default_list")]
pub language_filters: Vec<String>,
} }
fn default_map() -> HashMap<String, u64> { fn default_map() -> HashMap<String, u64> {
HashMap::new() HashMap::new()
} }
fn default_list() -> Vec<String> {
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(Hash)]
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]