Download official steam images as backup (#53)

This commit is contained in:
Philip Kristoffersen
2022-03-21 21:34:27 +01:00
committed by GitHub
parent 3663cbb849
commit b4dbcdd29c
2 changed files with 157 additions and 57 deletions
+112 -23
View File
@@ -4,6 +4,7 @@ use std::path::PathBuf;
use std::{collections::HashMap, path::Path}; use std::{collections::HashMap, path::Path};
use futures::{stream, StreamExt}; use futures::{stream, StreamExt};
use serde::{Deserialize, Serialize};
use std::error::Error; use std::error::Error;
use steamgriddb_api::query_parameters::GridDimentions; // 0.3.1 use steamgriddb_api::query_parameters::GridDimentions; // 0.3.1
@@ -74,6 +75,33 @@ pub async fn download_images_for_users<'b>(
} }
} }
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct PublicGameResponseMetadata {
store_asset_mtime: Option<u64>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct PublicGameResponseSteam {
id: String,
metadata: Option<PublicGameResponseMetadata>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct PublicGameResponsePlatforms {
steam: Option<PublicGameResponseSteam>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct PublicGameResponseData {
platforms: Option<PublicGameResponsePlatforms>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct PublicGameResponse {
success: bool,
data: Option<PublicGameResponseData>,
}
async fn search_fo_to_download( async fn search_fo_to_download(
known_images: Vec<String>, known_images: Vec<String>,
user_data_folder: &str, user_data_folder: &str,
@@ -124,7 +152,7 @@ async fn search_fo_to_download(
]; ];
let mut to_download = vec![]; let mut to_download = vec![];
for image_type in types { for image_type in types {
let mut images_needed = shortcuts let images_needed = shortcuts
.iter() .iter()
.filter(|s| search_results.contains_key(&s.app_id)) .filter(|s| search_results.contains_key(&s.app_id))
.filter(|s| !known_images.contains(&image_type.file_name(s.app_id))); .filter(|s| !known_images.contains(&image_type.file_name(s.app_id)));
@@ -133,6 +161,57 @@ async fn search_fo_to_download(
.filter_map(|s| search_results.get(&s.app_id)) .filter_map(|s| search_results.get(&s.app_id))
.copied() .copied()
.collect(); .collect();
let shortcuts: Vec<&ShortcutOwned> = images_needed.collect();
let image_search_result =
get_images_for_ids(client, &image_ids, &image_type, download_animated).await;
match image_search_result {
Ok(images) => {
let grid_folder = Path::new(user_data_folder).join("config").join("grid");
let images = images
.iter()
.enumerate()
.map(|(index, image)| (image, shortcuts[index], image_ids[index]));
let download_for_this_type = stream::iter(images)
.filter_map(|(image, shortcut, game_id)| {
let path = grid_folder.join(image_type.file_name(shortcut.app_id));
async move {
let image_url = match image {
Ok(img) => Some(img.url.clone()),
Err(_) => get_steam_image_url(game_id, &image_type).await,
};
if let Some(url) = image_url {
Some(ToDownload {
path,
url,
app_name: shortcut.app_name.clone(),
image_type: image_type.clone(),
})
} else {
None
}
}
})
.collect::<Vec<ToDownload>>()
.await;
to_download.extend(download_for_this_type);
}
Err(err) => println!("Error getting images: {}", err),
}
}
Ok(to_download)
}
async fn get_images_for_ids(
client: &Client,
image_ids: &[usize],
image_type: &ImageType,
download_animated: bool,
) -> Result<
Vec<steamgriddb_api::response::SteamGridDbResult<steamgriddb_api::images::Image>>,
Box<dyn std::error::Error>,
> {
use steamgriddb_api::query_parameters::AnimtionType; use steamgriddb_api::query_parameters::AnimtionType;
use steamgriddb_api::query_parameters::QueryType::*; use steamgriddb_api::query_parameters::QueryType::*;
let anymation_type = if download_animated { let anymation_type = if download_animated {
@@ -141,31 +220,26 @@ async fn search_fo_to_download(
None None
}; };
let big_picture_dims = [GridDimentions::D920x430, GridDimentions::D460x215]; let big_picture_dims = [GridDimentions::D920x430, GridDimentions::D460x215];
use steamgriddb_api::query_parameters::GridQueryParameters; use steamgriddb_api::query_parameters::GridQueryParameters;
let big_picture_parameters = GridQueryParameters { let big_picture_parameters = GridQueryParameters {
dimentions: Some(&big_picture_dims), dimentions: Some(&big_picture_dims),
types: anymation_type, types: anymation_type,
..Default::default() ..Default::default()
}; };
use steamgriddb_api::query_parameters::HeroQueryParameters; use steamgriddb_api::query_parameters::HeroQueryParameters;
let hero_parameters = HeroQueryParameters { let hero_parameters = HeroQueryParameters {
types: anymation_type, types: anymation_type,
..Default::default() ..Default::default()
}; };
let grid_parameters = GridQueryParameters { let grid_parameters = GridQueryParameters {
types: anymation_type, types: anymation_type,
..Default::default() ..Default::default()
}; };
use steamgriddb_api::query_parameters::LogoQueryParameters; use steamgriddb_api::query_parameters::LogoQueryParameters;
let logo_parameters = LogoQueryParameters { let logo_parameters = LogoQueryParameters {
types: anymation_type, types: anymation_type,
..Default::default() ..Default::default()
}; };
let query_type = match image_type { let query_type = match image_type {
ImageType::Hero => Hero(Some(hero_parameters)), ImageType::Hero => Hero(Some(hero_parameters)),
ImageType::BigPicture => Grid(Some(big_picture_parameters)), ImageType::BigPicture => Grid(Some(big_picture_parameters)),
@@ -174,32 +248,45 @@ async fn search_fo_to_download(
ImageType::Logo => Logo(Some(logo_parameters)), ImageType::Logo => Logo(Some(logo_parameters)),
}; };
match client let image_search_result = client.get_images_for_ids(image_ids, &query_type).await;
.get_images_for_ids(image_ids.as_slice(), &query_type) image_search_result
.await }
{
Ok(images) => { async fn get_steam_image_url(game_id: usize, image_type: &ImageType) -> Option<String> {
for image in images { let steamgriddb_page_url = format!("https://www.steamgriddb.com/api/public/game/{}/", game_id);
if let Some(shortcut) = images_needed.next() { let response = reqwest::get(steamgriddb_page_url).await;
if let Ok(image) = image { match response {
let grid_folder = Path::new(user_data_folder).join("config/grid"); Ok(response) => {
let path = grid_folder.join(image_type.file_name(shortcut.app_id)); let text_response = response.json::<PublicGameResponse>().await;
to_download.push(ToDownload { match text_response {
path, Ok(response) => {
url: image.url, let game_id = response
.data
.clone()
.map(|d| d.platforms.map(|p| p.steam.map(|s| s.id)));
let mtime = response.data.map(|d| {
d.platforms
.map(|p| p.steam.map(|s| s.metadata.map(|m| m.store_asset_mtime)))
}); });
if let (Some(Some(Some(steam_app_id))), Some(Some(Some(Some(Some(mtime)))))) =
(game_id, mtime)
{
return Some(image_type.steam_url(steam_app_id, mtime));
} }
} }
Err(_) => (),
} }
} }
Err(err) => println!("Error getting images: {}", err), Err(_) => (),
} }
} return None;
Ok(to_download)
} }
async fn download_to_download(to_download: &ToDownload) -> Result<(), Box<dyn Error>> { async fn download_to_download(to_download: &ToDownload) -> Result<(), Box<dyn Error>> {
println!("Downloading {} to {:?}", to_download.url, to_download.path); println!(
"Downloading {:?} for {} to {:?}",
to_download.image_type, to_download.app_name, to_download.path
);
let path = &to_download.path; let path = &to_download.path;
let url = &to_download.url; let url = &to_download.url;
let mut file = File::create(path).unwrap(); let mut file = File::create(path).unwrap();
@@ -212,4 +299,6 @@ async fn download_to_download(to_download: &ToDownload) -> Result<(), Box<dyn Er
pub struct ToDownload { pub struct ToDownload {
path: PathBuf, path: PathBuf,
url: String, url: String,
app_name: String,
image_type: ImageType,
} }
+11
View File
@@ -17,4 +17,15 @@ impl ImageType {
ImageType::BigPicture => format!("{}_bigpicture.png", app_id), ImageType::BigPicture => format!("{}_bigpicture.png", app_id),
} }
} }
pub fn steam_url<S:AsRef<str>>(&self,steam_app_id:S,mtime:u64) -> String{
let steam_app_id = steam_app_id.as_ref();
match self{
ImageType::Hero => format!("https://cdn.cloudflare.steamstatic.com/steam/apps/{}/library_hero.jpg?t={}",steam_app_id,mtime),
ImageType::Grid => format!("https://cdn.cloudflare.steamstatic.com/steam/apps/{}/library_600x900_2x.jpg?t={}",steam_app_id,mtime),
ImageType::WideGrid => format!("https://cdn.cloudflare.steamstatic.com/steam/apps/{}/header.jpg?t={}",steam_app_id,mtime),
ImageType::Logo => format!("https://cdn.cloudflare.steamstatic.com/steam/apps/{}/logo.png?t={}",steam_app_id,mtime),
ImageType::BigPicture => format!("https://cdn.cloudflare.steamstatic.com/steam/apps/{}/header.jpg?t={}",steam_app_id,mtime),
}
}
} }