tauri-plugin-mpv 0.5.2

A Tauri plugin for embedding the mpv player in your app by controlling its process via JSON IPC.
Documentation
use tauri::{
    plugin::{Builder, TauriPlugin},
    Manager, RunEvent, Runtime, WindowEvent,
};

pub use models::*;

#[cfg(desktop)]
mod desktop;
#[cfg(mobile)]
mod mobile;

mod commands;
mod error;
mod events;
mod ipc;
mod models;
mod process;
mod utils;

pub use error::{Error, Result};

#[cfg(desktop)]
use desktop::Mpv;
#[cfg(mobile)]
use mobile::Mpv;

pub trait MpvExt<R: Runtime> {
    fn mpv(&self) -> &Mpv<R>;
}

impl<R: Runtime, T: Manager<R>> crate::MpvExt<R> for T {
    fn mpv(&self) -> &Mpv<R> {
        self.state::<Mpv<R>>().inner()
    }
}

pub fn init<R: Runtime>() -> TauriPlugin<R> {
    Builder::new("mpv")
        .invoke_handler(tauri::generate_handler![
            commands::init,
            commands::destroy,
            commands::command,
            commands::set_video_margin_ratio,
        ])
        .setup(|app, api| {
            unsafe {
                let locale = std::ffi::CString::new("C").unwrap();
                libc::setlocale(libc::LC_NUMERIC, locale.as_ptr());
            }

            #[cfg(mobile)]
            let mpv = mobile::init(app, api)?;
            #[cfg(desktop)]
            let mpv = desktop::init(app, api)?;
            app.manage(mpv);
            Ok(())
        })
        .on_event(|app_handle, run_event| {
            if let RunEvent::WindowEvent {
                label,
                event: WindowEvent::CloseRequested { api, .. },
                ..
            } = run_event
            {
                let mpv_state = app_handle.state::<Mpv<R>>();

                let instance_exists = {
                    let instances_lock = match mpv_state.instances.lock() {
                        Ok(guard) => guard,
                        Err(poisoned) => {
                            log::warn!("Mutex for mpv instances was poisoned. Recovering.");
                            poisoned.into_inner()
                        }
                    };
                    instances_lock.contains_key(label)
                };

                if instance_exists {
                    api.prevent_close();

                    let app_handle_clone = app_handle.clone();
                    let window_label = label.to_string();

                    tauri::async_runtime::spawn(async move {
                        log::info!(
                            "Close requested for '{}', destroying mpv instance first...",
                            &window_label
                        );

                        if let Err(e) = app_handle_clone.mpv().destroy(&window_label) {
                            log::error!(
                                "Failed to destroy mpv for '{}': {}. Still closing.",
                                &window_label,
                                e
                            );
                        }

                        if let Some(window) = app_handle_clone.get_webview_window(&window_label) {
                            if let Err(e) = window.close() {
                                log::error!("Failed to close window '{}': {}", &window_label, e);
                            }
                        }
                    });
                }
            }
        })
        .build()
}