ensuring that slides can be created by different things
This commit is contained in:
parent
887fc733e7
commit
8f065380aa
7 changed files with 270 additions and 50 deletions
23
Cargo.lock
generated
23
Cargo.lock
generated
|
@ -499,6 +499,7 @@ dependencies = [
|
|||
"dirs",
|
||||
"fastrand 2.1.1",
|
||||
"obws",
|
||||
"pretty_assertions",
|
||||
"quote",
|
||||
"reqwest",
|
||||
"rfd",
|
||||
|
@ -781,6 +782,12 @@ dependencies = [
|
|||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diff"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
|
@ -2054,6 +2061,16 @@ dependencies = [
|
|||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pretty_assertions"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d"
|
||||
dependencies = [
|
||||
"diff",
|
||||
"yansi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "1.3.1"
|
||||
|
@ -3646,6 +3663,12 @@ dependencies = [
|
|||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
|
||||
|
||||
[[package]]
|
||||
name = "youtube_dl"
|
||||
version = "0.8.1"
|
||||
|
|
|
@ -42,6 +42,7 @@ time = { version = "0.3.29", features = ["formatting", "macros"] }
|
|||
obws = "0.13.0"
|
||||
reqwest = "0.11.23"
|
||||
color-eyre = "0.6.3"
|
||||
pretty_assertions = "1.4.1"
|
||||
# ffmpeg-next = "6.0.0"
|
||||
|
||||
# cxx-qt-build generates C++ code from the `#[cxx_qt::bridge]` module
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
pub struct Image {
|
||||
title: String,
|
||||
_title: String,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -91,5 +91,4 @@ pub trait Modeling {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use std::path::PathBuf;
|
||||
use std::{error::Error, fmt::Display, path::PathBuf};
|
||||
|
||||
use color_eyre::eyre::Result;
|
||||
use color_eyre::eyre::{eyre, Result};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::{
|
||||
images::Image, kinds::ServiceItemKind,
|
||||
|
@ -21,19 +22,81 @@ pub enum TextAlignment {
|
|||
BottomRight,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub struct Background {
|
||||
path: PathBuf,
|
||||
kind: BackgroundKind,
|
||||
}
|
||||
|
||||
impl TryFrom<String> for Background {
|
||||
type Error = ParseError;
|
||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||
let extension = value.rsplit_once('.').unwrap_or_default();
|
||||
match extension.0 {
|
||||
"jpg" | "png" | "webp" => Ok(Self {
|
||||
path: PathBuf::from(value),
|
||||
kind: BackgroundKind::Image,
|
||||
}),
|
||||
"mp4" | "mkv" | "webm" => Ok(Self {
|
||||
path: PathBuf::from(value),
|
||||
kind: BackgroundKind::Video,
|
||||
}),
|
||||
_ => Err(ParseError::NonBackgroundFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<PathBuf> for Background {
|
||||
type Error = ParseError;
|
||||
fn try_from(value: PathBuf) -> Result<Self, Self::Error> {
|
||||
let extension = value.extension().unwrap_or_default().to_str().unwrap_or_default();
|
||||
match extension {
|
||||
"jpg" | "png" | "webp" => Ok(Self {
|
||||
path: value,
|
||||
kind: BackgroundKind::Image,
|
||||
}),
|
||||
"mp4" | "mkv" | "webm" => Ok(Self {
|
||||
path: value,
|
||||
kind: BackgroundKind::Video,
|
||||
}),
|
||||
_ => Err(ParseError::NonBackgroundFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ParseError {
|
||||
NonBackgroundFile,
|
||||
}
|
||||
|
||||
impl Error for ParseError {}
|
||||
|
||||
impl Display for ParseError {
|
||||
fn fmt(
|
||||
&self,
|
||||
f: &mut std::fmt::Formatter<'_>,
|
||||
) -> std::fmt::Result {
|
||||
let message = match self {
|
||||
Self::NonBackgroundFile => "The file is not a recognized image or video type",
|
||||
};
|
||||
write!(f, "Error: {message}")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
pub enum Background {
|
||||
pub enum BackgroundKind {
|
||||
#[default]
|
||||
Image,
|
||||
Video,
|
||||
}
|
||||
|
||||
impl From<String> for Background {
|
||||
impl From<String> for BackgroundKind {
|
||||
fn from(value: String) -> Self {
|
||||
if value == "image" {
|
||||
Background::Image
|
||||
BackgroundKind::Image
|
||||
} else {
|
||||
Background::Video
|
||||
BackgroundKind::Video
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +105,7 @@ impl From<String> for Background {
|
|||
struct Slide {
|
||||
id: i32,
|
||||
database_id: i32,
|
||||
background: PathBuf,
|
||||
background: Background,
|
||||
text: String,
|
||||
font: String,
|
||||
font_size: i32,
|
||||
|
@ -75,9 +138,13 @@ impl From<Song> for Slide {
|
|||
}
|
||||
|
||||
impl From<Video> for Slide {
|
||||
fn from(_video: Video) -> Self {
|
||||
fn from(video: Video) -> Self {
|
||||
Self {
|
||||
kind: ServiceItemKind::Video,
|
||||
background: Background::try_from(video.path).unwrap_or_default(),
|
||||
video_loop: video.looping,
|
||||
video_start_time: video.start_time.unwrap_or_default(),
|
||||
video_end_time: video.end_time.unwrap_or_default(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
@ -103,34 +170,158 @@ impl From<Presentation> for Slide {
|
|||
}
|
||||
|
||||
impl SlideModel {
|
||||
pub fn add_song_to_end(&mut self, song: Song) -> Result<()> {
|
||||
pub fn add_song(&mut self, song: Song, index: i32) -> Result<()> {
|
||||
let lyrics = song.get_lyrics()?;
|
||||
let mut slides: Vec<Slide> = lyrics
|
||||
let current_length = self.slides.len();
|
||||
let slides: Vec<Slide> = lyrics
|
||||
.iter()
|
||||
.map(|lyric| Slide {
|
||||
background: song.background.clone(),
|
||||
background: song.background.clone().unwrap_or_default(),
|
||||
text: lyric.to_owned(),
|
||||
font: song.font.clone(),
|
||||
font_size: song.font_size,
|
||||
font: song.font.clone().unwrap_or_default(),
|
||||
font_size: song.font_size.unwrap_or_default(),
|
||||
kind: ServiceItemKind::Song,
|
||||
text_alignment: song.text_alignment,
|
||||
text_alignment: song.text_alignment.unwrap_or_default(),
|
||||
..Default::default()
|
||||
})
|
||||
.collect();
|
||||
self.slides.append(&mut slides);
|
||||
for (location, slide) in slides.iter().enumerate() {
|
||||
self.slides.insert(location + index as usize, slide.clone());
|
||||
debug!("Here is the current slide_model: {:?}", self);
|
||||
}
|
||||
if self.slides.len() > current_length {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(eyre!("The size of the slides vector didn't change"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_slide(&self, index: i32) -> Option<&Slide> {
|
||||
self.slides.get(index as usize)
|
||||
}
|
||||
|
||||
pub fn add_video(&mut self, video: Video, index: i32) -> Result<()> {
|
||||
self.slides.insert(index as usize, Slide::from(video));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_video_to_end(&mut self, video: Video) -> Result<()> {
|
||||
self.slides.push(Slide::from(video));
|
||||
pub fn add_image(&mut self, image: Image, index: i32) -> Result<()> {
|
||||
self.slides.insert(index as usize, Slide::from(image));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_presentation(&mut self, presentation: Presentation, index: i32) -> Result<()> {
|
||||
self.slides.insert(index as usize, Slide::from(presentation));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::songs::Song;
|
||||
use pretty_assertions::{assert_eq, assert_ne};
|
||||
use super::*;
|
||||
|
||||
fn test_song() -> Song {
|
||||
Song {
|
||||
title: "From The Day".to_string(),
|
||||
lyrics: Some("Verse 1
|
||||
When You found me,
|
||||
I was so blind
|
||||
My sin was before me,
|
||||
I was swallowed by pride
|
||||
|
||||
Chorus 1
|
||||
But out of the darkness,
|
||||
You brought me to Your light
|
||||
You showed me new mercy
|
||||
And opened up my eyes
|
||||
|
||||
Chorus 2
|
||||
From the day
|
||||
You saved my soul
|
||||
'Til the very moment
|
||||
When I come home
|
||||
|
||||
I'll sing, I'll dance,
|
||||
My heart will overflow
|
||||
From the day
|
||||
You saved my soul
|
||||
|
||||
Verse 2
|
||||
Where brilliant light
|
||||
Is all around
|
||||
And endless joy
|
||||
Is the only sound
|
||||
|
||||
Chorus 3
|
||||
Oh, rest my heart
|
||||
Forever now
|
||||
Oh, in Your arms
|
||||
I'll always be found
|
||||
|
||||
Bridge 1
|
||||
My love is Yours
|
||||
My heart is Yours
|
||||
My life is Yours
|
||||
Forever
|
||||
|
||||
My love is Yours
|
||||
My heart is Yours
|
||||
My life is Yours
|
||||
Forever
|
||||
|
||||
Other 1
|
||||
From the Day
|
||||
I Am They
|
||||
|
||||
Other 2
|
||||
|
||||
|
||||
Ending 1
|
||||
Oh Oh Oh
|
||||
From the day
|
||||
You saved my soul".to_string()),
|
||||
verse_order: Some("O1 V1 C1 C2 O2 V2 C3 C2 O2 B1 C2 C2 E1 O2"
|
||||
.to_string()
|
||||
.split(' ')
|
||||
.map(|s| s.to_string())
|
||||
.collect()),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn test_song_verse_one() -> String {
|
||||
"When You found me,\nI was so blind\nMy sin was before me,\nI was swallowed by pride".to_string()
|
||||
}
|
||||
|
||||
fn test_song_last_verse() -> String {
|
||||
"Oh Oh Oh\nFrom the day\nYou saved my soul\n".to_string()
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_slides() {
|
||||
assert_eq!(true, true)
|
||||
pub fn test_add_song_to_slides() {
|
||||
let song = test_song();
|
||||
let mut slide_model = SlideModel::default();
|
||||
let result = slide_model.add_song(song, 0);
|
||||
match result {
|
||||
Ok(_) => {
|
||||
let slide = slide_model.get_slide(15).unwrap();
|
||||
let new_slide = Slide {
|
||||
text: test_song_last_verse(),
|
||||
..Default::default()
|
||||
};
|
||||
assert_eq!(slide, &new_slide);
|
||||
let slide = slide_model.get_slide(1).unwrap();
|
||||
let new_slide = Slide {
|
||||
text: test_song_verse_one(),
|
||||
..Default::default()
|
||||
};
|
||||
assert_eq!(slide, &new_slide);
|
||||
},
|
||||
Err(e) => {
|
||||
panic!("There was a problem adding the slide: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::{collections::HashMap, mem::replace, path::PathBuf};
|
||||
|
||||
use color_eyre::eyre::{eyre, Result};
|
||||
use sqlx::{query, Connection, SqliteConnection};
|
||||
use sqlx::query;
|
||||
use tracing::{debug, error};
|
||||
|
||||
use crate::{
|
||||
|
@ -13,15 +13,14 @@ use crate::{
|
|||
pub struct Song {
|
||||
pub title: String,
|
||||
pub lyrics: Option<String>,
|
||||
pub author: String,
|
||||
pub ccli: String,
|
||||
pub audio: PathBuf,
|
||||
pub verse_order: Vec<String>,
|
||||
pub background: PathBuf,
|
||||
pub background_type: Background,
|
||||
pub text_alignment: TextAlignment,
|
||||
pub font: String,
|
||||
pub font_size: i32,
|
||||
pub author: Option<String>,
|
||||
pub ccli: Option<String>,
|
||||
pub audio: Option<PathBuf>,
|
||||
pub verse_order: Option<Vec<String>>,
|
||||
pub background: Option<Background>,
|
||||
pub text_alignment: Option<TextAlignment>,
|
||||
pub font: Option<String>,
|
||||
pub font_size: Option<i32>,
|
||||
}
|
||||
|
||||
const VERSE_KEYWORDS: [&'static str; 24] = [
|
||||
|
@ -39,7 +38,7 @@ impl Modeling for Model<Song> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn add_to_db(&mut self, item: Self::Item) -> Result<()> {
|
||||
fn add_to_db(&mut self, _item: Self::Item) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
|
@ -91,21 +90,20 @@ impl Model<Song> {
|
|||
let _ = self.add_item(Song {
|
||||
title: song.title,
|
||||
lyrics: Some(song.lyrics),
|
||||
author: song.author,
|
||||
ccli: song.ccli,
|
||||
audio: song.audio.into(),
|
||||
verse_order: song.verse_order.split(" ").map(|s| s.to_string()).collect(),
|
||||
background: song.background.into(),
|
||||
background_type: song.background_type.into(),
|
||||
author: Some(song.author),
|
||||
ccli: Some(song.ccli),
|
||||
audio: Some(song.audio.into()),
|
||||
verse_order: Some(song.verse_order.split(" ").map(|s| s.to_string()).collect()),
|
||||
background: Some(song.background.try_into().unwrap_or_default()),
|
||||
text_alignment: {
|
||||
if song.horizontal_text_alignment == "center" && song.vertical_text_alignment == "center" {
|
||||
TextAlignment::MiddleCenter
|
||||
Some(TextAlignment::MiddleCenter)
|
||||
} else {
|
||||
TextAlignment::TopCenter
|
||||
Some(TextAlignment::TopCenter)
|
||||
}
|
||||
},
|
||||
font: song.font,
|
||||
font_size: song.font_size,
|
||||
font: Some(song.font),
|
||||
font_size: Some(song.font_size),
|
||||
});
|
||||
}
|
||||
},
|
||||
|
@ -120,6 +118,13 @@ impl Model<Song> {
|
|||
impl Song {
|
||||
pub fn get_lyrics(&self) -> Result<Vec<String>> {
|
||||
let mut lyric_list = Vec::new();
|
||||
if self.lyrics.is_none() {
|
||||
return Err(eyre!("There is no lyrics here"))
|
||||
} else if self.verse_order.is_none() {
|
||||
return Err(eyre!("There is no verse_order here"))
|
||||
} else if self.verse_order.clone().is_some_and(|v| v.is_empty()) {
|
||||
return Err(eyre!("There is no verse_order here"))
|
||||
}
|
||||
if let Some(raw_lyrics) = self.lyrics.clone() {
|
||||
let raw_lyrics = raw_lyrics.as_str();
|
||||
let verse_order = self.verse_order.clone();
|
||||
|
@ -143,7 +148,7 @@ impl Song {
|
|||
}
|
||||
lyric_map.insert(verse_title, lyric);
|
||||
|
||||
for verse in verse_order {
|
||||
for verse in verse_order.unwrap_or_default() {
|
||||
let mut verse_name = "";
|
||||
debug!(verse = verse);
|
||||
for word in VERSE_KEYWORDS.clone() {
|
||||
|
@ -188,6 +193,7 @@ impl Song {
|
|||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use pretty_assertions::{assert_eq, assert_ne};
|
||||
|
||||
#[test]
|
||||
pub fn test_song_lyrics() {
|
||||
|
@ -256,7 +262,7 @@ You saved my soul"
|
|||
"O1 V1 C1 C2 O2 V2 C3 C2 O2 B1 C2 C2 E1 O2"
|
||||
.to_string()
|
||||
.split(' ')
|
||||
.map(|s| s.to_string())
|
||||
.map(|s| Some(s.to_string()))
|
||||
.collect();
|
||||
let lyrics = song.get_lyrics();
|
||||
match lyrics {
|
||||
|
|
|
@ -7,11 +7,11 @@ use std::path::PathBuf;
|
|||
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct Video {
|
||||
title: String,
|
||||
path: PathBuf,
|
||||
start_time: f32,
|
||||
end_time: f32,
|
||||
looping: bool,
|
||||
pub title: String,
|
||||
pub path: PathBuf,
|
||||
pub start_time: Option<f32>,
|
||||
pub end_time: Option<f32>,
|
||||
pub looping: bool,
|
||||
}
|
||||
|
||||
impl Model<Video> {}
|
||||
|
@ -24,7 +24,7 @@ impl Modeling for Model<Video> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn add_to_db(&mut self, item: Self::Item) -> Result<()> {
|
||||
fn add_to_db(&mut self, _item: Self::Item) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue