initial movie support

This commit is contained in:
Sebastian Hugentobler 2017-03-21 22:28:03 +01:00
parent e385a07aac
commit 88827749db
6 changed files with 87 additions and 6 deletions

2
Cargo.lock generated
View File

@ -1,6 +1,6 @@
[root] [root]
name = "gog-sync" name = "gog-sync"
version = "0.2.0" version = "0.2.1"
dependencies = [ dependencies = [
"chrono 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "clap 2.21.1 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -7,6 +7,7 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json; use serde_json;
use std::fmt;
use std::fs::File; use std::fs::File;
use std; use std;
use std::io::{Read, Write}; use std::io::{Read, Write};
@ -20,6 +21,15 @@ pub enum ConfigError {
SerdeJsonError(serde_json::Error), 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<std::io::Error> for ConfigError { impl From<std::io::Error> for ConfigError {
fn from(e: std::io::Error) -> Self { fn from(e: std::io::Error) -> Self {
ConfigError::IOError(e) ConfigError::IOError(e)

View File

@ -17,6 +17,7 @@ use models::{Token, Game, Installer, Config};
use serde_json; use serde_json;
use serde_json::Value; use serde_json::Value;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt;
use std::fs; use std::fs;
use std::fs::File; use std::fs::File;
use std::io; use std::io;
@ -33,6 +34,18 @@ pub enum GogError {
IOError(io::Error), 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<ConfigError> for GogError { impl From<ConfigError> for GogError {
fn from(e: ConfigError) -> Self { fn from(e: ConfigError) -> Self {
GogError::ConfigError(e) GogError::ConfigError(e)
@ -109,6 +122,7 @@ impl<'a> Gog<'a> {
/// 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, pub fn sync(&mut self,
storage_path: &str, storage_path: &str,
storage_path_movies: &str,
os_filters: &Vec<String>, os_filters: &Vec<String>,
language_filters: &Vec<String>) language_filters: &Vec<String>)
-> Result<(), GogError> { -> Result<(), GogError> {
@ -120,6 +134,7 @@ impl<'a> Gog<'a> {
Config { Config {
storage: String::from(storage_path), storage: String::from(storage_path),
movie_storage: String::from(storage_path_movies),
games: HashMap::new(), games: HashMap::new(),
installers: HashMap::new(), installers: HashMap::new(),
extras: HashMap::new(), extras: HashMap::new(),
@ -137,7 +152,14 @@ impl<'a> Gog<'a> {
None => u64::min_value(), 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); let game_hash = models::get_hash(&game);
if game_hash_saved == game_hash { if game_hash_saved == game_hash {
@ -145,7 +167,12 @@ impl<'a> Gog<'a> {
continue; 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)?; fs::create_dir_all(&game_root)?;
if !game.cd_key.is_empty() { 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 game_raw: Value = serde_json::from_str(response.as_str())?;
let downloads = &game_raw["downloads"]; 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 languages in downloads.as_array() {
for language in languages { for language in languages {
if !language.is_array() || language.as_array().unwrap().len() < 2 { if !language.is_array() || language.as_array().unwrap().len() < 2 {
@ -323,14 +361,15 @@ impl<'a> Gog<'a> {
continue; 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); info!("Skipping {} for {}", &installer_language, game.title);
continue; 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) { if is_movie && !os_filters.is_empty() && !os_filters.contains(system) {
info!("Skipping {} {} for {}", info!("Skipping {} {} for {}",
&installer_language, &installer_language,
system, system,

View File

@ -3,6 +3,7 @@
use curl; use curl;
use curl::easy::{Easy, List, WriteError}; use curl::easy::{Easy, List, WriteError};
use std::fmt;
use std::fs; use std::fs;
use std::fs::File; use std::fs::File;
use std::io; use std::io;
@ -24,6 +25,18 @@ pub enum HttpError {
UrlParseError(url::ParseError), 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<curl::Error> for HttpError { impl From<curl::Error> for HttpError {
fn from(e: curl::Error) -> Self { fn from(e: curl::Error) -> Self {
HttpError::CurlError(e) HttpError::CurlError(e)

View File

@ -52,6 +52,12 @@ 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("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") .arg(Arg::with_name("os")
.short("o") .short("o")
.long("os") .long("os")
@ -78,6 +84,11 @@ fn main() {
None => config.storage.as_str(), 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<String> = match matches.value_of("os") { let os_filters: Vec<String> = match matches.value_of("os") {
Some(value) => value.split(',').map(String::from).collect(), Some(value) => value.split(',').map(String::from).collect(),
None => config.os_filters, None => config.os_filters,
@ -91,5 +102,9 @@ fn main() {
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, &os_filters, &language_filters).unwrap(); gog.sync(download_folder,
download_folder_movies,
&os_filters,
&language_filters)
.unwrap();
} }

View File

@ -33,6 +33,7 @@ fn timestamp() -> i64 {
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Config { pub struct Config {
pub storage: String, pub storage: String,
pub movie_storage: String,
#[serde(default = "default_map")] #[serde(default = "default_map")]
pub games: HashMap<String, u64>, pub games: HashMap<String, u64>,
#[serde(default = "default_map")] #[serde(default = "default_map")]
@ -57,6 +58,7 @@ impl Config {
pub fn new() -> Config { pub fn new() -> Config {
Config { Config {
storage: String::from("."), storage: String::from("."),
movie_storage: String::from("."),
games: HashMap::new(), games: HashMap::new(),
installers: HashMap::new(), installers: HashMap::new(),
extras: HashMap::new(), extras: HashMap::new(),
@ -73,6 +75,8 @@ pub struct Game {
pub title: String, pub title: String,
pub cd_key: String, pub cd_key: String,
#[serde(skip_deserializing)] #[serde(skip_deserializing)]
pub is_movie: bool,
#[serde(skip_deserializing)]
pub installers: Vec<Installer>, pub installers: Vec<Installer>,
pub extras: Vec<Extra>, pub extras: Vec<Extra>,
} }