Skip to content

Commit

Permalink
Merge pull request #105 from Siirko/fix-yt-dlp-install
Browse files Browse the repository at this point in the history
Yt-dlp bin download if not present
  • Loading branch information
aaalloc authored Jan 9, 2024
2 parents 19400af + 4e1cecf commit 55a24be
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 5 deletions.
2 changes: 2 additions & 0 deletions src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@ 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"] }
youtube_dl = { version = "0.9.0", features = ["tokio", "downloader-native-tls"] }
tokio = { version = "1.20.1", features = ["full"] }
base64 = { version = "0.21.5" }

Expand Down
39 changes: 38 additions & 1 deletion src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@ 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::{
async_process::{async_process_model, AsyncProcInputTx},
player::MusicPlayer,
Expand Down Expand Up @@ -99,12 +103,45 @@ fn main() {
music::ytdlp_wrapper::download_audio_from_links,
])
.setup(|app| {
// init database
let handle = app.handle();
let app_state: State<DbState> = 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
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(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 {:?}",
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");
info!("yt-dlp downloaded at {:?}", path);
let mut str = ytdlp_wrapper::YT_DLP_BIN_PATH.write().await;
*str = path.to_str().unwrap().to_string();
});
}
}

tauri::async_runtime::spawn(async move {
async_process_model(async_process_input_rx, async_process_output_tx).await
});
Expand Down
2 changes: 1 addition & 1 deletion src-tauri/src/music/audio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()),
Expand Down
74 changes: 72 additions & 2 deletions src-tauri/src/music/ytdlp_wrapper.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
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};

Expand All @@ -10,6 +15,19 @@ pub enum ResultFromDownload {
Error(ErrorItem),
}

lazy_static! {
pub static ref YT_DLP_BIN_PATH: RwLock<String> = RwLock::new({
#[cfg(target_os = "windows")]
{
"yt-dlp.exe".to_string()
}
#[cfg(not(target_os = "windows"))]
{
"yt-dlp".to_string()
}
});
}

#[derive(serde::Serialize, Clone)]
pub struct TotalItem {
pub total: usize,
Expand All @@ -34,9 +52,10 @@ impl std::fmt::Display for MusicItem {
}
}

fn retrieve_possible_links(url: &str) -> Result<Vec<MusicItem>, Error> {
fn retrieve_possible_links(url: &str, yt_dlp_path: &str) -> Result<Vec<MusicItem>, Error> {
let mut links: Vec<MusicItem> = Vec::new();
let retrieved = YoutubeDl::new(url)
.youtube_dl_path(yt_dlp_path)
.socket_timeout("15")
.flat_playlist(true)
.run();
Expand Down Expand Up @@ -64,7 +83,10 @@ fn retrieve_possible_links(url: &str) -> Result<Vec<MusicItem>, Error> {
}

async fn download_audio(url: &str, path: &str) -> Result<YoutubeDlOutput, Error> {
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")
Expand All @@ -84,13 +106,61 @@ async fn download_audio(url: &str, path: &str) -> Result<YoutubeDlOutput, Error>
.run_async().await
}

pub enum YtDlpBin {
InPath,
InAppDir,
}

pub fn check_ytdl_bin(app_dir_path: &OsStr) -> Result<YtDlpBin, String> {
// 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(YtDlpBin::InPath)
}
Err(e) => {
if e.kind() == std::io::ErrorKind::NotFound {
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(YtDlpBin::InAppDir)
}
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())
}
}
}
}

#[tauri::command]
pub async fn download_audio_from_links(
url: String,
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) => {
Expand Down

0 comments on commit 55a24be

Please sign in to comment.