Compare commits
3 commits
2413b96791
...
645411b59c
Author | SHA1 | Date | |
---|---|---|---|
645411b59c | |||
3fe77c93e2 | |||
a186d3bec4 |
16 changed files with 471 additions and 400 deletions
18
flake.nix
18
flake.nix
|
@ -8,9 +8,11 @@
|
|||
fenix.url = "github:nix-community/fenix";
|
||||
};
|
||||
|
||||
outputs = inputs: with inputs;
|
||||
flake-utils.lib.eachDefaultSystem
|
||||
(system:
|
||||
outputs =
|
||||
inputs:
|
||||
with inputs;
|
||||
flake-utils.lib.eachDefaultSystem (
|
||||
system:
|
||||
let
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
|
@ -52,6 +54,7 @@
|
|||
vulkan-tools
|
||||
libGL
|
||||
cargo-flamegraph
|
||||
bacon
|
||||
|
||||
fontconfig
|
||||
glib
|
||||
|
@ -74,11 +77,14 @@
|
|||
sqlx-cli
|
||||
cargo-watch
|
||||
];
|
||||
in rec
|
||||
in
|
||||
rec {
|
||||
devShell =
|
||||
pkgs.mkShell.override
|
||||
{
|
||||
devShell = pkgs.mkShell.override {
|
||||
# stdenv = pkgs.stdenvAdapters.useMoldLinker pkgs.clangStdenv;
|
||||
} {
|
||||
}
|
||||
{
|
||||
nativeBuildInputs = nbi;
|
||||
buildInputs = bi;
|
||||
LD_LIBRARY_PATH = "$LD_LIBRARY_PATH:${
|
||||
|
|
|
@ -17,7 +17,7 @@ use std::path::PathBuf;
|
|||
use tracing::error;
|
||||
|
||||
#[derive(
|
||||
Clone, Debug, Default, PartialEq, Serialize, Deserialize,
|
||||
Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize,
|
||||
)]
|
||||
pub struct Image {
|
||||
pub id: i32,
|
||||
|
@ -52,8 +52,9 @@ impl Content for Image {
|
|||
if self.path.exists() {
|
||||
self.path
|
||||
.file_name()
|
||||
.map(|f| f.to_string_lossy().to_string())
|
||||
.unwrap_or("Missing image".into())
|
||||
.map_or("Missing image".into(), |f| {
|
||||
f.to_string_lossy().to_string()
|
||||
})
|
||||
} else {
|
||||
"Missing image".into()
|
||||
}
|
||||
|
@ -85,7 +86,7 @@ impl From<&Value> for Image {
|
|||
let path =
|
||||
p.to_str().unwrap_or_default().to_string();
|
||||
let title =
|
||||
path.rsplit_once("/").unwrap_or_default().1;
|
||||
path.rsplit_once('/').unwrap_or_default().1;
|
||||
title.to_string()
|
||||
});
|
||||
Self {
|
||||
|
@ -154,14 +155,16 @@ impl Model<Image> {
|
|||
.await;
|
||||
match result {
|
||||
Ok(v) => {
|
||||
for image in v.into_iter() {
|
||||
for image in v {
|
||||
let _ = self.add_item(image);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("There was an error in converting images: {e}")
|
||||
error!(
|
||||
"There was an error in converting images: {e}"
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -172,7 +175,7 @@ pub async fn update_image_in_db(
|
|||
let path = image
|
||||
.path
|
||||
.to_str()
|
||||
.map(|s| s.to_string())
|
||||
.map(std::string::ToString::to_string)
|
||||
.unwrap_or_default();
|
||||
query!(
|
||||
r#"UPDATE images SET title = $2, file_path = $3 WHERE id = $1"#,
|
||||
|
|
|
@ -21,11 +21,11 @@ pub enum ServiceItemKind {
|
|||
impl std::fmt::Display for ServiceItemKind {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let s = match self {
|
||||
Self::Song(s) => "song".to_owned(),
|
||||
Self::Image(i) => "image".to_owned(),
|
||||
Self::Video(v) => "video".to_owned(),
|
||||
Self::Presentation(p) => "html".to_owned(),
|
||||
Self::Content(s) => "content".to_owned(),
|
||||
Self::Song(_) => "song".to_owned(),
|
||||
Self::Image(_) => "image".to_owned(),
|
||||
Self::Video(_) => "video".to_owned(),
|
||||
Self::Presentation(_) => "html".to_owned(),
|
||||
Self::Content(_) => "content".to_owned(),
|
||||
};
|
||||
write!(f, "{s}")
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ impl std::fmt::Display for ServiceItemKind {
|
|||
// }
|
||||
|
||||
impl From<ServiceItemKind> for String {
|
||||
fn from(val: ServiceItemKind) -> String {
|
||||
fn from(val: ServiceItemKind) -> Self {
|
||||
match val {
|
||||
ServiceItemKind::Song(_) => "song".to_owned(),
|
||||
ServiceItemKind::Video(_) => "video".to_owned(),
|
||||
|
@ -76,7 +76,9 @@ impl Display for ParseError {
|
|||
f: &mut std::fmt::Formatter<'_>,
|
||||
) -> std::fmt::Result {
|
||||
let message = match self {
|
||||
Self::UnknownType => "The type does not exist. It needs to be one of 'song', 'video', 'image', 'presentation', or 'content'",
|
||||
Self::UnknownType => {
|
||||
"The type does not exist. It needs to be one of 'song', 'video', 'image', 'presentation', or 'content'"
|
||||
}
|
||||
};
|
||||
write!(f, "Error: {message}")
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use std::mem::replace;
|
||||
|
||||
use cosmic::iced::Executor;
|
||||
use miette::{miette, Result};
|
||||
use miette::{Result, miette};
|
||||
use sqlx::{Connection, SqliteConnection};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -10,7 +9,7 @@ pub struct Model<T> {
|
|||
pub kind: LibraryKind,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Copy)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
||||
pub enum LibraryKind {
|
||||
Song,
|
||||
Video,
|
||||
|
@ -46,6 +45,7 @@ impl<T> Model<T> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_item(&self, index: i32) -> Option<&T> {
|
||||
self.items.get(index as usize)
|
||||
}
|
||||
|
|
|
@ -75,8 +75,9 @@ impl Content for Presentation {
|
|||
if self.path.exists() {
|
||||
self.path
|
||||
.file_name()
|
||||
.map(|f| f.to_string_lossy().to_string())
|
||||
.unwrap_or("Missing presentation".into())
|
||||
.map_or("Missing presentation".into(), |f| {
|
||||
f.to_string_lossy().to_string()
|
||||
})
|
||||
} else {
|
||||
"Missing presentation".into()
|
||||
}
|
||||
|
@ -190,14 +191,16 @@ impl ServiceTrait for Presentation {
|
|||
}
|
||||
|
||||
impl Presentation {
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
title: "".to_string(),
|
||||
title: String::new(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_kind(&self) -> &PresKind {
|
||||
#[must_use]
|
||||
pub const fn get_kind(&self) -> &PresKind {
|
||||
&self.kind
|
||||
}
|
||||
}
|
||||
|
@ -240,7 +243,7 @@ impl Model<Presentation> {
|
|||
.await;
|
||||
match result {
|
||||
Ok(v) => {
|
||||
for presentation in v.into_iter() {
|
||||
for presentation in v {
|
||||
let _ = self.add_item(Presentation {
|
||||
id: presentation.id,
|
||||
title: presentation.title,
|
||||
|
@ -267,7 +270,7 @@ pub async fn update_presentation_in_db(
|
|||
let path = presentation
|
||||
.path
|
||||
.to_str()
|
||||
.map(|s| s.to_string())
|
||||
.map(std::string::ToString::to_string)
|
||||
.unwrap_or_default();
|
||||
let html = presentation.kind == PresKind::Html;
|
||||
query!(
|
||||
|
|
|
@ -79,13 +79,13 @@ impl AsMimeTypes for ServiceItem {
|
|||
impl From<&ServiceItem> for Value {
|
||||
fn from(value: &ServiceItem) -> Self {
|
||||
match &value.kind {
|
||||
ServiceItemKind::Song(song) => Value::from(song),
|
||||
ServiceItemKind::Video(video) => Value::from(video),
|
||||
ServiceItemKind::Image(image) => Value::from(image),
|
||||
ServiceItemKind::Song(song) => Self::from(song),
|
||||
ServiceItemKind::Video(video) => Self::from(video),
|
||||
ServiceItemKind::Image(image) => Self::from(image),
|
||||
ServiceItemKind::Presentation(presentation) => {
|
||||
Value::from(presentation)
|
||||
Self::from(presentation)
|
||||
}
|
||||
ServiceItemKind::Content(slide) => Value::from(slide),
|
||||
ServiceItemKind::Content(slide) => Self::from(slide),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -172,8 +172,8 @@ impl From<&Value> for ServiceItem {
|
|||
} else if let Some(background) =
|
||||
list.get(background_pos)
|
||||
{
|
||||
match background {
|
||||
Value::List(item) => match &item[0] {
|
||||
if let Value::List(item) = background {
|
||||
match &item[0] {
|
||||
Value::Symbol(Symbol(s))
|
||||
if s == "image" =>
|
||||
{
|
||||
|
@ -196,30 +196,29 @@ impl From<&Value> for ServiceItem {
|
|||
))
|
||||
}
|
||||
_ => todo!(),
|
||||
},
|
||||
_ => {
|
||||
}
|
||||
} else {
|
||||
error!(
|
||||
"There is no background here: {:?}",
|
||||
background
|
||||
);
|
||||
ServiceItem::default()
|
||||
}
|
||||
Self::default()
|
||||
}
|
||||
} else {
|
||||
error!(
|
||||
"There is no background here: {:?}",
|
||||
background_pos
|
||||
);
|
||||
ServiceItem::default()
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
Value::Symbol(Symbol(s)) if s == "song" => {
|
||||
let song = lisp_to_song(list.clone());
|
||||
Self::from(&song)
|
||||
}
|
||||
_ => ServiceItem::default(),
|
||||
_ => Self::default(),
|
||||
},
|
||||
_ => ServiceItem::default(),
|
||||
_ => Self::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -258,7 +257,7 @@ impl From<&Song> for ServiceItem {
|
|||
kind: ServiceItemKind::Song(song.clone()),
|
||||
database_id: song.id,
|
||||
title: song.title.clone(),
|
||||
slides: slides,
|
||||
slides,
|
||||
..Default::default()
|
||||
}
|
||||
} else {
|
||||
|
@ -279,7 +278,7 @@ impl From<&Video> for ServiceItem {
|
|||
kind: ServiceItemKind::Video(video.clone()),
|
||||
database_id: video.id,
|
||||
title: video.title.clone(),
|
||||
slides: slides,
|
||||
slides,
|
||||
..Default::default()
|
||||
}
|
||||
} else {
|
||||
|
@ -300,7 +299,7 @@ impl From<&Image> for ServiceItem {
|
|||
kind: ServiceItemKind::Image(image.clone()),
|
||||
database_id: image.id,
|
||||
title: image.title.clone(),
|
||||
slides: slides,
|
||||
slides,
|
||||
..Default::default()
|
||||
}
|
||||
} else {
|
||||
|
@ -323,7 +322,7 @@ impl From<&Presentation> for ServiceItem {
|
|||
),
|
||||
database_id: presentation.id,
|
||||
title: presentation.title.clone(),
|
||||
slides: slides,
|
||||
slides,
|
||||
..Default::default()
|
||||
},
|
||||
Err(e) => {
|
||||
|
|
|
@ -107,9 +107,9 @@ impl TryFrom<&Background> for Video {
|
|||
fn try_from(
|
||||
value: &Background,
|
||||
) -> std::result::Result<Self, Self::Error> {
|
||||
Video::new(
|
||||
Self::new(
|
||||
&url::Url::from_file_path(value.path.clone())
|
||||
.map_err(|_| ParseError::BackgroundNotVideo)?,
|
||||
.map_err(|()| ParseError::BackgroundNotVideo)?,
|
||||
)
|
||||
.map_err(|_| ParseError::BackgroundNotVideo)
|
||||
}
|
||||
|
@ -121,9 +121,9 @@ impl TryFrom<Background> for Video {
|
|||
fn try_from(
|
||||
value: Background,
|
||||
) -> std::result::Result<Self, Self::Error> {
|
||||
Video::new(
|
||||
Self::new(
|
||||
&url::Url::from_file_path(value.path)
|
||||
.map_err(|_| ParseError::BackgroundNotVideo)?,
|
||||
.map_err(|()| ParseError::BackgroundNotVideo)?,
|
||||
)
|
||||
.map_err(|_| ParseError::BackgroundNotVideo)
|
||||
}
|
||||
|
@ -132,7 +132,7 @@ impl TryFrom<Background> for Video {
|
|||
impl TryFrom<String> for Background {
|
||||
type Error = ParseError;
|
||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||
Background::try_from(value.as_str())
|
||||
Self::try_from(value.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -147,7 +147,7 @@ impl TryFrom<PathBuf> for Background {
|
|||
.to_str()
|
||||
.unwrap()
|
||||
.to_string();
|
||||
let path = path.replace("~", &home);
|
||||
let path = path.replace('~', &home);
|
||||
PathBuf::from(path)
|
||||
} else {
|
||||
path
|
||||
|
@ -192,10 +192,10 @@ impl TryFrom<&str> for Background {
|
|||
type Error = ParseError;
|
||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||
let value = value.trim_start_matches("file://");
|
||||
if value.starts_with("~") {
|
||||
if value.starts_with('~') {
|
||||
if let Some(home) = dirs::home_dir() {
|
||||
if let Some(home) = home.to_str() {
|
||||
let value = value.replace("~", home);
|
||||
let value = value.replace('~', home);
|
||||
Self::try_from(PathBuf::from(value))
|
||||
} else {
|
||||
Self::try_from(PathBuf::from(value))
|
||||
|
@ -252,9 +252,9 @@ impl Display for ParseError {
|
|||
impl From<String> for BackgroundKind {
|
||||
fn from(value: String) -> Self {
|
||||
if value == "image" {
|
||||
BackgroundKind::Image
|
||||
Self::Image
|
||||
} else {
|
||||
BackgroundKind::Video
|
||||
Self::Video
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -281,7 +281,7 @@ impl Slide {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn set_font_size(mut self, font_size: i32) -> Self {
|
||||
pub const fn set_font_size(mut self, font_size: i32) -> Self {
|
||||
self.font_size = font_size;
|
||||
self
|
||||
}
|
||||
|
@ -291,12 +291,12 @@ impl Slide {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn set_pdf_index(mut self, pdf_index: u32) -> Self {
|
||||
pub const fn set_pdf_index(mut self, pdf_index: u32) -> Self {
|
||||
self.pdf_index = pdf_index;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn background(&self) -> &Background {
|
||||
pub const fn background(&self) -> &Background {
|
||||
&self.background
|
||||
}
|
||||
|
||||
|
@ -304,11 +304,11 @@ impl Slide {
|
|||
self.text.clone()
|
||||
}
|
||||
|
||||
pub fn text_alignment(&self) -> TextAlignment {
|
||||
pub const fn text_alignment(&self) -> TextAlignment {
|
||||
self.text_alignment
|
||||
}
|
||||
|
||||
pub fn font_size(&self) -> i32 {
|
||||
pub const fn font_size(&self) -> i32 {
|
||||
self.font_size
|
||||
}
|
||||
|
||||
|
@ -316,7 +316,7 @@ impl Slide {
|
|||
self.font.clone()
|
||||
}
|
||||
|
||||
pub fn video_loop(&self) -> bool {
|
||||
pub const fn video_loop(&self) -> bool {
|
||||
self.video_loop
|
||||
}
|
||||
|
||||
|
@ -328,13 +328,13 @@ impl Slide {
|
|||
self.pdf_page.clone()
|
||||
}
|
||||
|
||||
pub fn pdf_index(&self) -> u32 {
|
||||
pub const fn pdf_index(&self) -> u32 {
|
||||
self.pdf_index
|
||||
}
|
||||
|
||||
pub fn song_slides(song: &Song) -> Result<Vec<Self>> {
|
||||
let lyrics = song.get_lyrics()?;
|
||||
let slides: Vec<Slide> = lyrics
|
||||
let slides: Vec<Self> = lyrics
|
||||
.iter()
|
||||
.map(|l| {
|
||||
let song = song.clone();
|
||||
|
@ -358,7 +358,7 @@ impl Slide {
|
|||
Ok(slides)
|
||||
}
|
||||
|
||||
pub(crate) fn set_index(&mut self, index: i32) {
|
||||
pub(crate) const fn set_index(&mut self, index: i32) {
|
||||
self.id = index;
|
||||
}
|
||||
|
||||
|
@ -381,7 +381,7 @@ impl From<&Value> for Slide {
|
|||
fn from(value: &Value) -> Self {
|
||||
match value {
|
||||
Value::List(list) => lisp_to_slide(list),
|
||||
_ => Slide::default(),
|
||||
_ => Self::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -404,7 +404,7 @@ fn lisp_to_slide(lisp: &Vec<Value>) -> Slide {
|
|||
slide = slide.background(lisp_to_background(background));
|
||||
} else {
|
||||
slide = slide.background(Background::default());
|
||||
};
|
||||
}
|
||||
|
||||
let text_position = lisp.iter().position(|v| match v {
|
||||
Value::List(vec) => {
|
||||
|
@ -691,9 +691,7 @@ impl SlideBuilder {
|
|||
|
||||
impl Image {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
..Default::default()
|
||||
}
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
use std::{collections::HashMap, option::Option, path::PathBuf};
|
||||
|
||||
use cosmic::iced::Executor;
|
||||
use crisp::types::{Keyword, Symbol, Value};
|
||||
use miette::{miette, IntoDiagnostic, Result};
|
||||
use miette::{IntoDiagnostic, Result, miette};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::{
|
||||
pool::PoolConnection, query, sqlite::SqliteRow, FromRow, Row,
|
||||
Sqlite, SqliteConnection, SqlitePool,
|
||||
FromRow, Row, Sqlite, SqliteConnection, SqlitePool,
|
||||
pool::PoolConnection, query, sqlite::SqliteRow,
|
||||
};
|
||||
use tracing::error;
|
||||
|
||||
use crate::{core::slide, Slide, SlideBuilder};
|
||||
use crate::{Slide, SlideBuilder, core::slide};
|
||||
|
||||
use super::{
|
||||
content::Content,
|
||||
|
@ -128,7 +127,9 @@ impl FromRow<'_, SqliteRow> for Song {
|
|||
})),
|
||||
verse_order: Some({
|
||||
let str: &str = row.try_get(0)?;
|
||||
str.split(' ').map(|s| s.to_string()).collect()
|
||||
str.split(' ')
|
||||
.map(std::string::ToString::to_string)
|
||||
.collect()
|
||||
}),
|
||||
background: {
|
||||
let string: String = row.try_get(7)?;
|
||||
|
@ -250,9 +251,7 @@ pub fn lisp_to_song(list: Vec<Value>) -> Song {
|
|||
.position(|v| v == &Value::Keyword(Keyword::from("title")))
|
||||
{
|
||||
let pos = key_pos + 1;
|
||||
list.get(pos)
|
||||
.map(String::from)
|
||||
.unwrap_or(String::from("song"))
|
||||
list.get(pos).map_or(String::from("song"), String::from)
|
||||
} else {
|
||||
String::from("song")
|
||||
};
|
||||
|
@ -319,30 +318,30 @@ pub fn lisp_to_song(list: Vec<Value>) -> Song {
|
|||
let lyric = String::from(&lyric[1]);
|
||||
|
||||
let verse_title = match lyric_verse.as_str() {
|
||||
"i1" => r#"\n\nIntro 1\n"#,
|
||||
"i2" => r#"\n\nIntro 1\n"#,
|
||||
"v1" => r#"\n\nVerse 1\n"#,
|
||||
"v2" => r#"\n\nVerse 2\n"#,
|
||||
"v3" => r#"\n\nVerse 3\n"#,
|
||||
"v4" => r#"\n\nVerse 4\n"#,
|
||||
"v5" => r#"\n\nVerse 5\n"#,
|
||||
"c1" => r#"\n\nChorus 1\n"#,
|
||||
"c2" => r#"\n\nChorus 2\n"#,
|
||||
"c3" => r#"\n\nChorus 3\n"#,
|
||||
"c4" => r#"\n\nChorus 4\n"#,
|
||||
"b1" => r#"\n\nBridge 1\n"#,
|
||||
"b2" => r#"\n\nBridge 2\n"#,
|
||||
"e1" => r#"\n\nEnding 1\n"#,
|
||||
"e2" => r#"\n\nEnding 2\n"#,
|
||||
"o1" => r#"\n\nOther 1\n"#,
|
||||
"o2" => r#"\n\nOther 2\n"#,
|
||||
"i1" => r"\n\nIntro 1\n",
|
||||
"i2" => r"\n\nIntro 1\n",
|
||||
"v1" => r"\n\nVerse 1\n",
|
||||
"v2" => r"\n\nVerse 2\n",
|
||||
"v3" => r"\n\nVerse 3\n",
|
||||
"v4" => r"\n\nVerse 4\n",
|
||||
"v5" => r"\n\nVerse 5\n",
|
||||
"c1" => r"\n\nChorus 1\n",
|
||||
"c2" => r"\n\nChorus 2\n",
|
||||
"c3" => r"\n\nChorus 3\n",
|
||||
"c4" => r"\n\nChorus 4\n",
|
||||
"b1" => r"\n\nBridge 1\n",
|
||||
"b2" => r"\n\nBridge 2\n",
|
||||
"e1" => r"\n\nEnding 1\n",
|
||||
"e2" => r"\n\nEnding 2\n",
|
||||
"o1" => r"\n\nOther 1\n",
|
||||
"o2" => r"\n\nOther 2\n",
|
||||
_ => "",
|
||||
};
|
||||
|
||||
let lyric = format!("{verse_title}{lyric}");
|
||||
let lyric = lyric.replace(
|
||||
"\\n", r#"
|
||||
"#,
|
||||
"\\n", r"
|
||||
",
|
||||
);
|
||||
lyrics.push(lyric);
|
||||
}
|
||||
|
@ -392,15 +391,15 @@ impl Model<Song> {
|
|||
let result = query(r#"SELECT verse_order as "verse_order!", font_size as "font_size!: i32", background_type as "background_type!", horizontal_text_alignment as "horizontal_text_alignment!", vertical_text_alignment as "vertical_text_alignment!", title as "title!", font as "font!", background as "background!", lyrics as "lyrics!", ccli as "ccli!", author as "author!", audio as "audio!", id as "id: i32" from songs"#).fetch_all(db).await;
|
||||
match result {
|
||||
Ok(s) => {
|
||||
for song in s.into_iter() {
|
||||
for song in s {
|
||||
match Song::from_row(&song) {
|
||||
Ok(song) => {
|
||||
let _ = self.add_item(song);
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Could not convert song: {e}")
|
||||
error!("Could not convert song: {e}");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
|
@ -425,7 +424,7 @@ pub async fn update_song_in_db(
|
|||
})
|
||||
.collect::<String>()
|
||||
} else {
|
||||
String::from("")
|
||||
String::new()
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -488,13 +487,13 @@ impl Song {
|
|||
let verse_order = self.verse_order.clone();
|
||||
|
||||
let mut lyric_map = HashMap::new();
|
||||
let mut verse_title = String::from("");
|
||||
let mut lyric = String::from("");
|
||||
let mut verse_title = String::new();
|
||||
let mut lyric = String::new();
|
||||
for (i, line) in raw_lyrics.split('\n').enumerate() {
|
||||
if VERSE_KEYWORDS.contains(&line) {
|
||||
if i != 0 {
|
||||
lyric_map.insert(verse_title, lyric);
|
||||
lyric = String::from("");
|
||||
lyric = String::new();
|
||||
verse_title = line.to_string();
|
||||
} else {
|
||||
verse_title = line.to_string();
|
||||
|
@ -535,7 +534,7 @@ impl Song {
|
|||
lyric_list.push(lyric.to_string());
|
||||
} else {
|
||||
// error!("NOT WORKING!");
|
||||
};
|
||||
}
|
||||
}
|
||||
// for lyric in lyric_list.iter() {
|
||||
// debug!(lyric = ?lyric)
|
||||
|
@ -626,7 +625,27 @@ You saved my soul"
|
|||
let lyrics = song.get_lyrics();
|
||||
match lyrics {
|
||||
Ok(lyrics) => {
|
||||
assert_eq!(vec!["From the Day\nI Am They", "When You found me,\nI was so blind\nMy sin was before me,\nI was swallowed by pride", "But out of the darkness,\nYou brought me to Your light\nYou showed me new mercy\nAnd opened up my eyes", "From the day\nYou saved my soul\n'Til the very moment\nWhen I come home", "I'll sing, I'll dance,\nMy heart will overflow\nFrom the day\nYou saved my soul", "Where brilliant light\nIs all around\nAnd endless joy\nIs the only sound", "Oh, rest my heart\nForever now\nOh, in Your arms\nI'll always be found", "From the day\nYou saved my soul\n'Til the very moment\nWhen I come home", "I'll sing, I'll dance,\nMy heart will overflow\nFrom the day\nYou saved my soul", "My love is Yours\nMy heart is Yours\nMy life is Yours\nForever", "My love is Yours\nMy heart is Yours\nMy life is Yours\nForever", "From the day\nYou saved my soul\n'Til the very moment\nWhen I come home", "I'll sing, I'll dance,\nMy heart will overflow\nFrom the day\nYou saved my soul", "From the day\nYou saved my soul\n'Til the very moment\nWhen I come home", "I'll sing, I'll dance,\nMy heart will overflow\nFrom the day\nYou saved my soul", "Oh Oh Oh\nFrom the day\nYou saved my soul\n"], lyrics);
|
||||
assert_eq!(
|
||||
vec![
|
||||
"From the Day\nI Am They",
|
||||
"When You found me,\nI was so blind\nMy sin was before me,\nI was swallowed by pride",
|
||||
"But out of the darkness,\nYou brought me to Your light\nYou showed me new mercy\nAnd opened up my eyes",
|
||||
"From the day\nYou saved my soul\n'Til the very moment\nWhen I come home",
|
||||
"I'll sing, I'll dance,\nMy heart will overflow\nFrom the day\nYou saved my soul",
|
||||
"Where brilliant light\nIs all around\nAnd endless joy\nIs the only sound",
|
||||
"Oh, rest my heart\nForever now\nOh, in Your arms\nI'll always be found",
|
||||
"From the day\nYou saved my soul\n'Til the very moment\nWhen I come home",
|
||||
"I'll sing, I'll dance,\nMy heart will overflow\nFrom the day\nYou saved my soul",
|
||||
"My love is Yours\nMy heart is Yours\nMy life is Yours\nForever",
|
||||
"My love is Yours\nMy heart is Yours\nMy life is Yours\nForever",
|
||||
"From the day\nYou saved my soul\n'Til the very moment\nWhen I come home",
|
||||
"I'll sing, I'll dance,\nMy heart will overflow\nFrom the day\nYou saved my soul",
|
||||
"From the day\nYou saved my soul\n'Til the very moment\nWhen I come home",
|
||||
"I'll sing, I'll dance,\nMy heart will overflow\nFrom the day\nYou saved my soul",
|
||||
"Oh Oh Oh\nFrom the day\nYou saved my soul\n"
|
||||
],
|
||||
lyrics
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
assert!(false, "{:?}", e)
|
||||
|
|
|
@ -11,7 +11,9 @@ pub fn bg_from_video(
|
|||
video: &Path,
|
||||
screenshot: &Path,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
if !screenshot.exists() {
|
||||
if screenshot.exists() {
|
||||
debug!("Screenshot already exists");
|
||||
} else {
|
||||
let output_duration = Command::new("ffprobe")
|
||||
.args(["-i", &video.to_string_lossy()])
|
||||
.output()
|
||||
|
@ -26,9 +28,9 @@ pub fn bg_from_video(
|
|||
let mut duration = log.split_off(duration_index + 10);
|
||||
duration.truncate(11);
|
||||
// debug!("rust-duration-is: {duration}");
|
||||
let mut hours = String::from("");
|
||||
let mut minutes = String::from("");
|
||||
let mut seconds = String::from("");
|
||||
let mut hours = String::new();
|
||||
let mut minutes = String::new();
|
||||
let mut seconds = String::new();
|
||||
for (i, c) in duration.chars().enumerate() {
|
||||
if i <= 1 {
|
||||
hours.push(c);
|
||||
|
@ -63,8 +65,6 @@ pub fn bg_from_video(
|
|||
.expect("failed to execute ffmpeg");
|
||||
// io::stdout().write_all(&output.stdout).unwrap();
|
||||
// io::stderr().write_all(&output.stderr).unwrap();
|
||||
} else {
|
||||
debug!("Screenshot already exists");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -7,13 +7,12 @@ use super::{
|
|||
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,
|
||||
Sqlite, SqliteConnection, SqlitePool, pool::PoolConnection,
|
||||
query, query_as,
|
||||
};
|
||||
use std::path::PathBuf;
|
||||
use tracing::error;
|
||||
|
@ -57,8 +56,9 @@ impl Content for Video {
|
|||
if self.path.exists() {
|
||||
self.path
|
||||
.file_name()
|
||||
.map(|f| f.to_string_lossy().to_string())
|
||||
.unwrap_or("Missing video".into())
|
||||
.map_or("Missing video".into(), |f| {
|
||||
f.to_string_lossy().to_string()
|
||||
})
|
||||
} else {
|
||||
"Missing video".into()
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ impl From<&Value> for Video {
|
|||
let path =
|
||||
p.to_str().unwrap_or_default().to_string();
|
||||
let title =
|
||||
path.rsplit_once("/").unwrap_or_default().1;
|
||||
path.rsplit_once('/').unwrap_or_default().1;
|
||||
title.to_string()
|
||||
});
|
||||
|
||||
|
@ -124,8 +124,7 @@ impl From<&Value> for Video {
|
|||
}) {
|
||||
let pos = loop_pos + 1;
|
||||
list.get(pos)
|
||||
.map(|l| String::from(l) == *"true")
|
||||
.unwrap_or_default()
|
||||
.is_some_and(|l| String::from(l) == *"true")
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
@ -193,14 +192,16 @@ impl Model<Video> {
|
|||
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(db).await;
|
||||
match result {
|
||||
Ok(v) => {
|
||||
for video in v.into_iter() {
|
||||
for video in v {
|
||||
let _ = self.add_item(video);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("There was an error in converting videos: {e}")
|
||||
error!(
|
||||
"There was an error in converting videos: {e}"
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -211,7 +212,7 @@ pub async fn update_video_in_db(
|
|||
let path = video
|
||||
.path
|
||||
.to_str()
|
||||
.map(|s| s.to_string())
|
||||
.map(std::string::ToString::to_string)
|
||||
.unwrap_or_default();
|
||||
query!(
|
||||
r#"UPDATE videos SET title = $2, file_path = $3, start_time = $4, end_time = $5, loop = $6 WHERE id = $1"#,
|
||||
|
|
104
src/main.rs
104
src/main.rs
|
@ -1,31 +1,32 @@
|
|||
use clap::{command, Parser};
|
||||
use clap::{Parser, command};
|
||||
use core::service_items::ServiceItem;
|
||||
use core::slide::*;
|
||||
use core::slide::{
|
||||
Background, BackgroundKind, Slide, SlideBuilder, TextAlignment,
|
||||
};
|
||||
use core::songs::Song;
|
||||
use cosmic::app::context_drawer::ContextDrawer;
|
||||
use cosmic::app::{Core, Settings, Task};
|
||||
use cosmic::iced::alignment::Vertical;
|
||||
use cosmic::iced::keyboard::{Key, Modifiers};
|
||||
use cosmic::iced::window::{Mode, Position};
|
||||
use cosmic::iced::{self, event, window, Length, Point};
|
||||
use cosmic::iced::{self, Length, Point, event, window};
|
||||
use cosmic::iced_futures::Subscription;
|
||||
use cosmic::iced_widget::{column, row, stack};
|
||||
use cosmic::theme;
|
||||
use cosmic::widget::Container;
|
||||
use cosmic::widget::dnd_destination::dnd_destination;
|
||||
use cosmic::widget::nav_bar::nav_bar_style;
|
||||
use cosmic::widget::segmented_button::Entity;
|
||||
use cosmic::widget::text;
|
||||
use cosmic::widget::tooltip::Position as TPosition;
|
||||
use cosmic::widget::{
|
||||
button, horizontal_space, mouse_area, nav_bar, search_input,
|
||||
tooltip, vertical_space, Space,
|
||||
Space, button, horizontal_space, mouse_area, nav_bar,
|
||||
search_input, tooltip, vertical_space,
|
||||
};
|
||||
use cosmic::widget::{icon, slider};
|
||||
use cosmic::{executor, Application, ApplicationExt, Element};
|
||||
use cosmic::{widget::Container, Theme};
|
||||
use cosmic::{Application, ApplicationExt, Element, executor};
|
||||
use crisp::types::Value;
|
||||
use lisp::parse_lisp;
|
||||
use miette::{miette, Result};
|
||||
use miette::{Result, miette};
|
||||
use rayon::prelude::*;
|
||||
use resvg::usvg::fontdb;
|
||||
use std::fs::read_to_string;
|
||||
|
@ -34,10 +35,10 @@ use std::sync::Arc;
|
|||
use tracing::{debug, level_filters::LevelFilter};
|
||||
use tracing::{error, warn};
|
||||
use tracing_subscriber::EnvFilter;
|
||||
use ui::EditorMode;
|
||||
use ui::library::{self, Library};
|
||||
use ui::presenter::{self, Presenter};
|
||||
use ui::song_editor::{self, SongEditor};
|
||||
use ui::EditorMode;
|
||||
|
||||
use crate::core::kinds::ServiceItemKind;
|
||||
use crate::ui::text_svg::{self};
|
||||
|
@ -94,9 +95,9 @@ fn main() -> Result<()> {
|
|||
.map_err(|e| miette!("Invalid things... {}", e))
|
||||
}
|
||||
|
||||
fn theme(_state: &App) -> Theme {
|
||||
Theme::dark()
|
||||
}
|
||||
// fn theme(_state: &App) -> Theme {
|
||||
// Theme::dark()
|
||||
// }
|
||||
|
||||
struct App {
|
||||
core: Core,
|
||||
|
@ -135,9 +136,6 @@ enum Message {
|
|||
Quit,
|
||||
Key(Key, Modifiers),
|
||||
None,
|
||||
DndLeave(Entity),
|
||||
DndEnter(Entity, Vec<String>),
|
||||
DndDrop,
|
||||
EditorToggle(bool),
|
||||
ChangeServiceItem(usize),
|
||||
AddServiceItem(usize, ServiceItem),
|
||||
|
@ -233,11 +231,11 @@ impl cosmic::Application for App {
|
|||
// }
|
||||
|
||||
// nav_model.activate_position(0);
|
||||
let mut app = App {
|
||||
let mut app = Self {
|
||||
presenter,
|
||||
core,
|
||||
nav_model,
|
||||
service: items.clone(),
|
||||
service: items,
|
||||
file: PathBuf::default(),
|
||||
windows,
|
||||
presentation_open: false,
|
||||
|
@ -248,7 +246,7 @@ impl cosmic::Application for App {
|
|||
song_editor,
|
||||
searching: false,
|
||||
search_results: vec![],
|
||||
search_query: "".into(),
|
||||
search_query: String::new(),
|
||||
search_id: cosmic::widget::Id::unique(),
|
||||
current_item: (0, 0),
|
||||
library_dragged_item: None,
|
||||
|
@ -259,11 +257,11 @@ impl cosmic::Application for App {
|
|||
|
||||
if input.ui {
|
||||
debug!("main view");
|
||||
batch.push(app.update_title())
|
||||
batch.push(app.update_title());
|
||||
} else {
|
||||
debug!("window view");
|
||||
batch.push(app.show_window())
|
||||
};
|
||||
batch.push(app.show_window());
|
||||
}
|
||||
|
||||
batch.push(app.add_library());
|
||||
// batch.push(app.add_service(items, Arc::clone(&fontdb)));
|
||||
|
@ -403,7 +401,7 @@ impl cosmic::Application for App {
|
|||
.fold(0, |a, item| a + item.slides.len());
|
||||
|
||||
let total_slides_text =
|
||||
format!("Total Slides: {}", total_slides);
|
||||
format!("Total Slides: {total_slides}");
|
||||
let row = row![
|
||||
text::body(total_items_text),
|
||||
text::body(total_slides_text)
|
||||
|
@ -635,7 +633,7 @@ impl cosmic::Application for App {
|
|||
cosmic::Action::App(
|
||||
Message::Present(m),
|
||||
)
|
||||
}))
|
||||
}));
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
|
@ -661,7 +659,7 @@ impl cosmic::Application for App {
|
|||
m,
|
||||
),
|
||||
)
|
||||
}))
|
||||
}));
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
|
@ -694,7 +692,7 @@ impl cosmic::Application for App {
|
|||
cosmic::Action::App(
|
||||
Message::Present(m),
|
||||
)
|
||||
}))
|
||||
}));
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
|
@ -735,7 +733,7 @@ impl cosmic::Application for App {
|
|||
m,
|
||||
),
|
||||
)
|
||||
}))
|
||||
}));
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
|
@ -816,10 +814,8 @@ impl cosmic::Application for App {
|
|||
});
|
||||
|
||||
self.windows.push(id);
|
||||
_ = self.set_window_title(
|
||||
format!("window_{}", count),
|
||||
id,
|
||||
);
|
||||
_ = self
|
||||
.set_window_title(format!("window_{count}"), id);
|
||||
|
||||
spawn_window.map(|id| {
|
||||
cosmic::Action::App(Message::WindowOpened(
|
||||
|
@ -873,34 +869,6 @@ impl cosmic::Application for App {
|
|||
Task::none()
|
||||
}
|
||||
Message::Quit => cosmic::iced::exit(),
|
||||
Message::DndEnter(entity, data) => {
|
||||
debug!(?entity);
|
||||
debug!(?data);
|
||||
Task::none()
|
||||
}
|
||||
Message::DndDrop => {
|
||||
// debug!(?entity);
|
||||
// debug!(?action);
|
||||
// debug!(?service_item);
|
||||
|
||||
if let Some(library) = &self.library
|
||||
&& let Some((lib, item)) = library.dragged_item
|
||||
{
|
||||
// match lib {
|
||||
// core::model::LibraryKind::Song => ,
|
||||
// core::model::LibraryKind::Video => todo!(),
|
||||
// core::model::LibraryKind::Image => todo!(),
|
||||
// core::model::LibraryKind::Presentation => todo!(),
|
||||
// }
|
||||
let item = library.get_song(item).unwrap();
|
||||
let item = ServiceItem::from(item);
|
||||
self.nav_model
|
||||
.insert()
|
||||
.text(item.title.clone())
|
||||
.data(item);
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
Message::AddLibrary(library) => {
|
||||
self.library = Some(library);
|
||||
Task::none()
|
||||
|
@ -910,10 +878,6 @@ impl cosmic::Application for App {
|
|||
Task::none()
|
||||
}
|
||||
Message::None => Task::none(),
|
||||
Message::DndLeave(entity) => {
|
||||
// debug!(?entity);
|
||||
Task::none()
|
||||
}
|
||||
Message::EditorToggle(edit) => {
|
||||
if edit {
|
||||
self.editor_mode = Some(EditorMode::Song);
|
||||
|
@ -992,7 +956,7 @@ impl cosmic::Application for App {
|
|||
Task::none()
|
||||
}
|
||||
Message::CloseSearch => {
|
||||
self.search_query = "".into();
|
||||
self.search_query = String::new();
|
||||
self.search_results = vec![];
|
||||
self.searching = false;
|
||||
Task::none()
|
||||
|
@ -1006,12 +970,12 @@ impl cosmic::Application for App {
|
|||
song_editor::Message::ChangeSong(song),
|
||||
))
|
||||
}
|
||||
ServiceItemKind::Video(video) => todo!(),
|
||||
ServiceItemKind::Image(image) => todo!(),
|
||||
ServiceItemKind::Presentation(presentation) => {
|
||||
ServiceItemKind::Video(_video) => todo!(),
|
||||
ServiceItemKind::Image(_image) => todo!(),
|
||||
ServiceItemKind::Presentation(_presentation) => {
|
||||
todo!()
|
||||
}
|
||||
ServiceItemKind::Content(slide) => todo!(),
|
||||
ServiceItemKind::Content(_slide) => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1198,7 +1162,7 @@ where
|
|||
})
|
||||
}
|
||||
|
||||
fn add_library(&mut self) -> Task<Message> {
|
||||
fn add_library(&self) -> Task<Message> {
|
||||
Task::perform(async move { Library::new().await }, |x| {
|
||||
cosmic::Action::App(Message::AddLibrary(x))
|
||||
})
|
||||
|
@ -1220,7 +1184,7 @@ where
|
|||
}
|
||||
|
||||
fn add_service(
|
||||
&mut self,
|
||||
&self,
|
||||
items: Vec<ServiceItem>,
|
||||
fontdb: Arc<fontdb::Database>,
|
||||
) -> Task<Message> {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use cosmic::{
|
||||
iced::{
|
||||
alignment::Vertical, clipboard::dnd::DndAction,
|
||||
|
@ -7,9 +9,10 @@ use cosmic::{
|
|||
iced_widget::{column, row as rowm, text as textm},
|
||||
theme,
|
||||
widget::{
|
||||
button, container, horizontal_space, icon, mouse_area,
|
||||
responsive, row, scrollable, text, text_input, Container,
|
||||
DndSource, Space, Widget,
|
||||
button, container, context_menu, horizontal_space, icon,
|
||||
menu::{self, Action as MenuAction},
|
||||
mouse_area, responsive, row, scrollable, text, text_input,
|
||||
Container, DndSource, Space,
|
||||
},
|
||||
Element, Task,
|
||||
};
|
||||
|
@ -41,6 +44,29 @@ pub(crate) struct Library {
|
|||
pub dragged_item: Option<(LibraryKind, i32)>,
|
||||
editing_item: Option<(LibraryKind, i32)>,
|
||||
db: SqlitePool,
|
||||
menu_keys: std::collections::HashMap<menu::KeyBind, MenuMessage>,
|
||||
context_menu: Option<i32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Copy)]
|
||||
enum MenuMessage {
|
||||
Delete((LibraryKind, i32)),
|
||||
Open,
|
||||
None,
|
||||
}
|
||||
|
||||
impl MenuAction for MenuMessage {
|
||||
type Message = Message;
|
||||
|
||||
fn message(&self) -> Self::Message {
|
||||
match self {
|
||||
MenuMessage::Delete((kind, index)) => {
|
||||
Message::DeleteItem((*kind, *index))
|
||||
}
|
||||
MenuMessage::Open => todo!(),
|
||||
MenuMessage::None => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum Action {
|
||||
|
@ -53,7 +79,7 @@ pub(crate) enum Action {
|
|||
#[derive(Clone, Debug)]
|
||||
pub(crate) enum Message {
|
||||
AddItem,
|
||||
RemoveItem,
|
||||
DeleteItem((LibraryKind, i32)),
|
||||
OpenItem(Option<(LibraryKind, i32)>),
|
||||
HoverLibrary(Option<LibraryKind>),
|
||||
OpenLibrary(Option<LibraryKind>),
|
||||
|
@ -69,6 +95,7 @@ pub(crate) enum Message {
|
|||
UpdatePresentation(Presentation),
|
||||
PresentationChanged,
|
||||
Error(String),
|
||||
OpenContext(i32),
|
||||
None,
|
||||
}
|
||||
|
||||
|
@ -90,6 +117,8 @@ impl<'a> Library {
|
|||
dragged_item: None,
|
||||
editing_item: None,
|
||||
db,
|
||||
menu_keys: HashMap::new(),
|
||||
context_menu: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,7 +130,16 @@ impl<'a> Library {
|
|||
match message {
|
||||
Message::AddItem => (),
|
||||
Message::None => (),
|
||||
Message::RemoveItem => (),
|
||||
Message::DeleteItem((kind, index)) => {
|
||||
match kind {
|
||||
LibraryKind::Song => todo!(),
|
||||
LibraryKind::Video => todo!(),
|
||||
LibraryKind::Image => todo!(),
|
||||
LibraryKind::Presentation => {
|
||||
self.presentation_library.remove_item(index);
|
||||
}
|
||||
};
|
||||
}
|
||||
Message::OpenItem(item) => {
|
||||
debug!(?item);
|
||||
self.editing_item = item;
|
||||
|
@ -133,18 +171,18 @@ impl<'a> Library {
|
|||
if kind != LibraryKind::Song {
|
||||
error!("Not editing a song item");
|
||||
return Action::None;
|
||||
};
|
||||
}
|
||||
|
||||
match self
|
||||
.song_library
|
||||
.update_item(song.clone(), index)
|
||||
{
|
||||
Ok(_) => {
|
||||
Ok(()) => {
|
||||
return Action::Task(
|
||||
Task::future(self.db.acquire()).and_then(
|
||||
move |conn| update_in_db(&song, conn),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
Err(_) => todo!(),
|
||||
}
|
||||
|
@ -162,13 +200,13 @@ impl<'a> Library {
|
|||
if kind != LibraryKind::Image {
|
||||
error!("Not editing a image item");
|
||||
return Action::None;
|
||||
};
|
||||
}
|
||||
|
||||
match self
|
||||
.image_library
|
||||
.update_item(image.clone(), index)
|
||||
{
|
||||
Ok(_) => {
|
||||
Ok(()) => {
|
||||
return Action::Task(
|
||||
Task::future(self.db.acquire()).and_then(
|
||||
move |conn| {
|
||||
|
@ -181,7 +219,7 @@ impl<'a> Library {
|
|||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
Err(_) => todo!(),
|
||||
}
|
||||
|
@ -196,13 +234,13 @@ impl<'a> Library {
|
|||
if kind != LibraryKind::Video {
|
||||
error!("Not editing a video item");
|
||||
return Action::None;
|
||||
};
|
||||
}
|
||||
|
||||
match self
|
||||
.video_library
|
||||
.update_item(video.clone(), index)
|
||||
{
|
||||
Ok(_) => {
|
||||
Ok(()) => {
|
||||
return Action::Task(
|
||||
Task::future(self.db.acquire()).and_then(
|
||||
move |conn| {
|
||||
|
@ -215,7 +253,7 @@ impl<'a> Library {
|
|||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
Err(_) => todo!(),
|
||||
}
|
||||
|
@ -230,13 +268,13 @@ impl<'a> Library {
|
|||
if kind != LibraryKind::Presentation {
|
||||
error!("Not editing a presentation item");
|
||||
return Action::None;
|
||||
};
|
||||
}
|
||||
|
||||
match self
|
||||
.presentation_library
|
||||
.update_item(presentation.clone(), index)
|
||||
{
|
||||
Ok(_) => return Action::Task(
|
||||
Ok(()) => return Action::Task(
|
||||
Task::future(self.db.acquire()).and_then(
|
||||
move |conn| {
|
||||
Task::perform(
|
||||
|
@ -254,7 +292,10 @@ impl<'a> Library {
|
|||
}
|
||||
Message::PresentationChanged => (),
|
||||
Message::Error(_) => (),
|
||||
};
|
||||
Message::OpenContext(index) => {
|
||||
self.context_menu = Some(index);
|
||||
}
|
||||
}
|
||||
Action::None
|
||||
}
|
||||
|
||||
|
@ -315,7 +356,7 @@ impl<'a> Library {
|
|||
textm!("Presentations").align_y(Vertical::Center),
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
let item_count = model.items.len();
|
||||
row = row.push(horizontal_space());
|
||||
row = row
|
||||
|
@ -373,9 +414,9 @@ impl<'a> Library {
|
|||
let service_item = item.to_service_item();
|
||||
let visual_item = self
|
||||
.single_item(index, item, model)
|
||||
.map(|_| Message::None);
|
||||
DndSource::<Message, ServiceItem>::new(
|
||||
mouse_area(visual_item)
|
||||
.map(|()| Message::None);
|
||||
DndSource::<Message, ServiceItem>::new({
|
||||
let mouse_area = Element::from(mouse_area(visual_item)
|
||||
.on_drag(Message::DragItem(service_item.clone()))
|
||||
.on_enter(Message::HoverItem(
|
||||
Some((
|
||||
|
@ -389,13 +430,31 @@ impl<'a> Library {
|
|||
index as i32,
|
||||
))),
|
||||
)
|
||||
.on_right_press(Message::OpenContext(index as i32))
|
||||
.on_exit(Message::HoverItem(None))
|
||||
.on_press(Message::SelectItem(
|
||||
Some((
|
||||
model.kind,
|
||||
index as i32,
|
||||
)),
|
||||
)),
|
||||
)));
|
||||
if let Some(context_id) = self.context_menu {
|
||||
if index == context_id as usize {
|
||||
let context_menu = context_menu(
|
||||
mouse_area,
|
||||
self.context_menu.map_or_else(|| None, |id| {
|
||||
Some(menu::items(&self.menu_keys,
|
||||
vec![menu::Item::Button("Delete", None, MenuMessage::Delete((model.kind, index as i32)))]))
|
||||
})
|
||||
);
|
||||
Element::from(context_menu)
|
||||
} else {
|
||||
Element::from(mouse_area)
|
||||
}
|
||||
} else {
|
||||
Element::from(mouse_area)
|
||||
}
|
||||
}
|
||||
)
|
||||
.action(DndAction::Copy)
|
||||
.drag_icon({
|
||||
|
@ -418,7 +477,7 @@ impl<'a> Library {
|
|||
)
|
||||
}})
|
||||
.drag_content(move || {
|
||||
service_item.to_owned()
|
||||
service_item.clone()
|
||||
})
|
||||
.into()
|
||||
},
|
||||
|
@ -481,11 +540,10 @@ impl<'a> Library {
|
|||
.accent_text_color()
|
||||
.into()
|
||||
}
|
||||
} else if let Some((library, selected)) = self.selected_item
|
||||
{
|
||||
if model.kind == library
|
||||
&& selected == index as i32
|
||||
} else if let Some((library, selected)) =
|
||||
self.selected_item
|
||||
{
|
||||
if model.kind == library && selected == index as i32 {
|
||||
theme::active().cosmic().control_0().into()
|
||||
} else {
|
||||
theme::active()
|
||||
|
@ -563,6 +621,7 @@ impl<'a> Library {
|
|||
.into()
|
||||
}
|
||||
|
||||
#[allow(clippy::unused_async)]
|
||||
pub async fn search_items(
|
||||
&self,
|
||||
query: String,
|
||||
|
@ -573,14 +632,18 @@ impl<'a> Library {
|
|||
.items
|
||||
.iter()
|
||||
.filter(|song| song.title.to_lowercase().contains(&query))
|
||||
.map(|song| song.to_service_item())
|
||||
.map(
|
||||
super::super::core::content::Content::to_service_item,
|
||||
)
|
||||
.collect();
|
||||
let videos: Vec<ServiceItem> = self
|
||||
.video_library
|
||||
.items
|
||||
.iter()
|
||||
.filter(|vid| vid.title.to_lowercase().contains(&query))
|
||||
.map(|vid| vid.to_service_item())
|
||||
.map(
|
||||
super::super::core::content::Content::to_service_item,
|
||||
)
|
||||
.collect();
|
||||
let images: Vec<ServiceItem> = self
|
||||
.image_library
|
||||
|
@ -589,14 +652,18 @@ impl<'a> Library {
|
|||
.filter(|image| {
|
||||
image.title.to_lowercase().contains(&query)
|
||||
})
|
||||
.map(|image| image.to_service_item())
|
||||
.map(
|
||||
super::super::core::content::Content::to_service_item,
|
||||
)
|
||||
.collect();
|
||||
let presentations: Vec<ServiceItem> = self
|
||||
.presentation_library
|
||||
.items
|
||||
.iter()
|
||||
.filter(|pres| pres.title.to_lowercase().contains(&query))
|
||||
.map(|pres| pres.to_service_item())
|
||||
.map(
|
||||
super::super::core::content::Content::to_service_item,
|
||||
)
|
||||
.collect();
|
||||
items.extend(videos);
|
||||
items.extend(images);
|
||||
|
@ -656,7 +723,7 @@ fn update_in_db(
|
|||
Task::perform(
|
||||
update_song_in_db(song.to_owned(), conn).map(
|
||||
move |r| match r {
|
||||
Ok(_) => {
|
||||
Ok(()) => {
|
||||
warn!(
|
||||
"Should have updated song: {:?}",
|
||||
song_title
|
||||
|
@ -667,7 +734,7 @@ fn update_in_db(
|
|||
}
|
||||
},
|
||||
),
|
||||
|_| Message::SongChanged,
|
||||
|()| Message::SongChanged,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -10,11 +10,13 @@ use cosmic::{
|
|||
iced_widget::{
|
||||
scrollable::{
|
||||
scroll_to, AbsoluteOffset, Direction, Scrollbar,
|
||||
}, stack, vertical_rule,
|
||||
},
|
||||
stack, vertical_rule,
|
||||
},
|
||||
prelude::*,
|
||||
widget::{
|
||||
container, image, mouse_area, responsive, scrollable, text, Container, Id, Row, Space,
|
||||
container, image, mouse_area, responsive, scrollable, text,
|
||||
Container, Id, Row, Space,
|
||||
},
|
||||
Task,
|
||||
};
|
||||
|
@ -152,7 +154,7 @@ impl Presenter {
|
|||
absolute_slide_index: 0,
|
||||
total_slides,
|
||||
video,
|
||||
audio: items[0].slides[0].audio().clone(),
|
||||
audio: items[0].slides[0].audio(),
|
||||
service: items,
|
||||
video_position: 0.0,
|
||||
hovered_slide: None,
|
||||
|
@ -219,8 +221,8 @@ impl Presenter {
|
|||
let offset = AbsoluteOffset {
|
||||
x: {
|
||||
if self.current_slide_index > 2 {
|
||||
self.current_slide_index as f32 * 187.5
|
||||
- 187.5
|
||||
(self.current_slide_index as f32)
|
||||
.mul_add(187.5, -187.5)
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
|
@ -254,7 +256,7 @@ impl Presenter {
|
|||
?current_audio,
|
||||
"audio needs to change"
|
||||
);
|
||||
self.audio = Some(new_audio.clone());
|
||||
self.audio = Some(new_audio);
|
||||
tasks.push(self.start_audio());
|
||||
}
|
||||
Some(current_audio) => {
|
||||
|
@ -269,10 +271,10 @@ impl Presenter {
|
|||
?new_audio,
|
||||
"could not find audio, need to change"
|
||||
);
|
||||
self.audio = Some(new_audio.clone());
|
||||
self.audio = Some(new_audio);
|
||||
tasks.push(self.start_audio());
|
||||
}
|
||||
};
|
||||
}
|
||||
} else {
|
||||
self.audio = None;
|
||||
self.update(Message::EndAudio);
|
||||
|
@ -324,7 +326,7 @@ impl Presenter {
|
|||
std::time::Duration::from_secs_f32(position),
|
||||
);
|
||||
match video.seek(position, false) {
|
||||
Ok(_) => debug!(
|
||||
Ok(()) => debug!(
|
||||
"Video position changed: {:?}",
|
||||
position
|
||||
),
|
||||
|
@ -353,7 +355,7 @@ impl Presenter {
|
|||
install_ctx
|
||||
.set_desktop_id(&format!("{}.desktop", "org.chriscochrun.lumina"));
|
||||
let install_detail = missing_plugin.installer_detail();
|
||||
println!("installing plugins: {}", install_detail);
|
||||
println!("installing plugins: {install_detail}");
|
||||
let status = gst_pbutils::missing_plugins::install_plugins_sync(
|
||||
&[&install_detail],
|
||||
Some(&install_ctx),
|
||||
|
@ -389,7 +391,7 @@ impl Presenter {
|
|||
Message::Error(error) => {
|
||||
error!(error);
|
||||
}
|
||||
};
|
||||
}
|
||||
Action::None
|
||||
}
|
||||
|
||||
|
@ -639,7 +641,7 @@ impl Presenter {
|
|||
v.set_looping(
|
||||
self.current_slide.video_loop(),
|
||||
);
|
||||
self.video = Some(v)
|
||||
self.video = Some(v);
|
||||
}
|
||||
Err(e) => {
|
||||
error!(
|
||||
|
@ -662,7 +664,7 @@ impl Presenter {
|
|||
let audio = audio.clone();
|
||||
Task::perform(
|
||||
start_audio(Arc::clone(&self.sink.1), audio),
|
||||
|_| Message::None,
|
||||
|()| Message::None,
|
||||
)
|
||||
} else {
|
||||
debug!(?self.audio, "Apparently this doesn't exist");
|
||||
|
|
|
@ -54,10 +54,10 @@ struct EditorProgram {
|
|||
}
|
||||
|
||||
impl SlideEditor {
|
||||
pub fn view<'a>(
|
||||
&'a self,
|
||||
pub fn view(
|
||||
&self,
|
||||
font: Font,
|
||||
) -> cosmic::Element<'a, SlideWidget> {
|
||||
) -> cosmic::Element<'_, SlideWidget> {
|
||||
container(
|
||||
widget::canvas(&self.program)
|
||||
.height(Length::Fill)
|
||||
|
@ -122,10 +122,10 @@ impl<'a> Program<SlideWidget, cosmic::Theme, cosmic::Renderer>
|
|||
match event {
|
||||
canvas::Event::Mouse(event) => match event {
|
||||
cosmic::iced::mouse::Event::CursorEntered => {
|
||||
debug!("cursor entered")
|
||||
debug!("cursor entered");
|
||||
}
|
||||
cosmic::iced::mouse::Event::CursorLeft => {
|
||||
debug!("cursor left")
|
||||
debug!("cursor left");
|
||||
}
|
||||
cosmic::iced::mouse::Event::CursorMoved {
|
||||
position,
|
||||
|
@ -140,7 +140,7 @@ impl<'a> Program<SlideWidget, cosmic::Theme, cosmic::Renderer>
|
|||
}
|
||||
cosmic::iced::mouse::Event::ButtonPressed(button) => {
|
||||
// self.mouse_button_pressed = Some(button);
|
||||
debug!(?button, "mouse button pressed")
|
||||
debug!(?button, "mouse button pressed");
|
||||
}
|
||||
cosmic::iced::mouse::Event::ButtonReleased(
|
||||
button,
|
||||
|
|
|
@ -1,30 +1,29 @@
|
|||
use std::{io, path::PathBuf, sync::Arc};
|
||||
|
||||
use cosmic::{
|
||||
Element, Task,
|
||||
dialog::file_chooser::open::Dialog,
|
||||
iced::{
|
||||
font::{Family, Stretch, Style, Weight},
|
||||
Font, Length,
|
||||
font::{Family, Stretch, Style, Weight},
|
||||
},
|
||||
iced_wgpu::graphics::text::cosmic_text::fontdb,
|
||||
iced_widget::row,
|
||||
theme,
|
||||
widget::{
|
||||
button, column, combo_box, container, horizontal_space, icon, text, text_editor, text_input,
|
||||
button, column, combo_box, container, horizontal_space, icon,
|
||||
text, text_editor, text_input,
|
||||
},
|
||||
Element, Task,
|
||||
};
|
||||
use dirs::font_dir;
|
||||
use iced_video_player::Video;
|
||||
use tracing::{debug, error};
|
||||
|
||||
use crate::{
|
||||
core::{service_items::ServiceTrait, songs::Song},
|
||||
Background, BackgroundKind, core::songs::Song,
|
||||
ui::slide_editor::SlideEditor,
|
||||
Background, BackgroundKind,
|
||||
};
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SongEditor {
|
||||
pub song: Option<Song>,
|
||||
|
@ -140,11 +139,11 @@ impl SongEditor {
|
|||
self.song = Some(song.clone());
|
||||
self.title = song.title;
|
||||
if let Some(font) = song.font {
|
||||
self.font = font
|
||||
};
|
||||
self.font = font;
|
||||
}
|
||||
if let Some(font_size) = song.font_size {
|
||||
self.font_size = font_size as usize
|
||||
};
|
||||
self.font_size = font_size as usize;
|
||||
}
|
||||
if let Some(verse_order) = song.verse_order {
|
||||
self.verse_order = verse_order
|
||||
.into_iter()
|
||||
|
@ -155,18 +154,18 @@ impl SongEditor {
|
|||
.collect();
|
||||
}
|
||||
if let Some(author) = song.author {
|
||||
self.author = author
|
||||
};
|
||||
self.author = author;
|
||||
}
|
||||
if let Some(audio) = song.audio {
|
||||
self.audio = audio
|
||||
};
|
||||
self.audio = audio;
|
||||
}
|
||||
if let Some(ccli) = song.ccli {
|
||||
self.ccli = ccli
|
||||
};
|
||||
self.ccli = ccli;
|
||||
}
|
||||
if let Some(lyrics) = song.lyrics {
|
||||
self.lyrics =
|
||||
text_editor::Content::with_text(&lyrics)
|
||||
};
|
||||
text_editor::Content::with_text(&lyrics);
|
||||
}
|
||||
self.background_video(&song.background);
|
||||
self.background = song.background;
|
||||
}
|
||||
|
@ -212,8 +211,8 @@ impl SongEditor {
|
|||
self.verse_order = verse_order.clone();
|
||||
if let Some(mut song) = self.song.clone() {
|
||||
let verse_order = verse_order
|
||||
.split(" ")
|
||||
.map(|s| s.to_owned())
|
||||
.split(' ')
|
||||
.map(std::borrow::ToOwned::to_owned)
|
||||
.collect();
|
||||
song.verse_order = Some(verse_order);
|
||||
return self.update_song(song);
|
||||
|
@ -244,8 +243,7 @@ impl SongEditor {
|
|||
Message::ChangeBackground(Ok(path)) => {
|
||||
debug!(?path);
|
||||
if let Some(mut song) = self.song.clone() {
|
||||
let background =
|
||||
Background::try_from(path.clone()).ok();
|
||||
let background = Background::try_from(path).ok();
|
||||
self.background_video(&background);
|
||||
song.background = background;
|
||||
return self.update_song(song);
|
||||
|
@ -258,7 +256,7 @@ impl SongEditor {
|
|||
return Action::Task(Task::perform(
|
||||
pick_background(),
|
||||
Message::ChangeBackground,
|
||||
))
|
||||
));
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
@ -416,7 +414,7 @@ order",
|
|||
.into()
|
||||
}
|
||||
|
||||
pub fn editing(&self) -> bool {
|
||||
pub const fn editing(&self) -> bool {
|
||||
self.editing
|
||||
}
|
||||
|
||||
|
|
|
@ -121,15 +121,18 @@ impl From<&str> for Font {
|
|||
}
|
||||
|
||||
impl Font {
|
||||
#[must_use]
|
||||
pub fn get_name(&self) -> String {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
pub fn get_weight(&self) -> Weight {
|
||||
#[must_use]
|
||||
pub const fn get_weight(&self) -> Weight {
|
||||
self.weight
|
||||
}
|
||||
|
||||
pub fn get_style(&self) -> Style {
|
||||
#[must_use]
|
||||
pub const fn get_style(&self) -> Style {
|
||||
self.style
|
||||
}
|
||||
|
||||
|
@ -148,7 +151,8 @@ impl Font {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn size(mut self, size: u8) -> Self {
|
||||
#[must_use]
|
||||
pub const fn size(mut self, size: u8) -> Self {
|
||||
self.size = size;
|
||||
self
|
||||
}
|
||||
|
@ -161,12 +165,12 @@ impl Hash for Color {
|
|||
}
|
||||
|
||||
impl Color {
|
||||
pub fn from_hex_str(color: impl AsRef<str>) -> Color {
|
||||
pub fn from_hex_str(color: impl AsRef<str>) -> Self {
|
||||
match Rgb::from_hex_str(color.as_ref()) {
|
||||
Ok(rgb) => Color(rgb),
|
||||
Ok(rgb) => Self(rgb),
|
||||
Err(e) => {
|
||||
error!("error in making color from hex_str: {:?}", e);
|
||||
Color::default()
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -236,7 +240,10 @@ impl TextSvg {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn alignment(mut self, alignment: TextAlignment) -> Self {
|
||||
pub const fn alignment(
|
||||
mut self,
|
||||
alignment: TextAlignment,
|
||||
) -> Self {
|
||||
self.alignment = alignment;
|
||||
self
|
||||
}
|
||||
|
@ -255,7 +262,7 @@ impl TextSvg {
|
|||
shadow.spread,
|
||||
shadow.color)
|
||||
} else {
|
||||
"".into()
|
||||
String::new()
|
||||
};
|
||||
let stroke = if let Some(stroke) = &self.stroke {
|
||||
format!(
|
||||
|
@ -263,17 +270,17 @@ impl TextSvg {
|
|||
stroke.color, stroke.size
|
||||
)
|
||||
} else {
|
||||
"".into()
|
||||
String::new()
|
||||
};
|
||||
let size = Size::new(1920.0, 1080.0);
|
||||
let font_size = self.font.size as f32;
|
||||
let font_size = f32::from(self.font.size);
|
||||
let total_lines = self.text.lines().count();
|
||||
let half_lines = (total_lines / 2) as f32;
|
||||
let middle_position = size.height / 2.0;
|
||||
let line_spacing = 10.0;
|
||||
let text_and_line_spacing = font_size + line_spacing;
|
||||
let starting_y_position =
|
||||
middle_position - (half_lines * text_and_line_spacing);
|
||||
let starting_y_position = half_lines
|
||||
.mul_add(-text_and_line_spacing, middle_position);
|
||||
|
||||
let text_pieces: Vec<String> = self
|
||||
.text
|
||||
|
@ -282,8 +289,10 @@ impl TextSvg {
|
|||
.map(|(index, text)| {
|
||||
format!(
|
||||
"<tspan x=\"50%\" y=\"{}\">{}</tspan>",
|
||||
(index as f32).mul_add(
|
||||
text_and_line_spacing,
|
||||
starting_y_position
|
||||
+ (index as f32 * text_and_line_spacing),
|
||||
),
|
||||
text
|
||||
)
|
||||
})
|
||||
|
@ -350,7 +359,7 @@ impl TextSvg {
|
|||
self.text
|
||||
.lines()
|
||||
.enumerate()
|
||||
.map(|(i, t)| format!("<tspan x=\"50%\">{}</tspan>", t))
|
||||
.map(|(i, t)| format!("<tspan x=\"50%\">{t}</tspan>"))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
@ -391,7 +400,7 @@ pub fn text_svg_generator(
|
|||
.shadow(shadow(2, 2, 5, "#000000"))
|
||||
.stroke(stroke(3, "#000"))
|
||||
.font(
|
||||
Font::from(slide.font().clone())
|
||||
Font::from(slide.font())
|
||||
.size(slide.font_size().try_into().unwrap()),
|
||||
)
|
||||
.fontdb(Arc::clone(&fontdb))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue