game_manager_lib/services/integration/gamebrain/
media.rs1use crate::database;
4use crate::utils::http_client::HTTP_CLIENT;
5
6use tauri::AppHandle;
7
8use super::cache::{
9 gamebrain_id_cache_key, gamebrain_media_cache_key, read_cached_json, read_stale_gamebrain_id,
10 save_cached_json,
11};
12use super::helpers::{build_game_media, resolve_gamebrain_id};
13use super::models::GameMedia;
14use super::raw::RawGameDetail;
15
16pub async fn fetch_game_media(
37 app: &AppHandle,
38 playlite_game_id: &str,
39 game_name: &str,
40) -> Result<GameMedia, String> {
41 let api_key = database::get_secret(app, "gamebrain_api_key").map_err(|e| e.to_string())?;
42
43 if api_key.trim().is_empty() {
44 return Err("GameBrain API key não configurada".into());
45 }
46
47 let gamebrain_id = {
51 let id_cache_key = gamebrain_id_cache_key(playlite_game_id);
52
53 if let Some(id) = read_cached_json::<u64>(app, "gamebrain", &id_cache_key, false)? {
54 tracing::debug!(
55 "GameBrain media: ID cache hit => game_id='{}' gamebrain_id={}",
56 playlite_game_id,
57 id
58 );
59 id
60 } else {
61 tracing::debug!(
62 "GameBrain media: ID cache miss => resolvendo '{}' via suggestions",
63 game_name
64 );
65
66 match resolve_gamebrain_id(&api_key, game_name).await {
67 Ok(Some(id)) => {
68 let _ = save_cached_json(app, "gamebrain", &id_cache_key, &id);
69 id
70 }
71 Ok(None) => {
72 read_stale_gamebrain_id(app, &id_cache_key, playlite_game_id)?.ok_or_else(
74 || format!("Jogo '{}' não encontrado na GameBrain", game_name),
75 )?
76 }
77 Err(err) => {
78 read_stale_gamebrain_id(app, &id_cache_key, playlite_game_id)?.ok_or(err)?
79 }
80 }
81 }
82 };
83
84 let media_cache_key = gamebrain_media_cache_key(gamebrain_id);
86
87 if let Some(cached) = read_cached_json::<GameMedia>(app, "gamebrain", &media_cache_key, false)?
88 {
89 tracing::debug!("GameBrain media cache hit => gamebrain_id={}", gamebrain_id);
90 return Ok(cached);
91 }
92
93 tracing::debug!(
95 "GameBrain media cache miss => chamando /v1/games/{} para mídia",
96 gamebrain_id
97 );
98
99 let url = format!("https://api.gamebrain.co/v1/games/{}", gamebrain_id);
100
101 let response = HTTP_CLIENT
102 .get(&url)
103 .header("x-api-key", &api_key)
104 .send()
105 .await
106 .map_err(|e| {
107 tracing::error!("GameBrain game detail request error: {}", e);
108 e.to_string()
109 });
110
111 let response = match response {
112 Ok(r) => r,
113 Err(err) => {
114 return read_cached_json::<GameMedia>(app, "gamebrain", &media_cache_key, true)?
116 .ok_or(err);
117 }
118 };
119
120 let status = response.status();
121
122 if !status.is_success() {
123 let body = response.text().await.unwrap_or_default();
124 tracing::error!(
125 "GameBrain game detail HTTP Error => status={} body={}",
126 status,
127 body
128 );
129 return read_cached_json::<GameMedia>(app, "gamebrain", &media_cache_key, true)?
130 .ok_or_else(|| format!("Erro GameBrain game detail: {}", status));
131 }
132
133 let text = response.text().await.map_err(|e| e.to_string())?;
134
135 tracing::debug!("GameBrain game detail response size: {} bytes", text.len());
136
137 let raw: RawGameDetail = serde_json::from_str(&text).map_err(|e| {
138 tracing::error!("GameBrain game detail JSON parse error: {}", e);
139 format!("Erro JSON GameBrain game detail: {}", e)
140 })?;
141
142 let media = build_game_media(raw);
143
144 tracing::debug!(
145 "GameBrain media => {} screenshots, {} trailers, {} embeds para gamebrain_id={}",
146 media.screenshots.len(),
147 media.trailers.len(),
148 media.youtube_embeds.len(),
149 gamebrain_id
150 );
151
152 let _ = save_cached_json(app, "gamebrain", &media_cache_key, &media);
153
154 Ok(media)
155}