feat(media): enhance MPV process management and add database migrations for playlists and content

This commit is contained in:
4gray committed 2024-12-16 01:35:07 +01:00
1 parent 2e8a7494b4
commit 1d59396c50
5 files changed
+195 -36

No files matched your search

+2
View File
@@ -9,7 +9,9 @@ dependencies = [
"chrono",
"fix-path-env",
"flate2",
"libc",
"log",
"once_cell",
"quick-xml 0.31.0",
"reqwest 0.11.27",
"serde",
+2
View File
@@ -37,6 +37,8 @@ reqwest = { version = "0.11", features = ["json", "gzip"] }
flate2 = "1.0"
quick-xml = { version = "0.31", features = ["serialize"] }
tauri-plugin-sql = { version = "2", features = ["sqlite"] }
once_cell = "1.19.0"
libc = "0.2"
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
tauri-plugin-updater = "2"
+123 -4
View File
@@ -1,9 +1,36 @@
use log::info;
use once_cell::sync::Lazy;
use serde::Serialize;
use std::collections::HashMap;
use std::path::Path;
use std::process::Command;
use std::sync::Mutex;
use std::thread;
use std::time::{SystemTime, UNIX_EPOCH};
use tauri::Emitter;
use tauri::Runtime;
#[derive(Debug, Serialize, Clone)]
pub struct MpvProcess {
id: u32,
url: String,
start_time: u64,
last_known_time: Option<f64>,
title: String,
thumbnail: Option<String>,
}
static MPV_PROCESSES: Lazy<Mutex<HashMap<u32, MpvProcess>>> =
Lazy::new(|| Mutex::new(HashMap::new()));
#[tauri::command]
pub async fn open_in_mpv(url: String, path: String) -> Result<(), String> {
pub async fn open_in_mpv<R: Runtime>(
url: String,
path: String,
title: String,
thumbnail: Option<String>,
app_handle: tauri::AppHandle<R>,
) -> Result<u32, String> {
info!("Custom MVP path: {}", path);
let mpv_paths = if cfg!(target_os = "windows") {
vec![
@@ -32,16 +59,108 @@ pub async fn open_in_mpv(url: String, path: String) -> Result<(), String> {
info!("Using the following MPV player path: {}", mpv_path);
Command::new(mpv_path)
// Add --input-ipc-server for IPC communication
let ipc_socket = format!(
"/tmp/mpv-socket-{}",
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
);
let child = Command::new(mpv_path)
.arg(format!("--input-ipc-server={}", ipc_socket))
.arg(&url)
.spawn()
.map_err(|e| e.to_string())?;
Ok(())
let process_id = child.id();
// Clone app_handle for the monitoring thread
let app_handle_clone = app_handle.clone();
// Spawn a thread to monitor the process
thread::spawn(move || {
let _ = child.wait_with_output(); // Wait for the process to exit
// Process has exited, remove it from our map and notify frontend
if let Some(process) = MPV_PROCESSES.lock().unwrap().remove(&process_id) {
let _ = app_handle_clone.emit("mpv-process-removed", process);
}
});
// Store process information
let mpv_process = MpvProcess {
id: process_id,
url: url.clone(),
start_time: SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs(),
last_known_time: None,
title,
thumbnail,
};
MPV_PROCESSES
.lock()
.unwrap()
.insert(process_id, mpv_process.clone());
// Updated event emission
app_handle
.emit("mpv-process-added", mpv_process)
.map_err(|e| e.to_string())?;
Ok(process_id)
}
#[tauri::command]
pub async fn open_in_vlc(url: String, path: String) -> Result<(), String> {
pub async fn get_active_mpv_processes() -> Vec<MpvProcess> {
MPV_PROCESSES.lock().unwrap().values().cloned().collect()
}
#[tauri::command]
pub async fn close_mpv_process<R: Runtime>(
process_id: u32,
app_handle: tauri::AppHandle<R>,
) -> Result<(), String> {
if let Some(process) = MPV_PROCESSES.lock().unwrap().remove(&process_id) {
#[cfg(unix)]
{
unsafe {
libc::kill(process_id as i32, libc::SIGTERM);
}
}
#[cfg(windows)]
{
unsafe {
let handle = OpenProcess(PROCESS_TERMINATE, 0, process_id);
if !handle.is_null() {
TerminateProcess(handle, 1);
CloseHandle(handle);
}
}
}
// Updated event emission
app_handle
.emit("mpv-process-removed", process)
.map_err(|e| e.to_string())?;
Ok(())
} else {
Err("Process not found".to_string())
}
}
#[tauri::command]
pub async fn open_in_vlc<R: Runtime>(
url: String,
path: String,
_app_handle: tauri::AppHandle<R>,
) -> Result<(), String> {
info!("Custom MVP path: {}", path);
let vlc_paths = if cfg!(target_os = "windows") {
vec![
+65 -31
View File
@@ -3,38 +3,72 @@ use tauri_plugin_sql::{Migration, MigrationKind};
pub fn get_migrations() -> Vec<Migration> {
vec![Migration {
version: 1,
description: "Create initial tables for Xtream API data",
description: "Create initial tables for playlist data",
kind: MigrationKind::Up,
sql: "
CREATE TABLE IF NOT EXISTS playlists (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
api_url TEXT NOT NULL,
username TEXT NOT NULL,
password TEXT NOT NULL,
last_updated DATETIME
);
CREATE TABLE IF NOT EXISTS categories (
id INTEGER PRIMARY KEY AUTOINCREMENT,
playlist_id TEXT NOT NULL,
name TEXT NOT NULL,
type TEXT NOT NULL CHECK (type IN ('live', 'movies', 'series')),
xtream_id INTEGER NOT NULL,
FOREIGN KEY (playlist_id) REFERENCES playlists (id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS content (
id INTEGER PRIMARY KEY AUTOINCREMENT,
category_id INTEGER NOT NULL,
title TEXT NOT NULL,
rating TEXT,
added TEXT,
poster_url TEXT,
xtream_id INTEGER NOT NULL,
type TEXT NOT NULL CHECK (type IN ('live', 'movie', 'series')),
FOREIGN KEY (category_id) REFERENCES categories (id) ON DELETE CASCADE
);
",
CREATE TABLE IF NOT EXISTS playlists (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
serverUrl TEXT,
username TEXT,
password TEXT,
date_created DATETIME DEFAULT CURRENT_TIMESTAMP,
last_updated DATETIME,
type TEXT NOT NULL CHECK (type IN ('xtream', 'stalker', 'm3u-file', 'm3u-text', 'm3u-url')),
userAgent TEXT,
filePath TEXT,
autoRefresh BOOLEAN DEFAULT 0,
macAddress TEXT,
url TEXT,
last_usage DATETIME
);
CREATE TABLE IF NOT EXISTS categories (
id INTEGER PRIMARY KEY AUTOINCREMENT,
playlist_id TEXT NOT NULL,
name TEXT NOT NULL,
type TEXT NOT NULL CHECK (type IN ('live', 'movies', 'series')),
xtream_id INTEGER NOT NULL,
FOREIGN KEY (playlist_id) REFERENCES playlists (id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS content (
id INTEGER PRIMARY KEY AUTOINCREMENT,
category_id INTEGER NOT NULL,
title TEXT NOT NULL,
rating TEXT,
added TEXT,
poster_url TEXT,
xtream_id INTEGER NOT NULL,
type TEXT NOT NULL CHECK (type IN ('live', 'movie', 'series')),
FOREIGN KEY (category_id) REFERENCES categories (id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS recently_viewed (
id INTEGER PRIMARY KEY AUTOINCREMENT,
content_id INTEGER NOT NULL,
playlist_id TEXT NOT NULL,
viewed_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (content_id) REFERENCES content (id) ON DELETE CASCADE,
FOREIGN KEY (playlist_id) REFERENCES playlists (id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS favorites (
id INTEGER PRIMARY KEY AUTOINCREMENT,
content_id INTEGER NOT NULL,
playlist_id TEXT NOT NULL,
added_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (content_id) REFERENCES content(id) ON DELETE CASCADE,
FOREIGN KEY (playlist_id) REFERENCES playlists(id) ON DELETE CASCADE
);
-- Create indexes for better query performance
CREATE INDEX IF NOT EXISTS idx_content_type ON content(type);
CREATE INDEX IF NOT EXISTS idx_content_category ON content(category_id);
CREATE INDEX IF NOT EXISTS idx_categories_playlist ON categories(playlist_id);
CREATE INDEX IF NOT EXISTS idx_content_title ON content(title);
CREATE INDEX IF NOT EXISTS idx_content_xtream ON content(xtream_id);
CREATE INDEX IF NOT EXISTS idx_categories_type ON categories(type);
",
}]
}
+3 -1
View File
@@ -27,7 +27,9 @@ pub fn run() {
commands::epg::get_channel_programs,
commands::epg::get_epg_by_range,
commands::media::open_in_mpv,
commands::media::open_in_vlc
commands::media::open_in_vlc,
commands::media::get_active_mpv_processes,
commands::media::close_mpv_process
])
.setup(|app| {
if cfg!(debug_assertions) {