#![allow(clippy::wildcard_imports)]
#[cfg(feature = "cloud")]
use super::*;
#[cfg(feature = "cloud-core")]
pub(crate) const EDGE_TRUSTED_CLIENT_TOKEN: &str = "6A5AA1D4EAFF4E9FB37E23D68491D6F4";
#[cfg(feature = "cloud-core")]
pub(crate) const EDGE_VOICE_LIST_URL: &str = "https://speech.platform.bing.com/consumer/speech/synthesize/readaloud/voices/list?trustedclienttoken=6A5AA1D4EAFF4E9FB37E23D68491D6F4";
#[cfg(feature = "cloud-core")]
pub(crate) const EDGE_DEFAULT_VOICE: &str = "en-US-AriaNeural";
#[cfg(feature = "cloud-core")]
pub(crate) const EDGE_USER_AGENT: &str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0";
#[cfg(feature = "cloud-core")]
pub(crate) const EDGE_ORIGIN: &str = "chrome-extension://jdiccldimpdaibmpdkjnbmckianbfold";
#[must_use]
#[cfg(feature = "cloud-core")]
pub fn edge_sec_ms_gec_at(unix_nanos: u128) -> String {
use sha2::{Digest, Sha256};
use std::fmt::Write;
#[allow(clippy::cast_possible_truncation)]
let filetime_ticks: u128 = unix_nanos / 100 + 116_444_736_000_000_000;
let rounded = filetime_ticks - (filetime_ticks % 3_000_000_000);
let input = format!("{rounded}{EDGE_TRUSTED_CLIENT_TOKEN}");
let hash = Sha256::digest(input.as_bytes());
let mut token = String::with_capacity(hash.len() * 2);
for b in &hash {
let _ = write!(token, "{b:02X}");
}
token
}
#[cfg(feature = "cloud")]
pub fn edge_sec_ms_gec() -> String {
let unix_nanos = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos();
edge_sec_ms_gec_at(unix_nanos)
}
#[cfg(feature = "cloud")]
#[cfg(feature = "cloud")]
pub(crate) type WsStream =
tungstenite::WebSocket<tungstenite::stream::MaybeTlsStream<std::net::TcpStream>>;
#[cfg(feature = "cloud")]
#[cfg(feature = "cloud")]
pub(crate) struct PooledConn {
socket: WsStream,
born_at: std::time::Instant,
}
#[cfg(feature = "cloud")]
static WS_POOL: std::sync::LazyLock<std::sync::Mutex<HashMap<String, Vec<PooledConn>>>> =
std::sync::LazyLock::new(|| std::sync::Mutex::new(HashMap::new()));
#[cfg(feature = "cloud")]
#[allow(clippy::duration_suboptimal_units)]
pub(crate) const WS_MAX_AGE: std::time::Duration = std::time::Duration::from_secs(3 * 60);
#[cfg(feature = "cloud")]
pub(crate) const WS_POOL_MAX_PER_URL: usize = 4;
#[cfg(feature = "cloud")]
#[cfg(feature = "cloud")]
pub(crate) fn ws_checkout(url: &str) -> Option<WsStream> {
let mut pool = WS_POOL.lock().ok()?;
let conns = pool.get_mut(url)?;
while let Some(conn) = conns.pop() {
if conn.born_at.elapsed() < WS_MAX_AGE {
return Some(conn.socket);
}
}
None
}
#[cfg(feature = "cloud")]
#[cfg(feature = "cloud")]
pub(crate) fn ws_checkin(url: String, socket: WsStream) {
let Ok(mut pool) = WS_POOL.lock() else {
return;
};
let bucket = pool.entry(url).or_default();
if bucket.len() < WS_POOL_MAX_PER_URL {
bucket.push(PooledConn {
socket,
born_at: std::time::Instant::now(),
});
}
}
#[must_use]
#[cfg(feature = "cloud-core")]
pub fn azure_ws_parse_word_boundary(
item: &serde_json::Value,
) -> Option<(&str, u64, u64, i32, i32)> {
let data = item.get("Data")?;
let offset_ticks = data
.get("Offset")
.and_then(serde_json::Value::as_i64)
.unwrap_or(0);
let duration_ticks = data
.get("Duration")
.and_then(serde_json::Value::as_i64)
.unwrap_or(0);
let word = data
.get("text")
.and_then(|v| v.as_object())
.and_then(|o| o.get("Text")?.as_str())
.or_else(|| {
data.get("Text")
.and_then(|v| v.as_object())
.and_then(|o| o.get("Text")?.as_str())
})
.or_else(|| data.get("text").and_then(|v| v.as_str()))
.filter(|s| !s.is_empty())?;
let text_obj = data.get("text").and_then(|v| v.as_object());
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
let char_offset = text_obj
.and_then(|o| o.get("Offset"))
.and_then(serde_json::Value::as_i64)
.map_or(-1, |v| v as i32);
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
let char_len = text_obj
.and_then(|o| o.get("Length"))
.and_then(serde_json::Value::as_i64)
.map_or(-1, |v| v as i32);
let offset_ms = (offset_ticks.max(0) / 10_000) as u64;
let duration_ms = (duration_ticks.max(0) / 10_000) as u64;
Some((word, offset_ms, duration_ms, char_offset, char_len))
}
#[must_use]
#[cfg(feature = "cloud-core")]
pub fn azure_ws_parse_viseme(item: &serde_json::Value) -> Option<(i32, f32)> {
let data = item.get("Data")?;
let viseme_id = data
.get("VisemeId")
.and_then(serde_json::Value::as_i64)
.unwrap_or(0) as i32;
let offset_ticks = data
.get("Offset")
.and_then(serde_json::Value::as_i64)
.unwrap_or(0);
#[allow(clippy::cast_precision_loss)]
let offset_sec = (offset_ticks as f64 / 10_000_000.0) as f32;
Some((viseme_id, offset_sec))
}