game_manager_lib/commands/metadata/
covers.rs1use super::shared::{fetch_rawg_metadata, EnrichCompletePayload, EnrichProgress};
6use crate::constants::RAWG_RATE_LIMIT_MS;
7use crate::database;
8use crate::database::AppState;
9use crate::errors::AppError;
10use rusqlite::params;
11use std::time::Duration;
12use tauri::{AppHandle, Emitter, Manager, State};
13use tokio::time::sleep;
14use tracing::info;
15
16#[tauri::command]
18pub async fn fetch_missing_covers(app: AppHandle) -> Result<(), AppError> {
19 let app_handle = app.clone();
20 let api_key = database::get_secret(&app, "rawg_api_key")?;
21 if api_key.is_empty() {
22 return Err(AppError::ValidationError(
23 "API Key da RAWG não configurada.".to_string(),
24 ));
25 }
26
27 tauri::async_runtime::spawn(async move {
28 info!("Iniciando busca de capas faltantes com cache...");
29
30 let state: State<AppState> = app_handle.state();
31 let mut total_updated = 0;
32 let mut total_failed = 0;
33
34 let games_without_cover: Vec<(String, String)> = {
35 let conn = state.games_db.lock().unwrap();
36 let mut stmt = conn
37 .prepare("SELECT id, name FROM games WHERE cover_url IS NULL OR cover_url = ''")
38 .unwrap();
39
40 stmt.query_map([], |row| -> rusqlite::Result<(String, String)> {
41 Ok((row.get(0)?, row.get(1)?))
42 })
43 .unwrap()
44 .flatten()
45 .collect()
46 };
47
48 if !games_without_cover.is_empty() {
49 let count = games_without_cover.len();
50
51 for (index, (game_id, name)) in games_without_cover.into_iter().enumerate() {
52 let _ = app_handle.emit(
53 "enrich_progress",
54 EnrichProgress {
55 current: (index + 1) as i32,
56 total_found: count as i32,
57 last_game: format!("Capa: {}", name),
58 status: "running".to_string(),
59 platform: None,
60 },
61 );
62
63 let img_url = {
65 let cache_conn = state.cache_db.lock().unwrap();
66 let result = tokio::task::block_in_place(|| {
67 let rt = tokio::runtime::Handle::current();
68 rt.block_on(async {
69 fetch_rawg_metadata(&api_key, &name, &cache_conn)
70 .await
71 .and_then(|d| d.background_image)
72 })
73 });
74 result
75 };
76
77 if let Some(img) = img_url {
78 let conn = state.games_db.lock().unwrap();
79 if conn
80 .execute(
81 "UPDATE games SET cover_url = ?1 WHERE id = ?2",
82 params![img, game_id],
83 )
84 .is_ok()
85 {
86 total_updated += 1;
87 } else {
88 total_failed += 1;
89 }
90 } else {
91 total_failed += 1;
92 }
93
94 sleep(Duration::from_millis(RAWG_RATE_LIMIT_MS)).await;
95 }
96 }
97
98 info!(
99 "Busca de capas finalizada: {} sucesso, {} falhas",
100 total_updated, total_failed
101 );
102 let _ = app_handle.emit(
103 "enrich_complete",
104 EnrichCompletePayload {
105 platform: None,
106 message: "Busca de capas finalizada.".to_string(),
107 },
108 );
109 });
110
111 Ok(())
112}