refactor: clean up daemon and CLI duplication
Reduce repeated adapter dispatch, CLI action rendering, and config save flows while keeping the current Roku behavior and docs aligned with the known secret-menu limitations.
This commit is contained in:
+65
-46
@@ -7,7 +7,10 @@ use tokio::fs;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
adapters::{AppInfo, Device, DeviceInfo, DeviceState, TvAdapter, TvKey, roku::RokuAdapter},
|
||||
adapters::{
|
||||
AppInfo, Device, DeviceInfo, DeviceState, TvAdapter, TvKey,
|
||||
roku::{RokuAdapter, RokuKeyMode},
|
||||
},
|
||||
daemon::config::TvctlConfig,
|
||||
};
|
||||
|
||||
@@ -196,8 +199,12 @@ impl AdapterRegistry {
|
||||
(!config.dev.roku_username.is_empty()).then(|| config.dev.roku_username.clone());
|
||||
let password =
|
||||
(!config.dev.roku_password.is_empty()).then(|| config.dev.roku_password.clone());
|
||||
let key_mode = RokuKeyMode::from_config(
|
||||
&config.remote.roku_key_mode,
|
||||
config.remote.roku_press_duration_ms,
|
||||
);
|
||||
Self {
|
||||
roku: RokuAdapter::with_dev_credentials(username, password),
|
||||
roku: RokuAdapter::with_config(username, password, key_mode),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,10 +215,8 @@ impl AdapterRegistry {
|
||||
|
||||
/// Discover candidate devices for one platform.
|
||||
pub async fn discover(&self, platform: &str) -> anyhow::Result<Vec<DeviceInfo>> {
|
||||
match platform {
|
||||
"roku" => Ok(self.roku.discover().await?),
|
||||
other => anyhow::bail!("unsupported platform '{other}'"),
|
||||
}
|
||||
self.with_platform(platform, |roku| Box::pin(roku.discover()))
|
||||
.await
|
||||
}
|
||||
|
||||
/// Return true when a platform is supported.
|
||||
@@ -226,83 +231,97 @@ impl AdapterRegistry {
|
||||
address: IpAddr,
|
||||
port: Option<u16>,
|
||||
) -> anyhow::Result<DeviceInfo> {
|
||||
match platform {
|
||||
"roku" => Ok(self
|
||||
.roku
|
||||
.probe_device(address, port.unwrap_or(8060))
|
||||
.await?),
|
||||
other => anyhow::bail!("unsupported platform '{other}'"),
|
||||
}
|
||||
self.with_platform(platform, |roku| {
|
||||
Box::pin(roku.probe_device(address, port.unwrap_or(8060)))
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
/// Return apps from a concrete device using its platform adapter.
|
||||
pub async fn list_apps(&self, device: &Device) -> anyhow::Result<Vec<AppInfo>> {
|
||||
match device.platform.as_str() {
|
||||
"roku" => Ok(self.roku.list_apps(device).await?),
|
||||
other => anyhow::bail!("unsupported platform '{other}'"),
|
||||
}
|
||||
self.with_device(device, |roku, device| Box::pin(roku.list_apps(device)))
|
||||
.await
|
||||
}
|
||||
|
||||
/// Fetch the current state for a concrete device.
|
||||
pub async fn state(&self, device: &Device) -> anyhow::Result<DeviceState> {
|
||||
match device.platform.as_str() {
|
||||
"roku" => Ok(self.roku.state(device).await?),
|
||||
other => anyhow::bail!("unsupported platform '{other}'"),
|
||||
}
|
||||
self.with_device(device, |roku, device| Box::pin(roku.state(device)))
|
||||
.await
|
||||
}
|
||||
|
||||
/// Launch an app on a concrete device.
|
||||
pub async fn launch(&self, device: &Device, app: &str) -> anyhow::Result<()> {
|
||||
match device.platform.as_str() {
|
||||
"roku" => Ok(self.roku.launch(device, app).await?),
|
||||
other => anyhow::bail!("unsupported platform '{other}'"),
|
||||
}
|
||||
let app = app.to_string();
|
||||
self.with_device(device, move |roku, device| {
|
||||
Box::pin(async move { roku.launch(device, &app).await })
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
/// Stop the currently running app on a concrete device.
|
||||
pub async fn stop_app(&self, device: &Device) -> anyhow::Result<()> {
|
||||
match device.platform.as_str() {
|
||||
"roku" => Ok(self.roku.stop_app(device).await?),
|
||||
other => anyhow::bail!("unsupported platform '{other}'"),
|
||||
}
|
||||
self.with_device(device, |roku, device| Box::pin(roku.stop_app(device)))
|
||||
.await
|
||||
}
|
||||
|
||||
/// Send a single normalized key to a concrete device.
|
||||
pub async fn key(&self, device: &Device, key: TvKey) -> anyhow::Result<()> {
|
||||
match device.platform.as_str() {
|
||||
"roku" => Ok(self.roku.key(device, key).await?),
|
||||
other => anyhow::bail!("unsupported platform '{other}'"),
|
||||
}
|
||||
self.with_device(device, |roku, device| Box::pin(roku.key(device, key)))
|
||||
.await
|
||||
}
|
||||
|
||||
/// Send a normalized key sequence to a concrete device.
|
||||
pub async fn sequence(&self, device: &Device, keys: Vec<TvKey>) -> anyhow::Result<()> {
|
||||
match device.platform.as_str() {
|
||||
"roku" => Ok(self.roku.sequence(device, keys).await?),
|
||||
other => anyhow::bail!("unsupported platform '{other}'"),
|
||||
}
|
||||
self.with_device(device, |roku, device| Box::pin(roku.sequence(device, keys)))
|
||||
.await
|
||||
}
|
||||
|
||||
/// Install a development package on a concrete device.
|
||||
pub async fn dev_install(&self, device: &Device, zip: &[u8]) -> anyhow::Result<()> {
|
||||
match device.platform.as_str() {
|
||||
"roku" => Ok(self.roku.dev_install(device, zip).await?),
|
||||
other => anyhow::bail!("unsupported platform '{other}'"),
|
||||
}
|
||||
let zip = zip.to_vec();
|
||||
self.with_device(device, move |roku, device| {
|
||||
Box::pin(async move { roku.dev_install(device, &zip).await })
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
/// Reload the active development package on a concrete device.
|
||||
pub async fn dev_reload(&self, device: &Device) -> anyhow::Result<()> {
|
||||
match device.platform.as_str() {
|
||||
"roku" => Ok(self.roku.dev_reload(device).await?),
|
||||
other => anyhow::bail!("unsupported platform '{other}'"),
|
||||
}
|
||||
self.with_device(device, |roku, device| Box::pin(roku.dev_reload(device)))
|
||||
.await
|
||||
}
|
||||
|
||||
/// Fetch development logs from a concrete device.
|
||||
pub async fn dev_logs(&self, device: &Device) -> anyhow::Result<Vec<String>> {
|
||||
self.with_device(device, |roku, device| Box::pin(roku.dev_logs(device)))
|
||||
.await
|
||||
}
|
||||
|
||||
async fn with_platform<T, F>(&self, platform: &str, call: F) -> anyhow::Result<T>
|
||||
where
|
||||
F: for<'a> FnOnce(
|
||||
&'a RokuAdapter,
|
||||
) -> std::pin::Pin<
|
||||
Box<dyn std::future::Future<Output = crate::adapters::Result<T>> + 'a>,
|
||||
>,
|
||||
{
|
||||
match platform {
|
||||
"roku" => Ok(call(&self.roku).await?),
|
||||
other => anyhow::bail!("unsupported platform '{other}'"),
|
||||
}
|
||||
}
|
||||
|
||||
async fn with_device<T, F>(&self, device: &Device, call: F) -> anyhow::Result<T>
|
||||
where
|
||||
F: for<'a> FnOnce(
|
||||
&'a RokuAdapter,
|
||||
&'a Device,
|
||||
) -> std::pin::Pin<
|
||||
Box<dyn std::future::Future<Output = crate::adapters::Result<T>> + 'a>,
|
||||
>,
|
||||
{
|
||||
match device.platform.as_str() {
|
||||
"roku" => Ok(self.roku.dev_logs(device).await?),
|
||||
"roku" => Ok(call(&self.roku, device).await?),
|
||||
other => anyhow::bail!("unsupported platform '{other}'"),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user