prehrajto_tauri/
lib.rs

1//! Prehraj.to Tauri Integration
2//!
3//! Provides Tauri plugin for frontend integration with prehraj.to scraper.
4//!
5//! # Usage
6//!
7//! Register the plugin in your Tauri application:
8//!
9//! ```ignore
10//! fn main() {
11//!     tauri::Builder::default()
12//!         .plugin(prehrajto_tauri::init())
13//!         .run(tauri::generate_context!())
14//!         .expect("error while running tauri application");
15//! }
16//! ```
17//!
18//! Then invoke commands from the frontend:
19//!
20//! ```javascript
21//! import { invoke } from '@tauri-apps/api/core';
22//!
23//! // Search for videos
24//! const results = await invoke('plugin:prehrajto|search_videos', { query: 'doctor who' });
25//!
26//! // Get download URL
27//! const url = await invoke('plugin:prehrajto|get_download_url', {
28//!   videoSlug: 'doctor-who-s07e05',
29//!   videoId: '63aba7f51f6cf'
30//! });
31//! ```
32
33use std::sync::Arc;
34use tokio::sync::Mutex;
35
36use prehrajto_core::PrehrajtoScraper;
37use tauri::{
38    plugin::{Builder, TauriPlugin},
39    Manager, Runtime,
40};
41
42mod commands;
43
44/// Thread-safe wrapper for PrehrajtoScraper
45///
46/// Uses Arc<Mutex<>> for safe concurrent access from multiple Tauri commands.
47/// This allows the scraper to be shared across threads while maintaining
48/// rate limiting state.
49///
50/// # Requirements
51/// - 7.2: Shared ScraperState for thread-safe access
52/// - 7.4: Arc<Mutex<>> wrapper for safe concurrent access
53pub struct ScraperState {
54    pub(crate) scraper: Arc<Mutex<PrehrajtoScraper>>,
55}
56
57impl ScraperState {
58    /// Create a new ScraperState with default configuration
59    ///
60    /// # Returns
61    /// A new `ScraperState` instance wrapping a `PrehrajtoScraper`
62    ///
63    /// # Errors
64    /// Returns error string if scraper initialization fails
65    pub fn new() -> Result<Self, String> {
66        let scraper = PrehrajtoScraper::new().map_err(|e| e.to_string())?;
67        Ok(Self {
68            scraper: Arc::new(Mutex::new(scraper)),
69        })
70    }
71}
72
73impl Default for ScraperState {
74    fn default() -> Self {
75        Self::new().expect("Failed to create default ScraperState")
76    }
77}
78
79/// Initialize the prehrajto plugin
80///
81/// # Returns
82/// A configured TauriPlugin ready to be registered with the Tauri application
83///
84/// # Example
85/// ```ignore
86/// tauri::Builder::default()
87///     .plugin(prehrajto_tauri::init())
88///     .run(tauri::generate_context!())
89///     .expect("error while running tauri application");
90/// ```
91pub fn init<R: Runtime>() -> TauriPlugin<R> {
92    Builder::new("prehrajto")
93        .invoke_handler(tauri::generate_handler![
94            commands::search_videos,
95            commands::get_download_url
96        ])
97        .setup(|app, _api| {
98            let state = ScraperState::new().map_err(Box::<dyn std::error::Error>::from)?;
99            app.manage(state);
100            Ok(())
101        })
102        .build()
103}
104
105// Re-export types for convenience
106pub use prehrajto_core::VideoResult as Video;
107
108#[cfg(test)]
109mod tests {
110    use super::*;
111
112    #[test]
113    fn test_scraper_state_creation() {
114        let state = ScraperState::new();
115        assert!(state.is_ok());
116    }
117
118    #[test]
119    fn test_scraper_state_default() {
120        let state = ScraperState::default();
121        assert!(state.scraper.try_lock().is_ok());
122    }
123}