moving the lisp presentation parsing to service_items
Since we need the nav_bar to have the ServiceItemModel, the presentation needs to be a list of service items, not slides, each service item will have a list of slides attached though.
This commit is contained in:
parent
9eb5bec320
commit
cb7fa372a9
|
@ -1,4 +1,7 @@
|
||||||
use super::model::Model;
|
use crate::{Background, Slide, SlideBuilder, TextAlignment};
|
||||||
|
|
||||||
|
use super::{model::Model, service_items::ServiceTrait};
|
||||||
|
use crisp::types::{Keyword, Value};
|
||||||
use miette::{miette, IntoDiagnostic, Result};
|
use miette::{miette, IntoDiagnostic, Result};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sqlx::{query_as, SqliteConnection};
|
use sqlx::{query_as, SqliteConnection};
|
||||||
|
@ -14,6 +17,74 @@ pub struct Image {
|
||||||
pub path: PathBuf,
|
pub path: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Value> for Image {
|
||||||
|
fn from(value: Value) -> Self {
|
||||||
|
Self::from(&value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Value> for Image {
|
||||||
|
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| {
|
||||||
|
p.to_str().unwrap_or_default().to_string()
|
||||||
|
});
|
||||||
|
Self {
|
||||||
|
title: title.unwrap_or_default(),
|
||||||
|
path: path.unwrap_or_default(),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ServiceTrait for Image {
|
||||||
|
fn title(&self) -> String {
|
||||||
|
self.title.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn id(&self) -> i32 {
|
||||||
|
self.id
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_slides(&self) -> Result<Vec<Slide>> {
|
||||||
|
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(false)
|
||||||
|
.video_start_time(0.0)
|
||||||
|
.video_end_time(0.0)
|
||||||
|
.build()?;
|
||||||
|
|
||||||
|
Ok(vec![slide])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn box_clone(&self) -> Box<dyn ServiceTrait> {
|
||||||
|
Box::new((*self).clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Model<Image> {
|
impl Model<Image> {
|
||||||
pub async fn load_from_db(&mut self) {
|
pub async fn load_from_db(&mut self) {
|
||||||
let result = query_as!(
|
let result = query_as!(
|
||||||
|
|
|
@ -2,65 +2,65 @@ use std::{error::Error, fmt::Display};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::presentations::PresKind;
|
use crate::Slide;
|
||||||
|
|
||||||
#[derive(
|
use super::{
|
||||||
Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize,
|
images::Image,
|
||||||
)]
|
presentations::{PresKind, Presentation},
|
||||||
|
songs::Song,
|
||||||
|
videos::Video,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
pub enum ServiceItemKind {
|
pub enum ServiceItemKind {
|
||||||
#[default]
|
Song(Song),
|
||||||
Song,
|
Video(Video),
|
||||||
Video,
|
Image(Image),
|
||||||
Image,
|
Presentation((Presentation, PresKind)),
|
||||||
Presentation(PresKind),
|
Content(Slide),
|
||||||
Content,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for ServiceItemKind {
|
impl std::fmt::Display for ServiceItemKind {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
let s = match self {
|
let s = match self {
|
||||||
Self::Song => "song".to_owned(),
|
Self::Song(s) => "song".to_owned(),
|
||||||
Self::Image => "image".to_owned(),
|
Self::Image(i) => "image".to_owned(),
|
||||||
Self::Video => "video".to_owned(),
|
Self::Video(v) => "video".to_owned(),
|
||||||
Self::Presentation(PresKind::Html) => "html".to_owned(),
|
Self::Presentation((p, k)) => "html".to_owned(),
|
||||||
Self::Presentation(PresKind::Pdf) => "pdf".to_owned(),
|
Self::Content(s) => "content".to_owned(),
|
||||||
Self::Presentation(PresKind::Generic) => {
|
|
||||||
"presentation".to_owned()
|
|
||||||
}
|
|
||||||
Self::Content => "content".to_owned(),
|
|
||||||
};
|
};
|
||||||
write!(f, "{s}")
|
write!(f, "{s}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<String> for ServiceItemKind {
|
// impl TryFrom<String> for ServiceItemKind {
|
||||||
type Error = ParseError;
|
// type Error = ParseError;
|
||||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
// fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||||
match value.as_str() {
|
// match value.as_str() {
|
||||||
"song" => Ok(Self::Song),
|
// "song" => Ok(Self::Song),
|
||||||
"image" => Ok(Self::Image),
|
// "image" => Ok(Self::Image),
|
||||||
"video" => Ok(Self::Video),
|
// "video" => Ok(Self::Video),
|
||||||
"presentation" => {
|
// "presentation" => {
|
||||||
Ok(Self::Presentation(PresKind::Generic))
|
// Ok(Self::Presentation(PresKind::Generic))
|
||||||
}
|
// }
|
||||||
"html" => Ok(Self::Presentation(PresKind::Html)),
|
// "html" => Ok(Self::Presentation(PresKind::Html)),
|
||||||
"pdf" => Ok(Self::Presentation(PresKind::Pdf)),
|
// "pdf" => Ok(Self::Presentation(PresKind::Pdf)),
|
||||||
"content" => Ok(Self::Content),
|
// "content" => Ok(Self::Content),
|
||||||
_ => Err(ParseError::UnknownType),
|
// _ => Err(ParseError::UnknownType),
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
impl From<ServiceItemKind> for String {
|
impl From<ServiceItemKind> for String {
|
||||||
fn from(val: ServiceItemKind) -> String {
|
fn from(val: ServiceItemKind) -> String {
|
||||||
match val {
|
match val {
|
||||||
ServiceItemKind::Song => "song".to_owned(),
|
ServiceItemKind::Song(_) => "song".to_owned(),
|
||||||
ServiceItemKind::Video => "video".to_owned(),
|
ServiceItemKind::Video(_) => "video".to_owned(),
|
||||||
ServiceItemKind::Image => "image".to_owned(),
|
ServiceItemKind::Image(_) => "image".to_owned(),
|
||||||
ServiceItemKind::Presentation(_) => {
|
ServiceItemKind::Presentation(_) => {
|
||||||
"presentation".to_owned()
|
"presentation".to_owned()
|
||||||
}
|
}
|
||||||
ServiceItemKind::Content => "content".to_owned(),
|
ServiceItemKind::Content(_) => "content".to_owned(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crisp::types::Value;
|
||||||
use miette::{miette, IntoDiagnostic, Result};
|
use miette::{miette, IntoDiagnostic, Result};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sqlx::{
|
use sqlx::{
|
||||||
|
@ -6,7 +7,9 @@ use sqlx::{
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
|
||||||
use super::model::Model;
|
use crate::{Background, Slide, SlideBuilder, TextAlignment};
|
||||||
|
|
||||||
|
use super::{model::Model, service_items::ServiceTrait};
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize,
|
Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize,
|
||||||
|
@ -28,6 +31,51 @@ pub struct Presentation {
|
||||||
pub kind: PresKind,
|
pub kind: PresKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Value> for Presentation {
|
||||||
|
fn from(value: Value) -> Self {
|
||||||
|
Self::from(&value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Value> for Presentation {
|
||||||
|
fn from(value: &Value) -> Self {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ServiceTrait for Presentation {
|
||||||
|
fn title(&self) -> String {
|
||||||
|
self.title.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn id(&self) -> i32 {
|
||||||
|
self.id
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_slides(&self) -> Result<Vec<Slide>> {
|
||||||
|
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(false)
|
||||||
|
.video_start_time(0.0)
|
||||||
|
.video_end_time(0.0)
|
||||||
|
.build()?;
|
||||||
|
|
||||||
|
Ok(vec![slide])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn box_clone(&self) -> Box<dyn ServiceTrait> {
|
||||||
|
Box::new((*self).clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Presentation {
|
impl Presentation {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
|
@ -1,29 +1,146 @@
|
||||||
|
use crisp::types::{Keyword, Symbol, Value};
|
||||||
use miette::Result;
|
use miette::Result;
|
||||||
|
use tracing::error;
|
||||||
|
|
||||||
|
use crate::Slide;
|
||||||
|
|
||||||
use super::images::Image;
|
use super::images::Image;
|
||||||
use super::presentations::Presentation;
|
use super::presentations::Presentation;
|
||||||
use super::songs::Song;
|
use super::songs::{lisp_to_song, Song};
|
||||||
use super::videos::Video;
|
use super::videos::Video;
|
||||||
|
|
||||||
use super::kinds::ServiceItemKind;
|
use super::kinds::ServiceItemKind;
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct ServiceItem {
|
pub struct ServiceItem {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
|
pub title: String,
|
||||||
pub database_id: i32,
|
pub database_id: i32,
|
||||||
pub kind: ServiceItemKind,
|
pub kind: ServiceItemKind,
|
||||||
|
// pub item: Box<dyn ServiceTrait>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq)]
|
impl ServiceItem {
|
||||||
|
pub fn to_slide(&self) -> Result<Vec<Slide>> {
|
||||||
|
match &self.kind {
|
||||||
|
ServiceItemKind::Song(song) => song.to_slides(),
|
||||||
|
ServiceItemKind::Video(video) => video.to_slides(),
|
||||||
|
ServiceItemKind::Image(image) => image.to_slides(),
|
||||||
|
ServiceItemKind::Presentation((presentation, _)) => {
|
||||||
|
presentation.to_slides()
|
||||||
|
}
|
||||||
|
ServiceItemKind::Content(slide) => {
|
||||||
|
Ok(vec![slide.clone()])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ServiceItem {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
id: 0,
|
||||||
|
title: String::default(),
|
||||||
|
database_id: 0,
|
||||||
|
kind: ServiceItemKind::Content(Slide::default()),
|
||||||
|
// item: Box::new(Image::default()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Value> for ServiceItem {
|
||||||
|
fn from(value: Value) -> Self {
|
||||||
|
Self::from(&value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Value> for ServiceItem {
|
||||||
|
fn from(value: &Value) -> Self {
|
||||||
|
match value {
|
||||||
|
Value::List(list) => match &list[0] {
|
||||||
|
Value::Symbol(Symbol(s)) if s == "slide" => {
|
||||||
|
let background_pos = list
|
||||||
|
.iter()
|
||||||
|
.position(|v| match v {
|
||||||
|
Value::Keyword(Keyword(background))
|
||||||
|
if background == "background" =>
|
||||||
|
{
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
})
|
||||||
|
.map_or_else(|| 1, |pos| pos + 1);
|
||||||
|
if let Some(background) = list.get(background_pos)
|
||||||
|
{
|
||||||
|
match background {
|
||||||
|
Value::List(item) => match &item[0] {
|
||||||
|
Value::Symbol(Symbol(s))
|
||||||
|
if s == "image" =>
|
||||||
|
{
|
||||||
|
Self::from(&Image::from(
|
||||||
|
background,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
Value::Symbol(Symbol(s))
|
||||||
|
if s == "video" =>
|
||||||
|
{
|
||||||
|
Self::from(&Video::from(
|
||||||
|
background,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
Value::Symbol(Symbol(s))
|
||||||
|
if s == "presentation" =>
|
||||||
|
{
|
||||||
|
Self::from(&Presentation::from(
|
||||||
|
background,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
_ => todo!(),
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
error!(
|
||||||
|
"There is no background here: {:?}",
|
||||||
|
background
|
||||||
|
);
|
||||||
|
ServiceItem::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error!(
|
||||||
|
"There is no background here: {:?}",
|
||||||
|
background_pos
|
||||||
|
);
|
||||||
|
ServiceItem::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Value::Symbol(Symbol(s)) if s == "song" => {
|
||||||
|
let song = lisp_to_song(list.clone());
|
||||||
|
Self::from(&song)
|
||||||
|
}
|
||||||
|
_ => todo!(),
|
||||||
|
},
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
pub struct ServiceItemModel {
|
pub struct ServiceItemModel {
|
||||||
items: Vec<ServiceItem>,
|
items: Vec<ServiceItem>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Vec<ServiceItem>> for ServiceItemModel {
|
||||||
|
fn from(items: Vec<ServiceItem>) -> Self {
|
||||||
|
Self { items }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<&Song> for ServiceItem {
|
impl From<&Song> for ServiceItem {
|
||||||
fn from(song: &Song) -> Self {
|
fn from(song: &Song) -> Self {
|
||||||
Self {
|
Self {
|
||||||
kind: ServiceItemKind::Song,
|
kind: ServiceItemKind::Song(song.clone()),
|
||||||
database_id: song.id,
|
database_id: song.id,
|
||||||
|
title: song.title.clone(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,8 +149,9 @@ impl From<&Song> for ServiceItem {
|
||||||
impl From<&Video> for ServiceItem {
|
impl From<&Video> for ServiceItem {
|
||||||
fn from(video: &Video) -> Self {
|
fn from(video: &Video) -> Self {
|
||||||
Self {
|
Self {
|
||||||
kind: ServiceItemKind::Video,
|
kind: ServiceItemKind::Video(video.clone()),
|
||||||
database_id: video.id,
|
database_id: video.id,
|
||||||
|
title: video.title.clone(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,8 +160,9 @@ impl From<&Video> for ServiceItem {
|
||||||
impl From<&Image> for ServiceItem {
|
impl From<&Image> for ServiceItem {
|
||||||
fn from(image: &Image) -> Self {
|
fn from(image: &Image) -> Self {
|
||||||
Self {
|
Self {
|
||||||
kind: ServiceItemKind::Image,
|
kind: ServiceItemKind::Image(image.clone()),
|
||||||
database_id: image.id,
|
database_id: image.id,
|
||||||
|
title: image.title.clone(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,10 +171,12 @@ impl From<&Image> for ServiceItem {
|
||||||
impl From<&Presentation> for ServiceItem {
|
impl From<&Presentation> for ServiceItem {
|
||||||
fn from(presentation: &Presentation) -> Self {
|
fn from(presentation: &Presentation) -> Self {
|
||||||
Self {
|
Self {
|
||||||
kind: ServiceItemKind::Presentation(
|
kind: ServiceItemKind::Presentation((
|
||||||
|
presentation.clone(),
|
||||||
presentation.kind.clone(),
|
presentation.kind.clone(),
|
||||||
),
|
)),
|
||||||
database_id: presentation.id,
|
database_id: presentation.id,
|
||||||
|
title: presentation.title.clone(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,6 +191,37 @@ impl ServiceItemModel {
|
||||||
self.items.push(service_item);
|
self.items.push(service_item);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn to_slides(&self) -> Result<Vec<Slide>> {
|
||||||
|
Ok(self
|
||||||
|
.items
|
||||||
|
.iter()
|
||||||
|
.filter_map(|item| item.to_slide().ok())
|
||||||
|
.flatten()
|
||||||
|
.collect::<Vec<Slide>>())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ServiceTrait {
|
||||||
|
fn title(&self) -> String;
|
||||||
|
fn id(&self) -> i32;
|
||||||
|
fn to_slides(&self) -> Result<Vec<Slide>>;
|
||||||
|
fn box_clone(&self) -> Box<dyn ServiceTrait>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for Box<dyn ServiceTrait> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
self.box_clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for Box<dyn ServiceTrait> {
|
||||||
|
fn fmt(
|
||||||
|
&self,
|
||||||
|
f: &mut std::fmt::Formatter<'_>,
|
||||||
|
) -> Result<(), std::fmt::Error> {
|
||||||
|
write!(f, "{}: {}", self.id(), self.title())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -99,26 +251,26 @@ mod test {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
// #[test]
|
||||||
pub fn test_service_item() {
|
// pub fn test_service_item() {
|
||||||
let song = test_song();
|
// let song = test_song();
|
||||||
let service_item = ServiceItem::from(&song);
|
// let service_item = ServiceItem::from(&song);
|
||||||
let pres = test_presentation();
|
// let pres = test_presentation();
|
||||||
let pres_item = ServiceItem::from(&pres);
|
// let pres_item = ServiceItem::from(&pres);
|
||||||
let mut service_model = ServiceItemModel::default();
|
// let mut service_model = ServiceItemModel::default();
|
||||||
match service_model.add_item(&song) {
|
// match service_model.add_item(&song) {
|
||||||
Ok(_) => {
|
// Ok(_) => {
|
||||||
assert_eq!(
|
// assert_eq!(
|
||||||
ServiceItemKind::Song,
|
// ServiceItemKind::Song,
|
||||||
service_model.items[0].kind
|
// service_model.items[0].kind
|
||||||
);
|
// );
|
||||||
assert_eq!(
|
// assert_eq!(
|
||||||
ServiceItemKind::Presentation(PresKind::Html),
|
// ServiceItemKind::Presentation(PresKind::Html),
|
||||||
pres_item.kind
|
// pres_item.kind
|
||||||
);
|
// );
|
||||||
assert_eq!(service_item, service_model.items[0]);
|
// assert_eq!(service_item, service_model.items[0]);
|
||||||
}
|
// }
|
||||||
Err(e) => panic!("Problem adding item: {:?}", e),
|
// Err(e) => panic!("Problem adding item: {:?}", e),
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
|
@ -228,6 +228,10 @@ impl Slide {
|
||||||
|
|
||||||
Ok(slides)
|
Ok(slides)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pub fn slides_from_item(item: &ServiceItem) -> Result<Vec<Self>> {
|
||||||
|
// todo!()
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Value> for Slide {
|
impl From<Value> for Slide {
|
||||||
|
|
|
@ -10,10 +10,11 @@ use sqlx::{
|
||||||
};
|
};
|
||||||
use tracing::{debug, error};
|
use tracing::{debug, error};
|
||||||
|
|
||||||
use crate::core::slide;
|
use crate::{core::slide, Slide, SlideBuilder};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
model::Model,
|
model::Model,
|
||||||
|
service_items::ServiceTrait,
|
||||||
slide::{Background, TextAlignment},
|
slide::{Background, TextAlignment},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -34,6 +35,47 @@ pub struct Song {
|
||||||
pub font_size: Option<i32>,
|
pub font_size: Option<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ServiceTrait for Song {
|
||||||
|
fn title(&self) -> String {
|
||||||
|
self.title.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn id(&self) -> i32 {
|
||||||
|
self.id
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_slides(&self) -> Result<Vec<Slide>> {
|
||||||
|
let lyrics = self.get_lyrics()?;
|
||||||
|
let slides: Vec<Slide> = lyrics
|
||||||
|
.iter()
|
||||||
|
.map(|l| {
|
||||||
|
SlideBuilder::new()
|
||||||
|
.background(
|
||||||
|
self.background.clone().unwrap_or_default(),
|
||||||
|
)
|
||||||
|
.font(self.font.clone().unwrap_or_default())
|
||||||
|
.font_size(self.font_size.unwrap_or_default())
|
||||||
|
.text_alignment(
|
||||||
|
self.text_alignment.unwrap_or_default(),
|
||||||
|
)
|
||||||
|
.audio(self.audio.clone().unwrap_or_default())
|
||||||
|
.video_loop(true)
|
||||||
|
.video_start_time(0.0)
|
||||||
|
.video_end_time(0.0)
|
||||||
|
.text(l)
|
||||||
|
.build()
|
||||||
|
.unwrap_or_default()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(slides)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn box_clone(&self) -> Box<dyn ServiceTrait> {
|
||||||
|
Box::new((*self).clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const VERSE_KEYWORDS: [&str; 24] = [
|
const VERSE_KEYWORDS: [&str; 24] = [
|
||||||
"Verse 1", "Verse 2", "Verse 3", "Verse 4", "Verse 5", "Verse 6",
|
"Verse 1", "Verse 2", "Verse 3", "Verse 4", "Verse 5", "Verse 6",
|
||||||
"Verse 7", "Verse 8", "Chorus 1", "Chorus 2", "Chorus 3",
|
"Verse 7", "Verse 8", "Chorus 1", "Chorus 2", "Chorus 3",
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
use super::model::Model;
|
use crate::{Background, SlideBuilder, TextAlignment};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
model::Model, service_items::ServiceTrait, slide::Slide,
|
||||||
|
};
|
||||||
use cosmic::{executor, iced::Executor};
|
use cosmic::{executor, iced::Executor};
|
||||||
|
use crisp::types::Value;
|
||||||
use miette::{miette, IntoDiagnostic, Result};
|
use miette::{miette, IntoDiagnostic, Result};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sqlx::{query_as, SqliteConnection};
|
use sqlx::{query_as, SqliteConnection};
|
||||||
|
@ -18,6 +23,51 @@ pub struct Video {
|
||||||
pub looping: bool,
|
pub looping: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Value> for Video {
|
||||||
|
fn from(value: Value) -> Self {
|
||||||
|
Self::from(&value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Value> for Video {
|
||||||
|
fn from(value: &Value) -> Self {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ServiceTrait for Video {
|
||||||
|
fn title(&self) -> String {
|
||||||
|
self.title.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn id(&self) -> i32 {
|
||||||
|
self.id
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_slides(&self) -> Result<Vec<Slide>> {
|
||||||
|
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<dyn ServiceTrait> {
|
||||||
|
Box::new((*self).clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Model<Video> {
|
impl Model<Video> {
|
||||||
pub async fn load_from_db(&mut self) {
|
pub async fn load_from_db(&mut self) {
|
||||||
let result = query_as!(Video, r#"SELECT title as "title!", file_path as "path!", start_time as "start_time!: f32", end_time as "end_time!: f32", loop as "looping!", id as "id: i32" from videos"#).fetch_all(&mut self.db).await;
|
let result = query_as!(Video, r#"SELECT title as "title!", file_path as "path!", start_time as "start_time!: f32", end_time as "end_time!: f32", loop as "looping!", id as "id: i32" from videos"#).fetch_all(&mut self.db).await;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use clap::{command, Parser};
|
use clap::{command, Parser};
|
||||||
|
use core::service_items::ServiceItem;
|
||||||
use cosmic::app::{Core, Settings, Task};
|
use cosmic::app::{Core, Settings, Task};
|
||||||
use cosmic::iced::keyboard::Key;
|
use cosmic::iced::keyboard::Key;
|
||||||
use cosmic::iced::window::{Mode, Position};
|
use cosmic::iced::window::{Mode, Position};
|
||||||
|
@ -93,6 +94,8 @@ struct App {
|
||||||
enum Message {
|
enum Message {
|
||||||
Present(presenter::Message),
|
Present(presenter::Message),
|
||||||
File(PathBuf),
|
File(PathBuf),
|
||||||
|
DndEnter(ServiceItem),
|
||||||
|
DndDrop(ServiceItem),
|
||||||
OpenWindow,
|
OpenWindow,
|
||||||
CloseWindow(Option<window::Id>),
|
CloseWindow(Option<window::Id>),
|
||||||
WindowOpened(window::Id, Option<Point>),
|
WindowOpened(window::Id, Option<Point>),
|
||||||
|
@ -429,6 +432,8 @@ impl cosmic::Application for App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::Quit => cosmic::iced::exit(),
|
Message::Quit => cosmic::iced::exit(),
|
||||||
|
Message::DndEnter(service_item) => todo!(),
|
||||||
|
Message::DndDrop(service_item) => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::time::Duration;
|
use std::{rc::Rc, time::Duration};
|
||||||
|
|
||||||
use cosmic::{
|
use cosmic::{
|
||||||
dialog::ashpd::url::Url,
|
dialog::ashpd::url::Url,
|
||||||
|
@ -13,7 +13,6 @@ use cosmic::{
|
||||||
stack,
|
stack,
|
||||||
},
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
theme::CosmicTheme,
|
|
||||||
widget::{
|
widget::{
|
||||||
container, image, mouse_area, responsive, scrollable,
|
container, image, mouse_area, responsive, scrollable,
|
||||||
Container, Responsive, Row, Space,
|
Container, Responsive, Row, Space,
|
||||||
|
@ -25,14 +24,17 @@ use miette::{Context, IntoDiagnostic, Result};
|
||||||
use tracing::{debug, error, info};
|
use tracing::{debug, error, info};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
core::{service_items::ServiceItem, slide::Slide},
|
core::{
|
||||||
|
service_items::{ServiceItem, ServiceItemModel},
|
||||||
|
slide::Slide,
|
||||||
|
},
|
||||||
BackgroundKind,
|
BackgroundKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
// #[derive(Default, Clone, Debug)]
|
// #[derive(Default, Clone, Debug)]
|
||||||
pub(crate) struct Presenter {
|
pub(crate) struct Presenter {
|
||||||
pub slides: Vec<Slide>,
|
pub slides: Vec<Slide>,
|
||||||
pub items: Vec<ServiceItem>,
|
pub items: ServiceItemModel,
|
||||||
pub current_slide: Slide,
|
pub current_slide: Slide,
|
||||||
pub current_slide_index: u16,
|
pub current_slide_index: u16,
|
||||||
pub video: Option<Video>,
|
pub video: Option<Video>,
|
||||||
|
@ -56,7 +58,47 @@ impl Presenter {
|
||||||
pub fn with_slides(slides: Vec<Slide>) -> Self {
|
pub fn with_slides(slides: Vec<Slide>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
slides: slides.clone(),
|
slides: slides.clone(),
|
||||||
items: vec![],
|
items: ServiceItemModel::default(),
|
||||||
|
current_slide: slides[0].clone(),
|
||||||
|
current_slide_index: 0,
|
||||||
|
video: {
|
||||||
|
if let Some(slide) = slides.get(0) {
|
||||||
|
let path = slide.background().path.clone();
|
||||||
|
if path.exists() {
|
||||||
|
let url = Url::from_file_path(path).unwrap();
|
||||||
|
let result = Video::new(&url);
|
||||||
|
match result {
|
||||||
|
Ok(mut v) => {
|
||||||
|
v.set_paused(true);
|
||||||
|
Some(v)
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("Had an error creating the video object: {e}");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
},
|
||||||
|
video_position: 0.0,
|
||||||
|
hovered_slide: -1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_items(items: Vec<ServiceItem>) -> Self {
|
||||||
|
let items = ServiceItemModel::from(items);
|
||||||
|
let slides = if let Ok(slides) = items.to_slides() {
|
||||||
|
slides
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
};
|
||||||
|
Self {
|
||||||
|
slides: slides.clone(),
|
||||||
|
items: items.into(),
|
||||||
current_slide: slides[0].clone(),
|
current_slide: slides[0].clone(),
|
||||||
current_slide_index: 0,
|
current_slide_index: 0,
|
||||||
video: {
|
video: {
|
||||||
|
|
Loading…
Reference in a new issue