feat: add YouTube streaming support using yt-dlp and update CSP for frame sources
All checks were successful
All checks were successful
This commit is contained in:
2
cinny
2
cinny
Submodule cinny updated: d5ca19f770...c55e311241
@@ -76,6 +76,72 @@ fn open_external_url(url: String) -> Result<(), String> {
|
|||||||
tauri_plugin_opener::open_url(&url, None::<&str>).map_err(|e| e.to_string())
|
tauri_plugin_opener::open_url(&url, None::<&str>).map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// YouTube stream info returned by yt-dlp
|
||||||
|
#[derive(serde::Serialize)]
|
||||||
|
struct YouTubeStreamInfo {
|
||||||
|
/// Direct video stream URL (may include video+audio or video only)
|
||||||
|
video_url: String,
|
||||||
|
/// Title of the video
|
||||||
|
title: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extract direct YouTube stream URL using yt-dlp
|
||||||
|
/// Requires yt-dlp to be installed and available in PATH
|
||||||
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
|
#[tauri::command]
|
||||||
|
async fn get_youtube_stream(url: String) -> Result<YouTubeStreamInfo, String> {
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
// First get the title
|
||||||
|
let title_output = Command::new("yt-dlp")
|
||||||
|
.args(["--get-title", &url])
|
||||||
|
.output()
|
||||||
|
.map_err(|e| format!("Failed to run yt-dlp (is it installed?): {}", e))?;
|
||||||
|
|
||||||
|
let title = if title_output.status.success() {
|
||||||
|
String::from_utf8_lossy(&title_output.stdout).trim().to_string()
|
||||||
|
} else {
|
||||||
|
"YouTube Video".to_string()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get the best format with video+audio combined (up to 1080p)
|
||||||
|
// -f "best[height<=1080]" gets combined format
|
||||||
|
// Fallback to "bestvideo[height<=1080]+bestaudio/best" for separate streams
|
||||||
|
let output = Command::new("yt-dlp")
|
||||||
|
.args([
|
||||||
|
"-g", // Get URL only
|
||||||
|
"-f", "best[height<=1080]/bestvideo[height<=1080]+bestaudio/best",
|
||||||
|
&url
|
||||||
|
])
|
||||||
|
.output()
|
||||||
|
.map_err(|e| format!("Failed to run yt-dlp: {}", e))?;
|
||||||
|
|
||||||
|
if !output.status.success() {
|
||||||
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||||
|
return Err(format!("yt-dlp error: {}", stderr));
|
||||||
|
}
|
||||||
|
|
||||||
|
let video_url = String::from_utf8_lossy(&output.stdout)
|
||||||
|
.lines()
|
||||||
|
.next()
|
||||||
|
.unwrap_or("")
|
||||||
|
.trim()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
if video_url.is_empty() {
|
||||||
|
return Err("yt-dlp returned empty URL".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(YouTubeStreamInfo { video_url, title })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stub for mobile platforms - YouTube streaming not supported
|
||||||
|
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||||
|
#[tauri::command]
|
||||||
|
async fn get_youtube_stream(_url: String) -> Result<YouTubeStreamInfo, String> {
|
||||||
|
Err("YouTube streaming not supported on mobile".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
/// Start background Matrix sync with the given credentials (mobile only)
|
/// Start background Matrix sync with the given credentials (mobile only)
|
||||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
@@ -300,6 +366,7 @@ pub fn run() {
|
|||||||
.invoke_handler(tauri::generate_handler![
|
.invoke_handler(tauri::generate_handler![
|
||||||
read_clipboard_image,
|
read_clipboard_image,
|
||||||
open_external_url,
|
open_external_url,
|
||||||
|
get_youtube_stream,
|
||||||
start_background_sync,
|
start_background_sync,
|
||||||
stop_background_sync,
|
stop_background_sync,
|
||||||
get_background_sync_state
|
get_background_sync_state
|
||||||
@@ -344,6 +411,7 @@ pub fn run() {
|
|||||||
.invoke_handler(tauri::generate_handler![
|
.invoke_handler(tauri::generate_handler![
|
||||||
read_clipboard_image,
|
read_clipboard_image,
|
||||||
open_external_url,
|
open_external_url,
|
||||||
|
get_youtube_stream,
|
||||||
start_background_sync,
|
start_background_sync,
|
||||||
stop_background_sync,
|
stop_background_sync,
|
||||||
get_background_sync_state
|
get_background_sync_state
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"security": {
|
"security": {
|
||||||
"csp": "script-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self'; img-src 'self' https: http: data: blob:"
|
"csp": "script-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self'; img-src 'self' https: http: data: blob:; frame-src https: http:"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"plugins": {
|
"plugins": {
|
||||||
|
|||||||
Reference in New Issue
Block a user