From ffe3091b226a264f23af07913c9a4f63ec23be9e Mon Sep 17 00:00:00 2001 From: Siirko Date: Sun, 7 Jan 2024 12:50:29 +0100 Subject: [PATCH 1/4] added logic to check ytdlp is installed and if not dl it into app dir --- src-tauri/Cargo.lock | 1 + src-tauri/Cargo.toml | 2 +- src-tauri/src/main.rs | 23 +++++++++++++++++++++++ src-tauri/src/music/ytdlp_wrapper.rs | 16 ++++++++++++++++ 4 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index cb109af..2cde091 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -5365,6 +5365,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc791a982b545c7392ea685957d39bbac4a716667b46d72a1d4c384065205ab9" dependencies = [ "log", + "reqwest", "serde", "serde_json", "tokio", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index d210b70..75cd84d 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -30,7 +30,7 @@ env_logger = "0.10.0" log="0.4.20" tauri-plugin-log = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "dev", features = ["colored"] } futures = "0.3.29" -youtube_dl = { version = "0.9.0", features = ["tokio"] } +youtube_dl = { version = "0.9.0", features = ["tokio", "downloader-native-tls"] } tokio = { version = "1.20.1", features = ["full"] } base64 = { version = "0.21.5" } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index c23a394..367b52e 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -14,6 +14,7 @@ mod db; mod music; use crate::{api::*, music::player::Player}; use db::{database, state::DbState}; +use log::info; use music::{ async_process::{async_process_model, AsyncProcInputTx}, player::MusicPlayer, @@ -99,12 +100,34 @@ fn main() { music::ytdlp_wrapper::download_audio_from_links, ]) .setup(|app| { + // init database let handle = app.handle(); let app_state: State = handle.state(); let db = database::initialize_database(&handle).expect("Database initialize should succeed"); *app_state.db.lock().unwrap() = Some(db); + // check if ytdlp is installed + match music::ytdlp_wrapper::check_ytdl_bin() { + Ok(_) => {} + Err(_) => { + let app_dir = handle + .path_resolver() + .app_data_dir() + .expect("The app data directory should exist."); + info!( + "yt-dlp not found, downloading it at {:?}", + app_dir.as_os_str() + ); + tauri::async_runtime::spawn(async move { + let path = youtube_dl::download_yt_dlp(app_dir.as_os_str()) + .await + .unwrap(); + assert!(path.is_file(), "downloaded file should exist"); + }); + } + } + tauri::async_runtime::spawn(async move { async_process_model(async_process_input_rx, async_process_output_tx).await }); diff --git a/src-tauri/src/music/ytdlp_wrapper.rs b/src-tauri/src/music/ytdlp_wrapper.rs index 756f386..23b1b34 100644 --- a/src-tauri/src/music/ytdlp_wrapper.rs +++ b/src-tauri/src/music/ytdlp_wrapper.rs @@ -1,3 +1,5 @@ +use std::process::Command; + use futures::StreamExt; use youtube_dl::{Error, YoutubeDl, YoutubeDlOutput}; @@ -84,6 +86,20 @@ async fn download_audio(url: &str, path: &str) -> Result .run_async().await } +pub fn check_ytdl_bin() -> Result { + // we check if user has yt-dlp installed + match Command::new("yt-dlp").arg("--version").output() { + Ok(_) => Ok(true), + Err(e) => { + if e.kind() == std::io::ErrorKind::NotFound { + Err("yt-dlp not found".to_string()) + } else { + Err(e.to_string()) + } + } + } +} + #[tauri::command] pub async fn download_audio_from_links( url: String, From 1c0c1c30ad3e99da191cecd4f7bdfe7c358fa8ca Mon Sep 17 00:00:00 2001 From: Siirko Date: Sun, 7 Jan 2024 13:04:11 +0100 Subject: [PATCH 2/4] make clippy happy --- src-tauri/src/music/audio.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-tauri/src/music/audio.rs b/src-tauri/src/music/audio.rs index 0729614..0dd6863 100644 --- a/src-tauri/src/music/audio.rs +++ b/src-tauri/src/music/audio.rs @@ -173,7 +173,7 @@ pub fn create_audio(path: PathBuf, format: FileFormat) -> _Audio { None => tagged_file.first_tag().unwrap_or(&tmp), }; // picture can be None - let picture = tag.pictures().get(0); + let picture = tag.pictures().first(); info!("{:?}", picture); let cover = match picture { Some(picture) => general_purpose::STANDARD_NO_PAD.encode(picture.data()), From 80a9eb30d2baaf0def09a4d87a2b085f58e72792 Mon Sep 17 00:00:00 2001 From: Siirko Date: Tue, 9 Jan 2024 19:21:39 +0100 Subject: [PATCH 3/4] yt-dlp downloaded if not found (work only for linux) --- src-tauri/Cargo.lock | 1 + src-tauri/Cargo.toml | 1 + src-tauri/src/main.rs | 18 ++++++++---- src-tauri/src/music/ytdlp_wrapper.rs | 42 ++++++++++++++++++++++++---- 4 files changed, 50 insertions(+), 12 deletions(-) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 2cde091..3322478 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -110,6 +110,7 @@ dependencies = [ "env_logger", "file-format", "futures", + "lazy_static", "lofty", "log", "rand 0.8.5", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 75cd84d..c6d1298 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -28,6 +28,7 @@ rusqlite = { version = "0.29.0", features = ["bundled"] } rand = "0.8.5" env_logger = "0.10.0" log="0.4.20" +lazy_static = "1.4.0" tauri-plugin-log = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "dev", features = ["colored"] } futures = "0.3.29" youtube_dl = { version = "0.9.0", features = ["tokio", "downloader-native-tls"] } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 367b52e..1810bb3 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -12,7 +12,10 @@ const LOG_TARGETS: [LogTarget; 2] = [LogTarget::Stdout, LogTarget::LogDir]; use std::sync::Arc; mod db; mod music; -use crate::{api::*, music::player::Player}; +use crate::{ + api::*, + music::{player::Player, ytdlp_wrapper}, +}; use db::{database, state::DbState}; use log::info; use music::{ @@ -108,13 +111,13 @@ fn main() { *app_state.db.lock().unwrap() = Some(db); // check if ytdlp is installed - match music::ytdlp_wrapper::check_ytdl_bin() { + let app_dir = handle + .path_resolver() + .app_data_dir() + .expect("The app data directory should exist."); + match music::ytdlp_wrapper::check_ytdl_bin(app_dir.as_os_str()) { Ok(_) => {} Err(_) => { - let app_dir = handle - .path_resolver() - .app_data_dir() - .expect("The app data directory should exist."); info!( "yt-dlp not found, downloading it at {:?}", app_dir.as_os_str() @@ -124,6 +127,9 @@ fn main() { .await .unwrap(); assert!(path.is_file(), "downloaded file should exist"); + info!("yt-dlp downloaded at {:?}", path); + let mut str = ytdlp_wrapper::YT_DLP_BIN_PATH.write().await; + *str = path.to_str().unwrap().to_string(); }); } } diff --git a/src-tauri/src/music/ytdlp_wrapper.rs b/src-tauri/src/music/ytdlp_wrapper.rs index 23b1b34..ce687b4 100644 --- a/src-tauri/src/music/ytdlp_wrapper.rs +++ b/src-tauri/src/music/ytdlp_wrapper.rs @@ -1,4 +1,7 @@ -use std::process::Command; +use lazy_static::lazy_static; +use log::info; +use std::{ffi::OsStr, process::Command}; +use tokio::sync::RwLock; use futures::StreamExt; use youtube_dl::{Error, YoutubeDl, YoutubeDlOutput}; @@ -12,6 +15,10 @@ pub enum ResultFromDownload { Error(ErrorItem), } +lazy_static! { + pub static ref YT_DLP_BIN_PATH: RwLock = RwLock::new("yt-dlp".to_string()); +} + #[derive(serde::Serialize, Clone)] pub struct TotalItem { pub total: usize, @@ -36,9 +43,10 @@ impl std::fmt::Display for MusicItem { } } -fn retrieve_possible_links(url: &str) -> Result, Error> { +fn retrieve_possible_links(url: &str, yt_dlp_path: &str) -> Result, Error> { let mut links: Vec = Vec::new(); let retrieved = YoutubeDl::new(url) + .youtube_dl_path(yt_dlp_path) .socket_timeout("15") .flat_playlist(true) .run(); @@ -66,7 +74,9 @@ fn retrieve_possible_links(url: &str) -> Result, Error> { } async fn download_audio(url: &str, path: &str) -> Result { + let yt_dlp_path = YT_DLP_BIN_PATH.read().await; YoutubeDl::new(url) + .youtube_dl_path(yt_dlp_path.as_str()) .socket_timeout("15") .extract_audio(true) .extra_arg("-x") @@ -86,13 +96,32 @@ async fn download_audio(url: &str, path: &str) -> Result .run_async().await } -pub fn check_ytdl_bin() -> Result { +pub fn check_ytdl_bin(app_dir_path: &OsStr) -> Result { // we check if user has yt-dlp installed + info!("Checking yt-dlp binary in PATH and at {:?}", app_dir_path); match Command::new("yt-dlp").arg("--version").output() { - Ok(_) => Ok(true), + Ok(_) => { + info!("yt-dlp found in PATH"); + Ok(true) + } Err(e) => { if e.kind() == std::io::ErrorKind::NotFound { - Err("yt-dlp not found".to_string()) + match Command::new(format!("{}/yt-dlp", app_dir_path.to_str().unwrap())) + .arg("--version") + .output() + { + Ok(_) => { + info!("yt-dlp found at {:?}", app_dir_path); + Ok(true) + } + Err(e) => { + if e.kind() == std::io::ErrorKind::NotFound { + Err("yt-dlp not found".to_string()) + } else { + Err(e.to_string()) + } + } + } } else { Err(e.to_string()) } @@ -106,7 +135,8 @@ pub async fn download_audio_from_links( path: String, state: tauri::State<'_, AsyncProcInputTx>, ) -> Result<(), String> { - let res_links = retrieve_possible_links(&url); + let yt_dlp_path = YT_DLP_BIN_PATH.read().await; + let res_links = retrieve_possible_links(&url, yt_dlp_path.as_str()); let links = match res_links { Ok(links) => links, Err(e) => { From 4e1cecf55b885044425e8cd4ce690ebe5b88ae8b Mon Sep 17 00:00:00 2001 From: Siirko Date: Tue, 9 Jan 2024 19:58:21 +0100 Subject: [PATCH 4/4] linux and windows handled --- src-tauri/src/main.rs | 10 +++++++- src-tauri/src/music/ytdlp_wrapper.rs | 38 +++++++++++++++++++++++----- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 1810bb3..a71595f 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -116,7 +116,15 @@ fn main() { .app_data_dir() .expect("The app data directory should exist."); match music::ytdlp_wrapper::check_ytdl_bin(app_dir.as_os_str()) { - Ok(_) => {} + Ok(res) => match res { + ytdlp_wrapper::YtDlpBin::InPath => {} + music::ytdlp_wrapper::YtDlpBin::InAppDir => { + tauri::async_runtime::spawn(async move { + let mut str = ytdlp_wrapper::YT_DLP_BIN_PATH.write().await; + *str = format!("{}/{}", app_dir.to_str().unwrap(), str); + }); + } + }, Err(_) => { info!( "yt-dlp not found, downloading it at {:?}", diff --git a/src-tauri/src/music/ytdlp_wrapper.rs b/src-tauri/src/music/ytdlp_wrapper.rs index ce687b4..60468c4 100644 --- a/src-tauri/src/music/ytdlp_wrapper.rs +++ b/src-tauri/src/music/ytdlp_wrapper.rs @@ -16,7 +16,16 @@ pub enum ResultFromDownload { } lazy_static! { - pub static ref YT_DLP_BIN_PATH: RwLock = RwLock::new("yt-dlp".to_string()); + pub static ref YT_DLP_BIN_PATH: RwLock = RwLock::new({ + #[cfg(target_os = "windows")] + { + "yt-dlp.exe".to_string() + } + #[cfg(not(target_os = "windows"))] + { + "yt-dlp".to_string() + } + }); } #[derive(serde::Serialize, Clone)] @@ -75,6 +84,7 @@ fn retrieve_possible_links(url: &str, yt_dlp_path: &str) -> Result Result { let yt_dlp_path = YT_DLP_BIN_PATH.read().await; + YoutubeDl::new(url) .youtube_dl_path(yt_dlp_path.as_str()) .socket_timeout("15") @@ -96,23 +106,37 @@ async fn download_audio(url: &str, path: &str) -> Result .run_async().await } -pub fn check_ytdl_bin(app_dir_path: &OsStr) -> Result { +pub enum YtDlpBin { + InPath, + InAppDir, +} + +pub fn check_ytdl_bin(app_dir_path: &OsStr) -> Result { // we check if user has yt-dlp installed info!("Checking yt-dlp binary in PATH and at {:?}", app_dir_path); match Command::new("yt-dlp").arg("--version").output() { Ok(_) => { info!("yt-dlp found in PATH"); - Ok(true) + Ok(YtDlpBin::InPath) } Err(e) => { if e.kind() == std::io::ErrorKind::NotFound { - match Command::new(format!("{}/yt-dlp", app_dir_path.to_str().unwrap())) - .arg("--version") - .output() + let rt = tokio::runtime::Runtime::new().unwrap(); + let yt_dlp_path: String = rt.block_on(async { + let yt_dlp_path = YT_DLP_BIN_PATH.read().await; + yt_dlp_path.clone() + }); + match Command::new(format!( + "{}/{}", + app_dir_path.to_str().unwrap(), + yt_dlp_path.as_str() + )) + .arg("--version") + .output() { Ok(_) => { info!("yt-dlp found at {:?}", app_dir_path); - Ok(true) + Ok(YtDlpBin::InAppDir) } Err(e) => { if e.kind() == std::io::ErrorKind::NotFound {