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}