Updated the ytmusic cookies situation
This commit is contained in:
107
src/cookie_refresh.rs
Normal file
107
src/cookie_refresh.rs
Normal file
@@ -0,0 +1,107 @@
|
||||
//! Background task that periodically refreshes YouTube cookies via headless Firefox.
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Stdio;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use tokio::process::Command;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::config::AppConfig;
|
||||
|
||||
/// Spawn the cookie refresh background loop.
|
||||
///
|
||||
/// This task runs forever, sleeping for `cookie_refresh_hours` between refreshes.
|
||||
/// It reads the current config on each iteration so changes take effect without restart.
|
||||
pub fn spawn(config: Arc<RwLock<AppConfig>>) {
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
let (enabled, hours) = {
|
||||
let cfg = config.read().await;
|
||||
(
|
||||
cfg.download.cookie_refresh_enabled,
|
||||
cfg.download.cookie_refresh_hours.max(1),
|
||||
)
|
||||
};
|
||||
|
||||
// Sleep for the configured interval
|
||||
tokio::time::sleep(Duration::from_secs(u64::from(hours) * 3600)).await;
|
||||
|
||||
if !enabled {
|
||||
continue;
|
||||
}
|
||||
|
||||
let profile_dir = shanty_config::data_dir().join("firefox-profile");
|
||||
let cookies_path = shanty_config::data_dir().join("cookies.txt");
|
||||
|
||||
if !profile_dir.exists() {
|
||||
tracing::warn!(
|
||||
"cookie refresh skipped: no Firefox profile at {}",
|
||||
profile_dir.display()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
tracing::info!("starting cookie refresh");
|
||||
|
||||
match run_refresh(&profile_dir, &cookies_path).await {
|
||||
Ok(msg) => tracing::info!("cookie refresh complete: {msg}"),
|
||||
Err(e) => tracing::error!("cookie refresh failed: {e}"),
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async fn run_refresh(profile_dir: &Path, cookies_path: &Path) -> Result<String, String> {
|
||||
let script = find_script()?;
|
||||
|
||||
let output = Command::new("python3")
|
||||
.arg(&script)
|
||||
.args([
|
||||
"refresh",
|
||||
&profile_dir.to_string_lossy(),
|
||||
&cookies_path.to_string_lossy(),
|
||||
])
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.output()
|
||||
.await
|
||||
.map_err(|e| format!("failed to run cookie_manager.py: {e}"))?;
|
||||
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
return Err(format!("cookie_manager.py failed: {stderr}"));
|
||||
}
|
||||
|
||||
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
|
||||
|
||||
// Check for error in JSON response
|
||||
if let Ok(v) = serde_json::from_str::<serde_json::Value>(&stdout)
|
||||
&& v.get("status").and_then(|s| s.as_str()) == Some("error")
|
||||
{
|
||||
let err = v.get("error").and_then(|e| e.as_str()).unwrap_or("unknown");
|
||||
return Err(err.to_string());
|
||||
}
|
||||
|
||||
Ok(stdout)
|
||||
}
|
||||
|
||||
fn find_script() -> Result<PathBuf, String> {
|
||||
let candidates = [
|
||||
std::env::current_exe()
|
||||
.ok()
|
||||
.and_then(|p| p.parent().map(|d| d.join("cookie_manager.py"))),
|
||||
Some(PathBuf::from("/usr/share/shanty/cookie_manager.py")),
|
||||
Some(PathBuf::from("/usr/local/share/shanty/cookie_manager.py")),
|
||||
Some(PathBuf::from("shanty-dl/scripts/cookie_manager.py")),
|
||||
];
|
||||
|
||||
for candidate in candidates.into_iter().flatten() {
|
||||
if candidate.exists() {
|
||||
return Ok(candidate);
|
||||
}
|
||||
}
|
||||
|
||||
Err("cookie_manager.py not found".into())
|
||||
}
|
||||
Reference in New Issue
Block a user