Compare commits
7 Commits
3592b86a94
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 953a920a23 | |||
| 146d7a5fb9 | |||
| 45aac067f8 | |||
| ba4ed63f4c | |||
| 384ccda515 | |||
| 1c1b9c4d1a | |||
| 642ad3e13a |
Generated
+1
-1
@@ -2387,7 +2387,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "record-daemon"
|
name = "record-daemon"
|
||||||
version = "0.1.0"
|
version = "0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "record-daemon"
|
name = "record-daemon"
|
||||||
version = "0.1.0"
|
version = "0.3.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "High-performance League of Legends recording daemon using libobs"
|
description = "High-performance League of Legends recording daemon using libobs"
|
||||||
authors = ["LeagueRecorder"]
|
authors = ["LeagueRecorder"]
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ pub struct LqpClient {
|
|||||||
shutdown: Arc<RwLock<bool>>,
|
shutdown: Arc<RwLock<bool>>,
|
||||||
/// Last emitted game ID for deduplication of GameStart events.
|
/// Last emitted game ID for deduplication of GameStart events.
|
||||||
last_emitted_game_id: Arc<RwLock<Option<u64>>>,
|
last_emitted_game_id: Arc<RwLock<Option<u64>>>,
|
||||||
|
/// WebSocket connection state (true if WebSocket is connected).
|
||||||
|
ws_connected: Arc<RwLock<bool>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LqpClient {
|
impl LqpClient {
|
||||||
@@ -50,6 +52,7 @@ impl LqpClient {
|
|||||||
http_client,
|
http_client,
|
||||||
shutdown: Arc::new(RwLock::new(false)),
|
shutdown: Arc::new(RwLock::new(false)),
|
||||||
last_emitted_game_id: Arc::new(RwLock::new(None)),
|
last_emitted_game_id: Arc::new(RwLock::new(None)),
|
||||||
|
ws_connected: Arc::new(RwLock::new(false)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,12 +66,20 @@ impl LqpClient {
|
|||||||
self.state.read().await.clone()
|
self.state.read().await.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if connected to League Client.
|
/// Check if connected to League Client (has valid credentials).
|
||||||
pub async fn is_connected(&self) -> bool {
|
pub async fn is_connected(&self) -> bool {
|
||||||
self.credentials.read().await.is_some()
|
self.credentials.read().await.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if WebSocket is connected.
|
||||||
|
pub async fn is_ws_connected(&self) -> bool {
|
||||||
|
*self.ws_connected.read().await
|
||||||
|
}
|
||||||
|
|
||||||
/// Connect to the League Client with the given credentials.
|
/// Connect to the League Client with the given credentials.
|
||||||
|
///
|
||||||
|
/// This only stores credentials and verifies basic connectivity.
|
||||||
|
/// The actual WebSocket connection is established in start_event_listener().
|
||||||
pub async fn connect(&self, creds: LockfileCredentials) -> Result<()> {
|
pub async fn connect(&self, creds: LockfileCredentials) -> Result<()> {
|
||||||
info!("Connecting to League Client at port {}", creds.port);
|
info!("Connecting to League Client at port {}", creds.port);
|
||||||
|
|
||||||
@@ -85,12 +96,13 @@ impl LqpClient {
|
|||||||
info!("Connected to League Client, current phase: {:?}", phase);
|
info!("Connected to League Client, current phase: {:?}", phase);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("Failed to verify connection: {}", e);
|
warn!("Failed to verify connection via REST API: {}", e);
|
||||||
// Still consider connected, WebSocket might work
|
// REST API might not be ready yet, but WebSocket could work
|
||||||
|
// Don't fail here - let start_event_listener() try the WebSocket
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch local player's puuid for champion extraction
|
// Fetch local player's puuid for champion extraction (best effort)
|
||||||
if let Ok(summoner) = self.get_summoner().await {
|
if let Ok(summoner) = self.get_summoner().await {
|
||||||
if let Some(puuid) = summoner.get("puuid").and_then(|p| p.as_str()) {
|
if let Some(puuid) = summoner.get("puuid").and_then(|p| p.as_str()) {
|
||||||
self.state.write().await.local_puuid = Some(puuid.to_string());
|
self.state.write().await.local_puuid = Some(puuid.to_string());
|
||||||
@@ -104,6 +116,7 @@ impl LqpClient {
|
|||||||
/// Disconnect from the League Client.
|
/// Disconnect from the League Client.
|
||||||
pub async fn disconnect(&self) {
|
pub async fn disconnect(&self) {
|
||||||
*self.shutdown.write().await = true;
|
*self.shutdown.write().await = true;
|
||||||
|
*self.ws_connected.write().await = false;
|
||||||
*self.credentials.write().await = None;
|
*self.credentials.write().await = None;
|
||||||
*self.state.write().await = ClientState::default();
|
*self.state.write().await = ClientState::default();
|
||||||
*self.last_emitted_game_id.write().await = None;
|
*self.last_emitted_game_id.write().await = None;
|
||||||
@@ -167,12 +180,16 @@ impl LqpClient {
|
|||||||
}
|
}
|
||||||
info!("All subscriptions sent");
|
info!("All subscriptions sent");
|
||||||
|
|
||||||
|
// Mark WebSocket as connected
|
||||||
|
*self.ws_connected.write().await = true;
|
||||||
|
|
||||||
// Clone references for the async block
|
// Clone references for the async block
|
||||||
let event_sender = self.event_sender.clone();
|
let event_sender = self.event_sender.clone();
|
||||||
let state = self.state.clone();
|
let state = self.state.clone();
|
||||||
let shutdown = self.shutdown.clone();
|
let shutdown = self.shutdown.clone();
|
||||||
let credentials = self.credentials.clone();
|
let credentials = self.credentials.clone();
|
||||||
let last_emitted_game_id = self.last_emitted_game_id.clone();
|
let last_emitted_game_id = self.last_emitted_game_id.clone();
|
||||||
|
let ws_connected = self.ws_connected.clone();
|
||||||
|
|
||||||
// Spawn the message handler
|
// Spawn the message handler
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
@@ -296,9 +313,10 @@ impl LqpClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear credentials on disconnect
|
// Clear connection state on disconnect
|
||||||
|
*ws_connected.write().await = false;
|
||||||
*credentials.write().await = None;
|
*credentials.write().await = None;
|
||||||
debug!("WebSocket listener ended");
|
info!("WebSocket listener ended");
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -503,12 +521,16 @@ impl LqpClient {
|
|||||||
info!("Starting live client event poller");
|
info!("Starting live client event poller");
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
|
// Small delay to ensure connection is stable
|
||||||
|
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
|
||||||
|
|
||||||
let mut last_event_id: Option<u64> = None;
|
let mut last_event_id: Option<u64> = None;
|
||||||
let mut poll_count = 0u32;
|
let mut poll_count = 0u32;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if *shutdown.read().await {
|
let is_shutdown = *shutdown.read().await;
|
||||||
info!("Live client event poller shutting down");
|
if is_shutdown {
|
||||||
|
info!("Live client event poller shutting down (shutdown flag is true)");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -206,16 +206,31 @@ impl Daemon {
|
|||||||
|
|
||||||
match watcher.check()? {
|
match watcher.check()? {
|
||||||
Some(true) => {
|
Some(true) => {
|
||||||
// Client started
|
// Client started - try to connect
|
||||||
info!("League Client detected");
|
info!("League Client detected");
|
||||||
self.state_machine
|
|
||||||
.transition(StateTransition::ClientStarted);
|
|
||||||
|
|
||||||
if let Some(creds) = watcher.credentials() {
|
if let Some(creds) = watcher.credentials() {
|
||||||
self.lqp_client.connect(creds.clone()).await?;
|
match self.lqp_client.connect(creds.clone()).await {
|
||||||
self.lqp_client.start_event_listener().await?;
|
Ok(()) => {
|
||||||
// Start polling for live client events (kills, deaths, objectives)
|
// Only transition to Monitoring after successful connection
|
||||||
self.lqp_client.start_live_client_event_poller().await;
|
self.state_machine
|
||||||
|
.transition(StateTransition::ClientStarted);
|
||||||
|
|
||||||
|
if let Err(e) = self.lqp_client.start_event_listener().await {
|
||||||
|
warn!("Failed to start event listener: {}", e);
|
||||||
|
// Still stay in Monitoring, will retry on next check
|
||||||
|
} else {
|
||||||
|
// Start polling for live client events (kills, deaths, objectives)
|
||||||
|
self.lqp_client.start_live_client_event_poller().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Failed to connect to League Client: {}", e);
|
||||||
|
// Don't transition to Monitoring - will retry on next check
|
||||||
|
// The lockfile watcher will return None on next check since
|
||||||
|
// credentials are already set, so we need to handle reconnection
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(false) => {
|
Some(false) => {
|
||||||
@@ -225,7 +240,34 @@ impl Daemon {
|
|||||||
.transition(StateTransition::ClientStopped);
|
.transition(StateTransition::ClientStopped);
|
||||||
self.lqp_client.disconnect().await;
|
self.lqp_client.disconnect().await;
|
||||||
}
|
}
|
||||||
None => {}
|
None => {
|
||||||
|
// No change in lockfile status
|
||||||
|
// Check if we need to reconnect (lockfile exists but WebSocket not connected)
|
||||||
|
if watcher.credentials().is_some() && !self.lqp_client.is_ws_connected().await {
|
||||||
|
info!("Lockfile exists but WebSocket not connected, attempting reconnect...");
|
||||||
|
if let Some(creds) = watcher.credentials() {
|
||||||
|
match self.lqp_client.connect(creds.clone()).await {
|
||||||
|
Ok(()) => {
|
||||||
|
// Transition to Monitoring if not already
|
||||||
|
if !self.state_machine.is_monitoring() {
|
||||||
|
self.state_machine
|
||||||
|
.transition(StateTransition::ClientStarted);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(e) = self.lqp_client.start_event_listener().await {
|
||||||
|
warn!("Failed to start event listener on reconnect: {}", e);
|
||||||
|
} else {
|
||||||
|
self.lqp_client.start_live_client_event_poller().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
info!("Reconnect attempt failed: {}", e);
|
||||||
|
// Will retry on next check
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tokio::time::sleep(poll_interval).await;
|
tokio::time::sleep(poll_interval).await;
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "tauri-app",
|
"name": "tauri-app",
|
||||||
"version": "0.1.0",
|
"version": "0.3.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "tauri-app",
|
"name": "tauri-app",
|
||||||
"version": "0.1.0",
|
"version": "0.3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tauri-apps/api": "^2",
|
"@tauri-apps/api": "^2",
|
||||||
"@tauri-apps/plugin-opener": "^2",
|
"@tauri-apps/plugin-opener": "^2",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "tauri-app",
|
"name": "tauri-app",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.1.0",
|
"version": "0.3.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
Generated
+75
-9
@@ -216,6 +216,17 @@ version = "1.1.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "auto-launch"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1f012b8cc0c850f34117ec8252a44418f2e34a2cf501de89e29b241ae5f79471"
|
||||||
|
dependencies = [
|
||||||
|
"dirs 4.0.0",
|
||||||
|
"thiserror 1.0.69",
|
||||||
|
"winreg 0.10.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@@ -740,7 +751,16 @@ version = "6.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d"
|
checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dirs-sys",
|
"dirs-sys 0.5.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs"
|
||||||
|
version = "4.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
|
||||||
|
dependencies = [
|
||||||
|
"dirs-sys 0.3.7",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -749,7 +769,18 @@ version = "6.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
|
checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dirs-sys",
|
"dirs-sys 0.5.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs-sys"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"redox_users 0.4.6",
|
||||||
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -760,7 +791,7 @@ checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"option-ext",
|
"option-ext",
|
||||||
"redox_users",
|
"redox_users 0.5.2",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -872,7 +903,7 @@ dependencies = [
|
|||||||
"rustc_version",
|
"rustc_version",
|
||||||
"toml 0.9.12+spec-1.1.0",
|
"toml 0.9.12+spec-1.1.0",
|
||||||
"vswhom",
|
"vswhom",
|
||||||
"winreg",
|
"winreg 0.55.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1962,7 +1993,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "leaguerecorder"
|
name = "leaguerecorder"
|
||||||
version = "0.1.0"
|
version = "0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"directories",
|
"directories",
|
||||||
@@ -1971,6 +2002,7 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"tauri",
|
"tauri",
|
||||||
"tauri-build",
|
"tauri-build",
|
||||||
|
"tauri-plugin-autostart",
|
||||||
"tauri-plugin-opener",
|
"tauri-plugin-opener",
|
||||||
"tokio",
|
"tokio",
|
||||||
"uuid",
|
"uuid",
|
||||||
@@ -2990,6 +3022,17 @@ dependencies = [
|
|||||||
"bitflags 2.11.0",
|
"bitflags 2.11.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_users"
|
||||||
|
version = "0.4.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.2.17",
|
||||||
|
"libredox",
|
||||||
|
"thiserror 1.0.69",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_users"
|
name = "redox_users"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
@@ -3770,7 +3813,7 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
"cookie",
|
"cookie",
|
||||||
"dirs",
|
"dirs 6.0.0",
|
||||||
"dunce",
|
"dunce",
|
||||||
"embed_plist",
|
"embed_plist",
|
||||||
"getrandom 0.3.4",
|
"getrandom 0.3.4",
|
||||||
@@ -3821,7 +3864,7 @@ checksum = "4bbc990d1dbf57a8e1c7fa2327f2a614d8b757805603c1b9ba5c81bade09fd4d"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cargo_toml",
|
"cargo_toml",
|
||||||
"dirs",
|
"dirs 6.0.0",
|
||||||
"glob",
|
"glob",
|
||||||
"heck 0.5.0",
|
"heck 0.5.0",
|
||||||
"json-patch",
|
"json-patch",
|
||||||
@@ -3893,6 +3936,20 @@ dependencies = [
|
|||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tauri-plugin-autostart"
|
||||||
|
version = "2.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "459383cebc193cdd03d1ba4acc40f2c408a7abce419d64bdcd2d745bc2886f70"
|
||||||
|
dependencies = [
|
||||||
|
"auto-launch",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"tauri",
|
||||||
|
"tauri-plugin",
|
||||||
|
"thiserror 2.0.18",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin-opener"
|
name = "tauri-plugin-opener"
|
||||||
version = "2.5.3"
|
version = "2.5.3"
|
||||||
@@ -4345,7 +4402,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "a5e85aa143ceb072062fc4d6356c1b520a51d636e7bc8e77ec94be3608e5e80c"
|
checksum = "a5e85aa143ceb072062fc4d6356c1b520a51d636e7bc8e77ec94be3608e5e80c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"dirs",
|
"dirs 6.0.0",
|
||||||
"libappindicator",
|
"libappindicator",
|
||||||
"muda",
|
"muda",
|
||||||
"objc2",
|
"objc2",
|
||||||
@@ -5297,6 +5354,15 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winreg"
|
||||||
|
version = "0.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winreg"
|
name = "winreg"
|
||||||
version = "0.55.0"
|
version = "0.55.0"
|
||||||
@@ -5411,7 +5477,7 @@ dependencies = [
|
|||||||
"block2",
|
"block2",
|
||||||
"cookie",
|
"cookie",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"dirs",
|
"dirs 6.0.0",
|
||||||
"dom_query",
|
"dom_query",
|
||||||
"dpi",
|
"dpi",
|
||||||
"dunce",
|
"dunce",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "leaguerecorder"
|
name = "leaguerecorder"
|
||||||
version = "0.1.0"
|
version = "0.3.0"
|
||||||
description = "A Tauri App"
|
description = "A Tauri App"
|
||||||
authors = ["you"]
|
authors = ["you"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
@@ -20,6 +20,7 @@ tauri-build = { version = "2", features = [] }
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
tauri = { version = "2", features = ["protocol-asset"] }
|
tauri = { version = "2", features = ["protocol-asset"] }
|
||||||
tauri-plugin-opener = "2"
|
tauri-plugin-opener = "2"
|
||||||
|
tauri-plugin-autostart = "2"
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
|
|||||||
@@ -5,6 +5,9 @@
|
|||||||
"windows": ["main"],
|
"windows": ["main"],
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"core:default",
|
"core:default",
|
||||||
"opener:default"
|
"opener:default",
|
||||||
|
"autostart:allow-enable",
|
||||||
|
"autostart:allow-disable",
|
||||||
|
"autostart:allow-is-enabled"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -395,6 +395,10 @@ fn get_video_metadata(video_path: String) -> Result<Value, String> {
|
|||||||
pub fn run() {
|
pub fn run() {
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
.plugin(tauri_plugin_opener::init())
|
.plugin(tauri_plugin_opener::init())
|
||||||
|
.plugin(tauri_plugin_autostart::init(
|
||||||
|
tauri_plugin_autostart::MacosLauncher::LaunchAgent,
|
||||||
|
Some(vec!["--hidden"]),
|
||||||
|
))
|
||||||
.setup(|app| {
|
.setup(|app| {
|
||||||
// Auto-start the record-daemon on app launch.
|
// Auto-start the record-daemon on app launch.
|
||||||
// We spawn a background task so the UI isn't blocked while the
|
// We spawn a background task so the UI isn't blocked while the
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "leaguerecorder",
|
"productName": "leaguerecorder",
|
||||||
"version": "0.2.0",
|
"version": "0.3.0",
|
||||||
"identifier": "v.leaguerecorder",
|
"identifier": "v.leaguerecorder",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "npm run dev",
|
"beforeDevCommand": "npm run dev",
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ const daemonStatus = ref<DaemonStatusResponse | null>(null);
|
|||||||
const encoders = ref<EncodersResponse | null>(null);
|
const encoders = ref<EncodersResponse | null>(null);
|
||||||
const daemonRunning = ref(false);
|
const daemonRunning = ref(false);
|
||||||
const statusPolling = ref<ReturnType<typeof setInterval> | null>(null);
|
const statusPolling = ref<ReturnType<typeof setInterval> | null>(null);
|
||||||
|
const autostartEnabled = ref(false);
|
||||||
|
const autostartLoading = ref(false);
|
||||||
|
|
||||||
// Active settings tab
|
// Active settings tab
|
||||||
type SettingsTab = "video" | "output" | "audio" | "daemon";
|
type SettingsTab = "video" | "output" | "audio" | "daemon";
|
||||||
@@ -97,6 +99,35 @@ async function refreshStatus() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Autostart management
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async function checkAutostart() {
|
||||||
|
try {
|
||||||
|
autostartEnabled.value = await invoke<boolean>("plugin:autostart|is_enabled");
|
||||||
|
} catch {
|
||||||
|
autostartEnabled.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function toggleAutostart() {
|
||||||
|
autostartLoading.value = true;
|
||||||
|
try {
|
||||||
|
if (autostartEnabled.value) {
|
||||||
|
await invoke("plugin:autostart|disable");
|
||||||
|
autostartEnabled.value = false;
|
||||||
|
} else {
|
||||||
|
await invoke("plugin:autostart|enable");
|
||||||
|
autostartEnabled.value = true;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
error.value = `Failed to ${autostartEnabled.value ? 'disable' : 'enable'} autostart: ${e}`;
|
||||||
|
} finally {
|
||||||
|
autostartLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Save settings
|
// Save settings
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -240,6 +271,7 @@ const statusColor = computed(() => {
|
|||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await loadSettings();
|
await loadSettings();
|
||||||
|
await checkAutostart();
|
||||||
// Poll daemon status every 3 seconds
|
// Poll daemon status every 3 seconds
|
||||||
statusPolling.value = setInterval(refreshStatus, 3000);
|
statusPolling.value = setInterval(refreshStatus, 3000);
|
||||||
});
|
});
|
||||||
@@ -651,6 +683,23 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
<!-- Daemon tab -->
|
<!-- Daemon tab -->
|
||||||
<div v-if="activeTab === 'daemon'" class="tab-content">
|
<div v-if="activeTab === 'daemon'" class="tab-content">
|
||||||
|
<section class="settings-section">
|
||||||
|
<h3>Startup</h3>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">Launch at Startup</label>
|
||||||
|
<label class="toggle">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
:checked="autostartEnabled"
|
||||||
|
:disabled="autostartLoading"
|
||||||
|
@change="toggleAutostart"
|
||||||
|
/>
|
||||||
|
<span class="toggle-slider"></span>
|
||||||
|
</label>
|
||||||
|
<span class="form-hint">Automatically launch League Recorder when you log in to Windows</span>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section class="settings-section">
|
<section class="settings-section">
|
||||||
<h3>Daemon Behavior</h3>
|
<h3>Daemon Behavior</h3>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|||||||
@@ -755,7 +755,7 @@ export const DEFAULT_HIGHLIGHT_SETTINGS: HighlightSettings = {
|
|||||||
buffer_before: 10,
|
buffer_before: 10,
|
||||||
buffer_after: 3,
|
buffer_after: 3,
|
||||||
min_duration: 5,
|
min_duration: 5,
|
||||||
included_types: ["kill", "death", "multi_kill", "trade_kill"],
|
included_types: ["kill", "death", "multi_kill", "trade_kill", "assist"],
|
||||||
merge_overlapping: true,
|
merge_overlapping: true,
|
||||||
merge_gap_secs: 5,
|
merge_gap_secs: 5,
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user