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`.
---
```
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.

View File

@ -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<String>,
language_filters: &Vec<String>)
-> 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<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 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);

View File

@ -9,7 +9,10 @@
//! -V, --version Prints version information
//!
//! 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;
@ -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<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 gog = Gog::new(&mut http_client);
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)]
#[serde(rename_all = "camelCase")]
pub struct Config {
pub storage: String,
#[serde(default = "default_map")]
@ -38,12 +39,33 @@ pub struct Config {
pub installers: HashMap<String, u64>,
#[serde(default = "default_map")]
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> {
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(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]