mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 17:06:15 -08:00
feat(media): enhance MPV process management and add database migrations for playlists and content
This commit is contained in:
1 parent
2e8a7494b4
commit
1d59396c50
5 files changed
+195
-36
No files matched your search
Generated
+2
@@ -9,7 +9,9 @@ dependencies = [
|
||||
"chrono",
|
||||
"fix-path-env",
|
||||
"flate2",
|
||||
"libc",
|
||||
"log",
|
||||
"once_cell",
|
||||
"quick-xml 0.31.0",
|
||||
"reqwest 0.11.27",
|
||||
"serde",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
@@ -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);
|
||||
",
|
||||
}]
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
Reference in new issue
Block a user