use crate::{Background, SlideBuilder, TextAlignment}; use super::{ content::Content, kinds::ServiceItemKind, model::{get_db, LibraryKind, Model}, service_items::ServiceTrait, slide::Slide, }; use cosmic::iced::Executor; use crisp::types::{Keyword, Symbol, Value}; use miette::{IntoDiagnostic, Result}; use serde::{Deserialize, Serialize}; use sqlx::{ pool::PoolConnection, query, query_as, Sqlite, SqliteConnection, SqlitePool, }; use std::path::PathBuf; use tracing::error; #[derive( Clone, Debug, Default, PartialEq, Serialize, Deserialize, )] pub struct Video { pub id: i32, pub title: String, pub path: PathBuf, pub start_time: Option, pub end_time: Option, pub looping: bool, } impl From<&Video> for Value { fn from(value: &Video) -> Self { Self::List(vec![Self::Symbol(Symbol("video".into()))]) } } impl Content for Video { fn title(&self) -> String { self.title.clone() } fn kind(&self) -> ServiceItemKind { ServiceItemKind::Video(self.clone()) } fn to_service_item(&self) -> super::service_items::ServiceItem { self.into() } fn background(&self) -> Option { if let Ok(background) = Background::try_from(self.path.clone()) { Some(background) } else { None } } fn subtext(&self) -> String { if self.path.exists() { self.path .file_name() .map(|f| f.to_string_lossy().to_string()) .unwrap_or("Missing video".into()) } else { "Missing video".into() } } } impl From for Video { fn from(value: Value) -> Self { Self::from(&value) } } impl From<&Value> for Video { fn from(value: &Value) -> Self { match value { Value::List(list) => { let path = if let Some(path_pos) = list.iter().position(|v| { v == &Value::Keyword(Keyword::from("source")) }) { let pos = path_pos + 1; list.get(pos) .map(|p| PathBuf::from(String::from(p))) } else { None }; let title = path.clone().map(|p| { let path = p.to_str().unwrap_or_default().to_string(); let title = path.rsplit_once("/").unwrap_or_default().1; title.to_string() }); let start_time = if let Some(start_pos) = list.iter().position(|v| { v == &Value::Keyword(Keyword::from( "start-time", )) }) { let pos = start_pos + 1; list.get(pos).map(|p| i32::from(p) as f32) } else { None }; let end_time = if let Some(end_pos) = list.iter().position(|v| { v == &Value::Keyword(Keyword::from( "end-time", )) }) { let pos = end_pos + 1; list.get(pos).map(|p| i32::from(p) as f32) } else { None }; let looping = if let Some(loop_pos) = list.iter().position(|v| { v == &Value::Keyword(Keyword::from("loop")) }) { let pos = loop_pos + 1; list.get(pos) .map(|l| String::from(l) == *"true") .unwrap_or_default() } else { false }; Self { title: title.unwrap_or_default(), path: path.unwrap_or_default(), start_time, end_time, looping, ..Default::default() } } _ => todo!(), } } } impl ServiceTrait for Video { fn title(&self) -> String { self.title.clone() } fn id(&self) -> i32 { self.id } fn to_slides(&self) -> Result> { let slide = SlideBuilder::new() .background( Background::try_from(self.path.clone()) .into_diagnostic()?, ) .text("") .audio("") .font("") .font_size(50) .text_alignment(TextAlignment::MiddleCenter) .video_loop(self.looping) .video_start_time(self.start_time.unwrap_or(0.0)) .video_end_time(self.end_time.unwrap_or(0.0)) .build()?; Ok(vec![slide]) } fn box_clone(&self) -> Box { Box::new((*self).clone()) } } impl Model