From f81eff849649e3e44168aca4373392b03ee87f4d Mon Sep 17 00:00:00 2001 From: Valentin Haudiquet Date: Wed, 25 Mar 2026 11:07:36 +0100 Subject: [PATCH] record-daemon: fmt, clippy --- record-daemon/src/lqp/client.rs | 202 ++++++++++++++++++------- record-daemon/src/lqp/mod.rs | 5 +- record-daemon/src/main.rs | 36 +++-- record-daemon/src/recording/encoder.rs | 6 +- record-daemon/src/state/machine.rs | 18 ++- 5 files changed, 189 insertions(+), 78 deletions(-) diff --git a/record-daemon/src/lqp/client.rs b/record-daemon/src/lqp/client.rs index 1601cdb..7d3f142 100644 --- a/record-daemon/src/lqp/client.rs +++ b/record-daemon/src/lqp/client.rs @@ -976,17 +976,20 @@ impl LqpClient { /// Get active player data from live client. pub async fn get_live_client_active_player(&self) -> Result { - self.request("GET", endpoints::LIVE_CLIENT_DATA_ACTIVE_PLAYER).await + self.request("GET", endpoints::LIVE_CLIENT_DATA_ACTIVE_PLAYER) + .await } /// Get player list from live client (contains all players with puuid and summoner names). pub async fn get_live_client_player_list(&self) -> Result { - self.request("GET", endpoints::LIVE_CLIENT_DATA_PLAYER_LIST).await + self.request("GET", endpoints::LIVE_CLIENT_DATA_PLAYER_LIST) + .await } /// Get local player selection from champion select. pub async fn get_local_player_selection(&self) -> Result { - self.request("GET", endpoints::CHAMPION_SELECT_LOCAL_PLAYER).await + self.request("GET", endpoints::CHAMPION_SELECT_LOCAL_PLAYER) + .await } /// Fetch pre-game metadata (champion, skin, runes, queue info). @@ -1130,9 +1133,13 @@ impl LqpClient { // Get summoner info - try multiple field names for summoner name if let Ok(summoner) = self.get_summoner().await { - metadata.puuid = summoner.get("puuid").and_then(|p| p.as_str()).map(|s| s.to_string()); + metadata.puuid = summoner + .get("puuid") + .and_then(|p| p.as_str()) + .map(|s| s.to_string()); // Try displayName first, then name, then internalName - metadata.summoner_name = summoner.get("displayName") + metadata.summoner_name = summoner + .get("displayName") .or_else(|| summoner.get("name")) .or_else(|| summoner.get("internalName")) .and_then(|n| n.as_str()) @@ -1141,8 +1148,14 @@ impl LqpClient { // Get rune page if let Ok(rune_page) = self.get_rune_page().await { - let primary_style_id = rune_page.get("primaryStyleId").and_then(|id| id.as_u64()).unwrap_or(0) as u32; - let secondary_style_id = rune_page.get("subStyleId").and_then(|id| id.as_u64()).unwrap_or(0) as u32; + let primary_style_id = rune_page + .get("primaryStyleId") + .and_then(|id| id.as_u64()) + .unwrap_or(0) as u32; + let secondary_style_id = rune_page + .get("subStyleId") + .and_then(|id| id.as_u64()) + .unwrap_or(0) as u32; let selected_perks = rune_page .get("selectedPerkIds") .and_then(|ids| ids.as_array()) @@ -1159,37 +1172,57 @@ impl LqpClient { secondary_style_id, selected_perks, stat_modifiers: Vec::new(), // Stat modifiers are part of selectedPerkIds - name: rune_page.get("name").and_then(|n| n.as_str()).map(|s| s.to_string()), - current: rune_page.get("current").and_then(|c| c.as_bool()).unwrap_or(true), + name: rune_page + .get("name") + .and_then(|n| n.as_str()) + .map(|s| s.to_string()), + current: rune_page + .get("current") + .and_then(|c| c.as_bool()) + .unwrap_or(true), }); } } // Try to get summoner spells from live client data (available during game) if let Ok(active_player) = self.get_live_client_active_player().await { - debug!("[METADATA] Live client active player data: {:?}", active_player); - + debug!( + "[METADATA] Live client active player data: {:?}", + active_player + ); + // Get summoner spells from active player - try multiple field structures if let Some(summoner_spells) = active_player.get("summonerSpells") { // Try nested structure first: summonerSpells.summonerSpellOne.spellId - let spell1_id = summoner_spells.get("summonerSpellOne") + let spell1_id = summoner_spells + .get("summonerSpellOne") .and_then(|s| s.get("spellId")) .and_then(|id| id.as_u64()) .unwrap_or_else(|| { // Fallback: direct spell1Id field - summoner_spells.get("spell1Id").and_then(|id| id.as_u64()).unwrap_or(0) + summoner_spells + .get("spell1Id") + .and_then(|id| id.as_u64()) + .unwrap_or(0) }) as u32; - - let spell2_id = summoner_spells.get("summonerSpellTwo") + + let spell2_id = summoner_spells + .get("summonerSpellTwo") .and_then(|s| s.get("spellId")) .and_then(|id| id.as_u64()) .unwrap_or_else(|| { // Fallback: direct spell2Id field - summoner_spells.get("spell2Id").and_then(|id| id.as_u64()).unwrap_or(0) + summoner_spells + .get("spell2Id") + .and_then(|id| id.as_u64()) + .unwrap_or(0) }) as u32; - - debug!("[METADATA] Summoner spells from live client: spell1={}, spell2={}", spell1_id, spell2_id); - + + debug!( + "[METADATA] Summoner spells from live client: spell1={}, spell2={}", + spell1_id, spell2_id + ); + if spell1_id > 0 || spell2_id > 0 { metadata.summoner_spells = Some(SummonerSpells { spell1_id, @@ -1199,10 +1232,13 @@ impl LqpClient { }); } } - + // Get summoner name from active player if not already set - if metadata.summoner_name.is_none() || metadata.summoner_name.as_ref().map_or(true, |n| n.is_empty()) { - metadata.summoner_name = active_player.get("summonerName") + if metadata.summoner_name.is_none() + || metadata.summoner_name.as_ref().is_none_or(|n| n.is_empty()) + { + metadata.summoner_name = active_player + .get("summonerName") .or_else(|| active_player.get("displayName")) .or_else(|| active_player.get("riotId")) .and_then(|n| n.as_str()) @@ -1220,10 +1256,20 @@ impl LqpClient { for team_key in &["teamOne", "teamTwo"] { if let Some(team) = game_data.get(team_key).and_then(|t| t.as_array()) { for player in team { - if player.get("puuid").and_then(|p| p.as_str()) == Some(local_puuid.as_str()) { - let spell1_id = player.get("spell1Id").and_then(|id| id.as_u64()).unwrap_or(0) as u32; - let spell2_id = player.get("spell2Id").and_then(|id| id.as_u64()).unwrap_or(0) as u32; - + if player.get("puuid").and_then(|p| p.as_str()) + == Some(local_puuid.as_str()) + { + let spell1_id = player + .get("spell1Id") + .and_then(|id| id.as_u64()) + .unwrap_or(0) + as u32; + let spell2_id = player + .get("spell2Id") + .and_then(|id| id.as_u64()) + .unwrap_or(0) + as u32; + if spell1_id > 0 || spell2_id > 0 { metadata.summoner_spells = Some(SummonerSpells { spell1_id, @@ -1232,9 +1278,15 @@ impl LqpClient { spell2_name: spell_id_to_name(spell2_id), }); } - - metadata.champion_id = player.get("championId").and_then(|id| id.as_u64()).map(|v| v as u32); - metadata.team = player.get("teamId").and_then(|id| id.as_u64()).map(|v| v as u32); + + metadata.champion_id = player + .get("championId") + .and_then(|id| id.as_u64()) + .map(|v| v as u32); + metadata.team = player + .get("teamId") + .and_then(|id| id.as_u64()) + .map(|v| v as u32); break; } } @@ -1263,19 +1315,24 @@ impl LqpClient { if let Some(arr) = player_list.as_array() { for player in arr { // Get summoner name - try multiple fields - let summoner_name = player.get("summonerName") + let summoner_name = player + .get("summonerName") .or_else(|| player.get("riotId")) .and_then(|n| n.as_str()) .unwrap_or(""); - + // Get team from teamId - let team = player.get("team").and_then(|t| t.as_u64()).map(|v| v as u32); - + let team = player + .get("team") + .and_then(|t| t.as_u64()) + .map(|v| v as u32); + // Get champion name - let champion_name = player.get("championName") + let champion_name = player + .get("championName") .and_then(|n| n.as_str()) .map(|s| s.to_string()); - + if let Some(puuid) = player.get("puuid").and_then(|p| p.as_str()) { players.push(super::PlayerIdentity { puuid: puuid.to_string(), @@ -1294,19 +1351,28 @@ impl LqpClient { if let Ok(session) = self.get_session().await { if let Some(game_data) = session.get("gameData") { for team_key in &["teamOne", "teamTwo"] { - let team_id = if *team_key == "teamOne" { 100u32 } else { 200u32 }; + let team_id = if *team_key == "teamOne" { + 100u32 + } else { + 200u32 + }; if let Some(team) = game_data.get(team_key).and_then(|t| t.as_array()) { for player in team { if let (Some(puuid), Some(summoner_name)) = ( player.get("puuid").and_then(|p| p.as_str()), player.get("summonerName").and_then(|n| n.as_str()), ) { - let champion_id = player.get("championId").and_then(|id| id.as_u64()).map(|v| v as u32); - let champion_name = champion_id.and_then(|id| champion_id_to_name(id)); + let champion_id = player + .get("championId") + .and_then(|id| id.as_u64()) + .map(|v| v as u32); + let champion_name = champion_id.and_then(champion_id_to_name); players.push(super::PlayerIdentity { puuid: puuid.to_string(), summoner_name: summoner_name.to_string(), - summoner_id: player.get("summonerId").and_then(|id| id.as_u64()), + summoner_id: player + .get("summonerId") + .and_then(|id| id.as_u64()), champion_name, team: Some(team_id), }); @@ -1324,24 +1390,35 @@ impl LqpClient { /// Fetch final items from end-of-game stats or live client data. pub async fn fetch_final_items(&self) -> Result> { info!("[ITEMS] Fetching final items..."); - + // First try live client data (available during game) match self.get_live_client_player_list().await { Ok(player_list) => { - info!("[ITEMS] Live client player list response: {:?}", player_list); + info!( + "[ITEMS] Live client player list response: {:?}", + player_list + ); if let Some(players) = player_list.as_array() { - info!("[ITEMS] Found {} players in live client data", players.len()); + info!( + "[ITEMS] Found {} players in live client data", + players.len() + ); // Find the local player (first player or one with matching puuid) for player in players { // Check if this is the local player - let is_local = player.get("isLocalPlayer").and_then(|l| l.as_bool()).unwrap_or(false); + let is_local = player + .get("isLocalPlayer") + .and_then(|l| l.as_bool()) + .unwrap_or(false); if is_local { info!("[ITEMS] Found local player in live client data"); if let Some(items) = player.get("items").and_then(|i| i.as_array()) { info!("[ITEMS] Items array has {} items", items.len()); let item_build = self.parse_items_from_live_client(items); if item_build.is_some() { - info!("[ITEMS] Successfully parsed items from live client data"); + info!( + "[ITEMS] Successfully parsed items from live client data" + ); return Ok(item_build); } } else { @@ -1360,7 +1437,7 @@ impl LqpClient { match self.get_game_stats().await { Ok(stats) => { info!("[ITEMS] Game stats response received"); - + // First try localPlayer field (items are just numbers in an array) if let Some(local_player) = stats.get("localPlayer") { info!("[ITEMS] Found localPlayer in game stats"); @@ -1373,18 +1450,26 @@ impl LqpClient { } } } - + // Try teams[].players[] structure if let Some(teams) = stats.get("teams").and_then(|t| t.as_array()) { info!("[ITEMS] Found {} teams in game stats", teams.len()); for team in teams { if let Some(players) = team.get("players").and_then(|p| p.as_array()) { for player in players { - let is_local = player.get("isLocalPlayer").and_then(|l| l.as_bool()).unwrap_or(false); + let is_local = player + .get("isLocalPlayer") + .and_then(|l| l.as_bool()) + .unwrap_or(false); if is_local { info!("[ITEMS] Found local player in teams[].players[]"); - if let Some(items) = player.get("items").and_then(|i| i.as_array()) { - info!("[ITEMS] Player items array has {} items", items.len()); + if let Some(items) = + player.get("items").and_then(|i| i.as_array()) + { + info!( + "[ITEMS] Player items array has {} items", + items.len() + ); let item_build = self.parse_items_from_game_stats(items); if item_build.is_some() { info!("[ITEMS] Successfully parsed items from teams[].players[]"); @@ -1396,10 +1481,13 @@ impl LqpClient { } } } - + // Try old players array structure (for backwards compatibility) if let Some(players) = stats.get("players").and_then(|p| p.as_array()) { - info!("[ITEMS] Found {} players in game stats (legacy)", players.len()); + info!( + "[ITEMS] Found {} players in game stats (legacy)", + players.len() + ); if let Some(player) = players.first() { if let Some(items) = player.get("items").and_then(|i| i.as_array()) { let item_build = self.parse_items_from_game_stats(items); @@ -1409,7 +1497,7 @@ impl LqpClient { } } } - + info!("[ITEMS] Could not find items in game stats structure"); } Err(e) => { @@ -1422,7 +1510,10 @@ impl LqpClient { } /// Parse items from live client data format. - fn parse_items_from_live_client(&self, items: &[serde_json::Value]) -> Option { + fn parse_items_from_live_client( + &self, + items: &[serde_json::Value], + ) -> Option { let mut item_list = Vec::new(); let mut trinket = None; @@ -1431,7 +1522,10 @@ impl LqpClient { if item_id > 0 { let item_info = ItemInfo { item_id: item_id as u32, - name: item.get("displayName").and_then(|n| n.as_str()).map(|s| s.to_string()), + name: item + .get("displayName") + .and_then(|n| n.as_str()) + .map(|s| s.to_string()), slot: slot as u32, }; diff --git a/record-daemon/src/lqp/mod.rs b/record-daemon/src/lqp/mod.rs index a7df3e7..6bb504d 100644 --- a/record-daemon/src/lqp/mod.rs +++ b/record-daemon/src/lqp/mod.rs @@ -8,7 +8,10 @@ mod client; mod events; pub use auth::{LockfileCredentials, LockfileWatcher}; -pub use client::{champion_id_to_name, spell_id_to_name, GameEndMetadata, GameflowPhase, LqpClient, PreGameMetadata}; +pub use client::{ + champion_id_to_name, spell_id_to_name, GameEndMetadata, GameflowPhase, LqpClient, + PreGameMetadata, +}; pub use events::{ ChampSelectStartInfo, ChampionPickInfo, DeathEvent, EventData, GameEndInfo, GameEvent, GameStartInfo, GameflowSession, InGameStats, ItemBuild, ItemInfo, KillEvent, MatchInfo, diff --git a/record-daemon/src/main.rs b/record-daemon/src/main.rs index 5b7658c..cfe2da3 100644 --- a/record-daemon/src/main.rs +++ b/record-daemon/src/main.rs @@ -443,33 +443,38 @@ impl Daemon { }); // Fetch player game metadata (runes, summoner spells, puuid) as fallback - let player_metadata = self.lqp_client.fetch_player_game_metadata().await.ok(); + let player_metadata = + self.lqp_client.fetch_player_game_metadata().await.ok(); let (fetched_puuid, fetched_runes, fetched_summoner_spells) = player_metadata.map_or((None, None, None), |m| { (m.puuid, m.runes, m.summoner_spells) }); - + // Use transition values first, then fall back to fetched values let final_puuid = transition_puuid.or(fetched_puuid); let final_runes = transition_runes.or(fetched_runes); - let final_summoner_spells = transition_summoner_spells.or(fetched_summoner_spells); - + let final_summoner_spells = + transition_summoner_spells.or(fetched_summoner_spells); + info!( "[EVENT_HANDLER] Final values: puuid={:?}, runes={:?}, summoner_spells={:?}", final_puuid, final_runes, final_summoner_spells ); // Fetch all players identities for puuid mapping - let all_players_identities = self.lqp_client.fetch_all_players_identities().await.ok(); + let all_players_identities = + self.lqp_client.fetch_all_players_identities().await.ok(); let all_players_info: Vec = - all_players_identities.unwrap_or_default().into_iter().map(|p| { - record_daemon::timeline::PlayerIdentityInfo { + all_players_identities + .unwrap_or_default() + .into_iter() + .map(|p| record_daemon::timeline::PlayerIdentityInfo { puuid: p.puuid, summoner_name: p.summoner_name, champion_name: p.champion_name, team: p.team, - } - }).collect(); + }) + .collect(); // Build game metadata for timeline let metadata_update = record_daemon::timeline::MetadataUpdate { @@ -501,14 +506,18 @@ impl Daemon { info!("[EVENT_HANDLER] start_recording completed successfully"); } } - StateTransition::GameEnded { game_end_info, final_items: _ } => { + StateTransition::GameEnded { + game_end_info, + final_items: _, + } => { info!( "[EVENT_HANDLER] GameEnded transition with info: {:?}", game_end_info ); // Fetch final items before stopping - let fetched_final_items = self.lqp_client.fetch_final_items().await.ok().flatten(); + let fetched_final_items = + self.lqp_client.fetch_final_items().await.ok().flatten(); // Convert GameEndInfo to GameEndMetadata if available let game_end_metadata = @@ -551,7 +560,10 @@ impl Daemon { game_end_metadata ); - if let Err(e) = self.stop_recording_with_metadata(game_end_metadata, fetched_final_items).await { + if let Err(e) = self + .stop_recording_with_metadata(game_end_metadata, fetched_final_items) + .await + { error!("[EVENT_HANDLER] Failed to stop recording: {}", e); // Don't propagate error - keep daemon running diff --git a/record-daemon/src/recording/encoder.rs b/record-daemon/src/recording/encoder.rs index 46f7ed2..c372d2a 100644 --- a/record-daemon/src/recording/encoder.rs +++ b/record-daemon/src/recording/encoder.rs @@ -298,7 +298,8 @@ pub fn detect_hardware_encoders() -> Vec { info!("[ENCODER_DETECT] Starting hardware encoder detection..."); - let mut capabilities = Vec::new(); + // Software encoding is always available= + let capabilities = vec![EncoderCapability::Software]; #[cfg(target_os = "windows")] { @@ -326,9 +327,6 @@ pub fn detect_hardware_encoders() -> Vec { } } - // Software encoding is always available - capabilities.push(EncoderCapability::Software); - info!("[ENCODER_DETECT] Detected encoders: {:?}", capabilities); capabilities diff --git a/record-daemon/src/state/machine.rs b/record-daemon/src/state/machine.rs index 287d68a..70afb98 100644 --- a/record-daemon/src/state/machine.rs +++ b/record-daemon/src/state/machine.rs @@ -235,21 +235,25 @@ impl DaemonStateMachine { GameEvent::GameStart(info) => { // Extract summoner spells from session data if available let summoner_spells = info.session.as_ref().and_then(|session| { - session.player_champion_selections.first().map(|selection| { - SummonerSpells { + session + .player_champion_selections + .first() + .map(|selection| SummonerSpells { spell1_id: selection.spell1_id, spell2_id: selection.spell2_id, spell1_name: crate::lqp::spell_id_to_name(selection.spell1_id), spell2_name: crate::lqp::spell_id_to_name(selection.spell2_id), - } - }) + }) }); - + // Extract puuid from session data if available let puuid = info.session.as_ref().and_then(|session| { - session.player_champion_selections.first().map(|selection| selection.puuid.clone()) + session + .player_champion_selections + .first() + .map(|selection| selection.puuid.clone()) }); - + Some(StateTransition::GameStarted { game_id: info.game_id, champion: info.champion.clone(),