From 88827749db299c42482ec8edabbf29890d9237c2 Mon Sep 17 00:00:00 2001 From: Sebastian Hugentobler Date: Tue, 21 Mar 2017 22:28:03 +0100 Subject: [PATCH] initial movie support --- Cargo.lock | 2 +- src/configfiles.rs | 10 ++++++++++ src/gog.rs | 47 ++++++++++++++++++++++++++++++++++++++++++---- src/http.rs | 13 +++++++++++++ src/main.rs | 17 ++++++++++++++++- src/models.rs | 4 ++++ 6 files changed, 87 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5787265..8af871c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ [root] name = "gog-sync" -version = "0.2.0" +version = "0.2.1" dependencies = [ "chrono 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.21.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/src/configfiles.rs b/src/configfiles.rs index 6eb1ba9..2915a32 100644 --- a/src/configfiles.rs +++ b/src/configfiles.rs @@ -7,6 +7,7 @@ use serde::{Deserialize, Serialize}; use serde_json; +use std::fmt; use std::fs::File; use std; use std::io::{Read, Write}; @@ -20,6 +21,15 @@ pub enum ConfigError { SerdeJsonError(serde_json::Error), } +impl fmt::Display for ConfigError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + ConfigError::IOError(ref err) => fmt::Display::fmt(err, f), + ConfigError::SerdeJsonError(ref err) => fmt::Display::fmt(err, f), + } + } +} + impl From for ConfigError { fn from(e: std::io::Error) -> Self { ConfigError::IOError(e) diff --git a/src/gog.rs b/src/gog.rs index a9bead7..edbeab0 100644 --- a/src/gog.rs +++ b/src/gog.rs @@ -17,6 +17,7 @@ use models::{Token, Game, Installer, Config}; use serde_json; use serde_json::Value; use std::collections::HashMap; +use std::fmt; use std::fs; use std::fs::File; use std::io; @@ -33,6 +34,18 @@ pub enum GogError { IOError(io::Error), } +impl fmt::Display for GogError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + GogError::Error(ref err) => fmt::Display::fmt(err, f), + GogError::ConfigError(ref err) => fmt::Display::fmt(err, f), + GogError::HttpError(ref err) => fmt::Display::fmt(err, f), + GogError::SerdeError(ref err) => fmt::Display::fmt(err, f), + GogError::IOError(ref err) => fmt::Display::fmt(err, f), + } + } +} + impl From for GogError { fn from(e: ConfigError) -> Self { GogError::ConfigError(e) @@ -109,6 +122,7 @@ impl<'a> Gog<'a> { /// Uses a hash to figure out whether something has changed. pub fn sync(&mut self, storage_path: &str, + storage_path_movies: &str, os_filters: &Vec, language_filters: &Vec) -> Result<(), GogError> { @@ -120,6 +134,7 @@ impl<'a> Gog<'a> { Config { storage: String::from(storage_path), + movie_storage: String::from(storage_path_movies), games: HashMap::new(), installers: HashMap::new(), extras: HashMap::new(), @@ -137,7 +152,14 @@ impl<'a> Gog<'a> { None => u64::min_value(), }; - let game = self.get_game(game_id, &os_filters, &language_filters)?; + let game = match self.get_game(game_id, &os_filters, &language_filters) { + Ok(value) => value, + Err(error) => { + error!("{}", error); + break; + } + }; + let game_hash = models::get_hash(&game); if game_hash_saved == game_hash { @@ -145,7 +167,12 @@ impl<'a> Gog<'a> { continue; } - let game_root = Path::new(storage_path).join(&game.title); + let game_root = if game.is_movie { + Path::new(storage_path_movies).join(&game.title) + } else { + Path::new(storage_path).join(&game.title) + }; + fs::create_dir_all(&game_root)?; if !game.cd_key.is_empty() { @@ -306,6 +333,17 @@ impl<'a> Gog<'a> { let game_raw: Value = serde_json::from_str(response.as_str())?; let downloads = &game_raw["downloads"]; + if game_raw.is_object() && !game_raw.as_object().unwrap().contains_key("forumLink") { + return Err(GogError::Error("No forumLink property")); + } + + let is_movie = match game_raw["forumLink"].as_str() { + Some(value) => value == "https://embed.gog.com/forum/movies", + None => false, + }; + + game.is_movie = is_movie; + for languages in downloads.as_array() { for language in languages { if !language.is_array() || language.as_array().unwrap().len() < 2 { @@ -323,14 +361,15 @@ impl<'a> Gog<'a> { continue; } - if !language_filters.is_empty() && !language_filters.contains(&installer_language) { + if !is_movie && !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) { + if is_movie && !os_filters.is_empty() && !os_filters.contains(system) { info!("Skipping {} {} for {}", &installer_language, system, diff --git a/src/http.rs b/src/http.rs index 5fdd8c2..a43b262 100644 --- a/src/http.rs +++ b/src/http.rs @@ -3,6 +3,7 @@ use curl; use curl::easy::{Easy, List, WriteError}; +use std::fmt; use std::fs; use std::fs::File; use std::io; @@ -24,6 +25,18 @@ pub enum HttpError { UrlParseError(url::ParseError), } +impl fmt::Display for HttpError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + HttpError::Error(ref err) => fmt::Display::fmt(err, f), + HttpError::CurlError(ref err) => fmt::Display::fmt(err, f), + HttpError::Utf8Error(ref err) => fmt::Display::fmt(err, f), + HttpError::IOError(ref err) => fmt::Display::fmt(err, f), + HttpError::UrlParseError(ref err) => fmt::Display::fmt(err, f), + } + } +} + impl From for HttpError { fn from(e: curl::Error) -> Self { HttpError::CurlError(e) diff --git a/src/main.rs b/src/main.rs index 442079a..81c91e2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -52,6 +52,12 @@ fn main() { .value_name("FOLDER") .help("Sets the download folder (defaults to the working directory).") .takes_value(true)) + .arg(Arg::with_name("movie-storage") + .short("m") + .long("movie-storage") + .value_name("FOLDER") + .help("Sets the download folder for movies (defaults to the game directory).") + .takes_value(true)) .arg(Arg::with_name("os") .short("o") .long("os") @@ -78,6 +84,11 @@ fn main() { None => config.storage.as_str(), }; + let download_folder_movies: &str = match matches.value_of("movieStorage") { + Some(value) => value, + None => config.movie_storage.as_str(), + }; + let os_filters: Vec = match matches.value_of("os") { Some(value) => value.split(',').map(String::from).collect(), None => config.os_filters, @@ -91,5 +102,9 @@ fn main() { let mut http_client = Http::new(); let mut gog = Gog::new(&mut http_client); gog.login().unwrap(); - gog.sync(download_folder, &os_filters, &language_filters).unwrap(); + gog.sync(download_folder, + download_folder_movies, + &os_filters, + &language_filters) + .unwrap(); } diff --git a/src/models.rs b/src/models.rs index 9b2ab09..e577f58 100644 --- a/src/models.rs +++ b/src/models.rs @@ -33,6 +33,7 @@ fn timestamp() -> i64 { #[serde(rename_all = "camelCase")] pub struct Config { pub storage: String, + pub movie_storage: String, #[serde(default = "default_map")] pub games: HashMap, #[serde(default = "default_map")] @@ -57,6 +58,7 @@ impl Config { pub fn new() -> Config { Config { storage: String::from("."), + movie_storage: String::from("."), games: HashMap::new(), installers: HashMap::new(), extras: HashMap::new(), @@ -73,6 +75,8 @@ pub struct Game { pub title: String, pub cd_key: String, #[serde(skip_deserializing)] + pub is_movie: bool, + #[serde(skip_deserializing)] pub installers: Vec, pub extras: Vec, }