Compare commits

...

10 commits

Author SHA1 Message Date
Chris Cochrun
b56425c671 testy 2025-07-25 15:19:31 -05:00
Chris Cochrun
504b4bc944 fixy 2025-07-25 15:19:21 -05:00
Chris Cochrun
92f2b18a20 rust side builds but having trouble linking in main.cpp 2025-03-12 09:35:36 -05:00
Chris Cochrun
fbcb6afc7a fixing formatting 2025-03-11 17:25:56 -05:00
Chris Cochrun
26975c2004 updating to newer shell model and updating rust 2025-03-11 17:25:23 -05:00
Chris Cochrun
8d9e00cb7a adding idk 2025-03-11 17:25:14 -05:00
Chris Cochrun
0ba3e7588b fixing lots of bugs 2025-03-10 23:18:30 -05:00
Chris Cochrun
da735aa00b Error handling..... grrr 2024-10-14 10:58:21 -05:00
Chris Cochrun
6fe0bf8fe2 updating 2024-10-13 15:42:35 -05:00
Chris Cochrun
b1d938acba make service_model public 2024-10-13 15:42:23 -05:00
45 changed files with 1711 additions and 819 deletions

View file

@ -30,7 +30,10 @@ include(ECMPoQmTools)
kde_enable_exceptions()
find_package(Qt6 ${QT_MIN_VERSION} REQUIRED NO_MODULE COMPONENTS Core Core5Compat Quick Test Gui Qml QuickControls2 Widgets Sql QmlImportScanner WebEngineQuick)
set(CXXQT_QTCOMPONENTS Core Gui Qml QuickControls2 QuickTest Test)
set(CXXQT_QTCOMPONENTS ${CXXQT_QTCOMPONENTS} QmlImportScanner)
find_package(Qt6 ${QT_MIN_VERSION} REQUIRED NO_MODULE COMPONENTS Core Core5Compat Quick Test Gui Qml QuickControls2 Widgets Sql QmlImportScanner WebEngineQuick Multimedia)
find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS Kirigami CoreAddons I18n)
# find_package(KF6FileMetaData ${KF6_MIN_VERSION})
@ -57,45 +60,70 @@ set_package_properties(Ytdlp PROPERTIES TYPE RUNTIME)
# execute_process(COMMAND ${XDG-DESKTOP-MENU_EXECUTABLE} install --novender librepresenter.desktop)
get_target_property(QMAKE Qt::qmake IMPORTED_LOCATION)
find_package(Corrosion QUIET)
if(NOT Corrosion_FOUND)
find_package(CxxQt QUIET)
if(NOT CxxQt_FOUND)
include(FetchContent)
FetchContent_Declare(
Corrosion
GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git
GIT_TAG v0.3.0
CxxQt
GIT_REPOSITORY https://github.com/kdab/cxx-qt-cmake.git
GIT_TAG v0.7.0
)
FetchContent_MakeAvailable(Corrosion)
FetchContent_MakeAvailable(CxxQt)
endif()
add_subdirectory(src)
# find_package(Corrosion QUIET)
# if(NOT Corrosion_FOUND)
# include(FetchContent)
# FetchContent_Declare(
# Corrosion
# GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git
# GIT_TAG v0.3.0
# )
# FetchContent_MakeAvailable(Corrosion)
# endif()
set(CRATE liblumina)
# CXX-Qt (using Corrosion) creates a CMake target with the same name as the crate.
cxx_qt_import_crate(
MANIFEST_PATH Cargo.toml
CRATES liblumina
QT_MODULES Qt::Core Qt::Gui Qt::Qml Qt::QuickControls2 Qt::WebEngineQuick)
cxx_qt_import_qml_module(liblumina_qml_module
URI "org.presenter"
SOURCE_CRATE liblumina)
# Corrosion creates a CMake target with the same name as the crate.
corrosion_import_crate(MANIFEST_PATH Cargo.toml CRATES ${CRATE})
# corrosion_import_crate(MANIFEST_PATH Cargo.toml CRATES ${CRATE})
# The Rust library's build script needs to be told where to output the
# generated headers so CMake can find them. To do this, tell Corrosion
# to set the CXXQT_EXPORT_DIR environment variable when calling `cargo build`.
# Also, set the QMAKE environment variable to ensure the Rust library uses
# the same installation of Qt as CMake.
set(CXXQT_EXPORT_DIR "${CMAKE_CURRENT_BINARY_DIR}/cxxqt")
corrosion_set_env_vars(${CRATE}
"CXXQT_EXPORT_DIR=${CXXQT_EXPORT_DIR}"
"QMAKE=${QMAKE}"
)
# set(CXXQT_EXPORT_DIR "${CMAKE_CURRENT_BINARY_DIR}/cxxqt")
# corrosion_set_env_vars(${CRATE}
# "CXXQT_EXPORT_DIR=${CXXQT_EXPORT_DIR}"
# "QMAKE=${QMAKE}"
# )
add_library(${APP_NAME}_lib INTERFACE)
add_subdirectory(src)
# Include the headers generated by the Rust library's build script. Each
# crate gets its own subdirectory under CXXQT_EXPORT_DIR. This allows you
# to include headers generated by multiple crates without risk of one crate
# overwriting another's files.
target_include_directories(${APP_NAME}_lib INTERFACE "${CXXQT_EXPORT_DIR}/${CRATE}")
target_include_directories(liblumina INTERFACE "${CXXQT_EXPORT_DIR}/${CRATE}")
# Link the Rust INTERFACE library target to Qt. Do this on the library target
# rather than the main executable. This way, CMake targets besides the main
# executable which link the Rust library, for example tests, will also link Qt.
target_link_libraries(${APP_NAME}_lib INTERFACE
target_link_libraries(liblumina INTERFACE
"$<LINK_LIBRARY:WHOLE_ARCHIVE,${CRATE}-static>"
Qt6::Quick
Qt6::Qml
@ -104,6 +132,7 @@ target_link_libraries(${APP_NAME}_lib INTERFACE
Qt6::Widgets
Qt6::Sql
Qt6::WebEngineQuick
Qt6::Multimedia
KF6::Kirigami
KF6::I18n
KF6::CoreAddons
@ -114,8 +143,9 @@ target_link_libraries(${APP_NAME}_lib INTERFACE
crypto
)
# Link to the Rust library
target_link_libraries(${APP_NAME} PRIVATE ${APP_NAME}_lib)
target_link_libraries(${APP_NAME} PRIVATE liblumina)
# If we are using a statically linked Qt then we need to import any qml plugins
qt_import_qml_plugins(${APP_NAME})

159
Cargo.lock generated
View file

@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
version = 4
[[package]]
name = "addr2line"
@ -338,6 +338,19 @@ dependencies = [
"serde",
]
[[package]]
name = "blah"
version = "0.1.0"
dependencies = [
"cxx",
"cxx-qt",
"cxx-qt-build",
"cxx-qt-lib",
"cxx-qt-lib-extras",
"markdown",
"qt-build-utils",
]
[[package]]
name = "block"
version = "0.1.6"
@ -490,32 +503,6 @@ dependencies = [
"unicode-segmentation",
]
[[package]]
name = "core"
version = "0.1.0"
dependencies = [
"color-eyre",
"configparser",
"dirs",
"fastrand 2.1.1",
"obws",
"pretty_assertions",
"quote",
"reqwest",
"rfd",
"serde",
"serde_derive",
"serde_json",
"sqlx",
"tar",
"time",
"tokio",
"tracing",
"tracing-subscriber",
"youtube_dl",
"zstd",
]
[[package]]
name = "core-foundation"
version = "0.9.4"
@ -593,21 +580,6 @@ dependencies = [
"link-cplusplus",
]
[[package]]
name = "cxx-build"
version = "1.0.128"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c77953e99f01508f89f55c494bfa867171ef3a6c8cea03d26975368f2121a5c1"
dependencies = [
"cc",
"codespan-reporting",
"once_cell",
"proc-macro2",
"quote",
"scratch",
"syn 2.0.77",
]
[[package]]
name = "cxx-gen"
version = "0.7.128"
@ -622,38 +594,41 @@ dependencies = [
[[package]]
name = "cxx-qt"
version = "0.6.1"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08aa6cda7588b6d17c563b0d2fadc060d4204d04908c0f359ae288857091218d"
checksum = "208ad6c4feac92f221fde00796f317b049ba1892b97be0d60ca177d0d3469fc5"
dependencies = [
"cxx",
"cxx-qt-build",
"cxx-qt-macro",
"qt-build-utils",
"static_assertions",
"thiserror",
]
[[package]]
name = "cxx-qt-build"
version = "0.6.1"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e097b99f49792922a72a8ca35d9391762e48e63363d6998255be1f2ca1edf69"
checksum = "15f80e109aa68795486c70c302f6c2d921f00028b3b62038a4601efb5c585c1c"
dependencies = [
"cc",
"codespan-reporting",
"convert_case",
"cxx-gen",
"cxx-qt-gen",
"cxx-qt-lib-headers",
"proc-macro2",
"qt-build-utils",
"quote",
"serde",
"serde_json",
"version_check",
]
[[package]]
name = "cxx-qt-gen"
version = "0.6.1"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ede7c73dbfbcc234d8826919e257830c1789db2cac586546a87d2a82e3cbe5d5"
checksum = "dc17d95ca9cc60c2f91f804a4e0ba6a3e1b8ed338c207a1bd8d176133e2fd05d"
dependencies = [
"clang-format",
"convert_case",
@ -665,27 +640,33 @@ dependencies = [
[[package]]
name = "cxx-qt-lib"
version = "0.6.1"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "002f1a6119bcb7dfec67eb7c0803a7b1d595dc54610559faeac35133f22a5880"
checksum = "f116c5d982bbf3be707acf97f566802c30454d52ca319c745ed39a04834e8bc6"
dependencies = [
"cxx",
"cxx-build",
"cxx-qt-lib-headers",
"cxx-qt",
"cxx-qt-build",
"qt-build-utils",
]
[[package]]
name = "cxx-qt-lib-headers"
version = "0.6.1"
name = "cxx-qt-lib-extras"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9abdeab6b77cfc5a53b724f3f62a37bcb5ac1423cccc2dba4c134f4273440b8c"
checksum = "bbe1aaed6391a224d746e314104f33b4031138291ebd368170a2109b6008ace2"
dependencies = [
"cxx",
"cxx-qt",
"cxx-qt-build",
"cxx-qt-lib",
]
[[package]]
name = "cxx-qt-macro"
version = "0.6.1"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "699e8a668c03b03419b084960d72eed253632bb16349b33fd0a0c893b61b664c"
checksum = "58a4fe02c0604eda28c605792f5ba0d0251b4947f8f0fc43e55b61c06b2b8ec6"
dependencies = [
"cxx-qt-gen",
"proc-macro2",
@ -1432,9 +1413,9 @@ checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4"
[[package]]
name = "itertools"
version = "0.11.0"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [
"either",
]
@ -1490,6 +1471,7 @@ dependencies = [
"cxx-qt-lib",
"dirs",
"fastrand 2.1.1",
"lumina_core",
"obws",
"qt-build-utils",
"quote",
@ -1573,6 +1555,32 @@ version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "lumina_core"
version = "0.1.0"
dependencies = [
"color-eyre",
"configparser",
"dirs",
"fastrand 2.1.1",
"obws",
"pretty_assertions",
"quote",
"reqwest",
"rfd",
"serde",
"serde_derive",
"serde_json",
"sqlx",
"tar",
"time",
"tokio",
"tracing",
"tracing-subscriber",
"youtube_dl",
"zstd",
]
[[package]]
name = "malloc_buf"
version = "0.0.6"
@ -1582,6 +1590,15 @@ dependencies = [
"libc",
]
[[package]]
name = "markdown"
version = "1.0.0-alpha.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21e27d6220ce21f80ce5c4201f23a37c6f1ad037c72c9d1ff215c2919605a5d6"
dependencies = [
"unicode-id",
]
[[package]]
name = "matchers"
version = "0.1.0"
@ -2092,9 +2109,9 @@ dependencies = [
[[package]]
name = "qt-build-utils"
version = "0.6.1"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d59c828fe2434dad34dd0c30a4ba037509b61dad92a55baf0dc42699e6aa2f10"
checksum = "efb239fdd8c036fabb95364320041ef68197cd4ab971bb3b4ca3ea0b7b93d12c"
dependencies = [
"cc",
"thiserror",
@ -2364,12 +2381,6 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "scratch"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3cf7c11c38cb994f3d40e8a8cde3bbd1f72a435e4c49e85d6553d8312306152"
[[package]]
name = "security-framework"
version = "2.11.1"
@ -3212,6 +3223,12 @@ version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
[[package]]
name = "unicode-id"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10103c57044730945224467c09f71a4db0071c123a0648cc3e818913bde6b561"
[[package]]
name = "unicode-ident"
version = "1.0.12"
@ -3304,9 +3321,9 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "versions"
version = "5.0.1"
version = "6.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c73a36bc44e3039f51fbee93e39f41225f6b17b380eb70cc2aab942df06b34dd"
checksum = "f25d498b63d1fdb376b4250f39ab3a5ee8d103957346abacd911e2d8b612c139"
dependencies = [
"itertools",
"nom",

View file

@ -1,5 +1,5 @@
[workspace]
members = ["src/rust/core"]
members = [ "smdview/blah","src/rust/core"]
[package]
name = "liblumina"
@ -21,13 +21,14 @@ path = "src/rust/lib.rs"
# path = "src/rust/main.rs"
[dependencies]
lumina_core = { path = "src/rust/core" }
configparser = "3.0.2"
serde = "1.0.152"
serde_derive = "1.0.152"
quote = "1.0.27"
cxx = "1.0.83"
cxx-qt = "0.6.1"
cxx-qt-lib = "0.6.1"
cxx-qt = "0.7.1"
cxx-qt-lib = { version = "0.7.1", features = [ "qt_full" ] }
# home = "0.5.4"
dirs = "5.0.0"
# libsqlite3-sys = { version = ">=0.17.2", features = ["bundled"] }
@ -50,8 +51,8 @@ color-eyre = "0.6.3"
# cxx-qt-build generates C++ code from the `#[cxx_qt::bridge]` module
# and compiles it together with the Rust static library
[build-dependencies]
cxx-qt-build = { version = "0.6.1", features = [ "link_qt_object_files" ] }
qt-build-utils = "0.6.1"
cxx-qt-build = { version = "0.7.1", features = [ "link_qt_object_files" ] }
qt-build-utils = "0.7.1"
# [dependencies.confy]
# features = ["yaml_conf"]

View file

@ -4,7 +4,8 @@
:CATEGORY: dev
:END:
* Tasks [63%] [55/86]
* Tasks [63%] [55/87]
** TODO [#A] REWRITE FOR ALL RUST AND BUILD WITH CARGO
** TODO [#A] Plugin architecture with steel or some scheme as an extension language
** TODO [#A] Server client architecture
** TODO [#A] Organize and layout structure of rust code :maintenance:

View file

@ -1,3 +1,5 @@
use std::{env, path::PathBuf};
use cxx_qt_build::{CxxQtBuilder, QmlModule};
fn main() {
@ -17,6 +19,37 @@ fn main() {
.file("src/rust/obs.rs")
.build();
// let mut kde_include_dir = String::from("/usr/include/");
// let mut kde_lib_dir = String::from("/usr/lib/x86_64-linux-gnu/");
// if let Ok(mut include_dir) = env::var("CMAKE_INCLUDE_PATH") {
// println!("{}", include_dir);
// if let Some(include_dir) =
// include_dir.split(":").find(|s| s.contains("ki18n"))
// {
// kde_include_dir = include_dir.to_owned();
// }
// } else {
// println!(
// "cargo:warning=KDE_INCLUDEDIR is not defined, used default value: {}",
// kde_include_dir
// );
// }
// if let Ok(lib_dir) = env::var("KDE_LIBDIR") {
// kde_lib_dir = lib_dir;
// } else {
// println!(
// "cargo:warning=KDE_LIBDIR is not defined, used default value: {}",
// kde_lib_dir
// );
// }
// let ki18n_include_path = PathBuf::from(kde_include_dir)
// .canonicalize()
// .expect("Cannot get canonical path of KDE_INCLUDEDIR")
// .join("KF6")
// .join("KI18n");
// CxxQtBuilder::new()
// // Link Qt's Network library
// // - Qt Core is always linked
@ -27,13 +60,64 @@ fn main() {
// // .qt_module("Kirigami")
// // .qt_module("WebEngineQuick")
// .qt_module("Network")
// // .qt_module("Quick")
// // .qt_module("Test")
// // .qt_module("WebEngineQuick")
// // .qt_module("I18n")
// // .qt_module("CoreAddons")
// .qml_module(QmlModule {
// uri: "com.cochrun.xyz",
// rust_files: &["src/rust/settings.rs"],
// qml_files: &["src/qml/main.qml"],
// rust_files: &[
// "src/rust/settings.rs",
// "src/rust/service_item_model.rs",
// "src/rust/file_helper.rs",
// "src/rust/slide_model.rs",
// "src/rust/slide_object.rs",
// "src/rust/ytdl.rs",
// "src/rust/utils.rs",
// "src/rust/obs.rs",
// "src/rust/video_model.rs",
// "src/rust/image_model.rs",
// "src/rust/presentation_model.rs",
// // "src/rust/songs/song_model.rs",
// // "src/rust/songs/song_editor.rs",
// ],
// qrc_files: &["src/resources.qrc"],
// qml_files: &[
// "src/qml/main.qml",
// "src/qml/presenter/LeftDock.qml",
// "src/qml/presenter/ServiceList.qml",
// "src/qml/presenter/MainWindow.qml",
// "src/qml/presenter/Library.qml",
// "src/qml/presenter/LibraryItem.qml",
// "src/qml/presenter/Header.qml",
// "src/qml/presenter/Actions.qml",
// "src/qml/presenter/PanelItem.qml",
// "src/qml/presenter/SongEditor.qml",
// "src/qml/presenter/VideoEditor.qml",
// "src/qml/presenter/ImageEditor.qml",
// "src/qml/presenter/PresentationEditor.qml",
// "src/qml/presenter/SlideEditor.qml",
// "src/qml/presenter/Slide.qml",
// "src/qml/presenter/SlidesListView.qml",
// "src/qml/presenter/SongEditorSlideList.qml",
// "src/qml/presenter/DragHandle.qml",
// "src/qml/presenter/Presentation.qml",
// "src/qml/presenter/PresentationWindow.qml",
// "src/qml/presenter/PreviewSlideListDelegate.qml",
// "src/qml/presenter/PreviewSlide.qml",
// "src/qml/presenter/Settings.qml",
// "src/qml/presenter/RangedSlider.qml",
// "src/qml/presenter/NewVideo.qml",
// "src/qml/presenter/TextBackground.qml",
// "src/qml/presenter/TextBox.qml",
// "src/qml/presenter/LoadingSpinner.qml",
// ],
// ..Default::default()
// })
// .cc_builder(|cc| {
// cc.include("cpp");
// cc.include(format!("{}", ki18n_include_path.display()));
// })
// .build();
}

131
flake.lock generated
View file

@ -1,15 +1,56 @@
{
"nodes": {
"fenix": {
"inputs": {
"nixpkgs": "nixpkgs",
"rust-analyzer-src": "rust-analyzer-src"
},
"locked": {
"lastModified": 1753252982,
"narHash": "sha256-brrpvP+4GRXLHjvnDr1j1/yA4117hzs6t9IT60JuSI8=",
"owner": "nix-community",
"repo": "fenix",
"rev": "8546562a84feb5370ce57493277b6f2c3cbdc432",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "fenix",
"type": "github"
}
},
"fenix_2": {
"inputs": {
"nixpkgs": [
"naersk",
"nixpkgs"
],
"rust-analyzer-src": "rust-analyzer-src_2"
},
"locked": {
"lastModified": 1752475459,
"narHash": "sha256-z6QEu4ZFuHiqdOPbYss4/Q8B0BFhacR8ts6jO/F/aOU=",
"owner": "nix-community",
"repo": "fenix",
"rev": "bf0d6f70f4c9a9cf8845f992105652173f4b617f",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "fenix",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
@ -20,14 +61,15 @@
},
"naersk": {
"inputs": {
"nixpkgs": "nixpkgs"
"fenix": "fenix_2",
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1721727458,
"narHash": "sha256-r/xppY958gmZ4oTfLiHN0ZGuQ+RSTijDblVgVLFi1mw=",
"lastModified": 1752689277,
"narHash": "sha256-uldUBFkZe/E7qbvxa3mH1ItrWZyT6w1dBKJQF/3ZSsc=",
"owner": "nix-community",
"repo": "naersk",
"rev": "3fb418eaf352498f6b6c30592e3beb63df42ef11",
"rev": "0e72363d0938b0208d6c646d10649164c43f4d64",
"type": "github"
},
"original": {
@ -38,23 +80,43 @@
},
"nixpkgs": {
"locked": {
"lastModified": 0,
"narHash": "sha256-+yj+xgsfZaErbfYM3T+QvEE2hU7UuE+Jf0fJCJ8uPS0=",
"path": "/nix/store/6inj491lsap4ia7mmvn2gbh53jb27zq0-source",
"type": "path"
"lastModified": 1752950548,
"narHash": "sha256-NS6BLD0lxOrnCiEOcvQCDVPXafX1/ek1dfJHX1nUIzc=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "c87b95e25065c028d31a94f06a62927d18763fdf",
"type": "github"
},
"original": {
"id": "nixpkgs",
"type": "indirect"
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1725634671,
"narHash": "sha256-v3rIhsJBOMLR8e/RNWxr828tB+WywYIoajrZKFM+0Gg=",
"lastModified": 1752077645,
"narHash": "sha256-HM791ZQtXV93xtCY+ZxG1REzhQenSQO020cu6rHtAPk=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "be9e214982e20b8310878ac2baa063a961c1bdf6",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1752950548,
"narHash": "sha256-NS6BLD0lxOrnCiEOcvQCDVPXafX1/ek1dfJHX1nUIzc=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "574d1eac1c200690e27b8eb4e24887f8df7ac27c",
"rev": "c87b95e25065c028d31a94f06a62927d18763fdf",
"type": "github"
},
"original": {
@ -66,9 +128,44 @@
},
"root": {
"inputs": {
"fenix": "fenix",
"flake-utils": "flake-utils",
"naersk": "naersk",
"nixpkgs": "nixpkgs_2"
"nixpkgs": "nixpkgs_3"
}
},
"rust-analyzer-src": {
"flake": false,
"locked": {
"lastModified": 1753204114,
"narHash": "sha256-xH8EIod+Hwog4P9OwX9hdtk6Nqr54M0tzMI71yGNOYI=",
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "b40fce3ccdc5f94453c6aca4da8b64174a03a5ad",
"type": "github"
},
"original": {
"owner": "rust-lang",
"ref": "nightly",
"repo": "rust-analyzer",
"type": "github"
}
},
"rust-analyzer-src_2": {
"flake": false,
"locked": {
"lastModified": 1752428706,
"narHash": "sha256-EJcdxw3aXfP8Ex1Nm3s0awyH9egQvB2Gu+QEnJn2Sfg=",
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "591e3b7624be97e4443ea7b5542c191311aa141d",
"type": "github"
},
"original": {
"owner": "rust-lang",
"ref": "nightly",
"repo": "rust-analyzer",
"type": "github"
}
},
"systems": {

180
flake.nix
View file

@ -6,77 +6,121 @@
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
naersk.url = "github:nix-community/naersk";
flake-utils.url = "github:numtide/flake-utils";
fenix.url = "github:nix-community/fenix";
# nixpkgs.follows = "cargo2nix/nixpkgs";
};
outputs = inputs: with inputs;
flake-utils.lib.eachDefaultSystem
(system:
let
pkgs = import nixpkgs {
inherit system;
# overlays = [cargo2nix.overlays.default];
};
naersk' = pkgs.callPackage naersk {};
# src = ./.;
# rustPkgs = pkgs.rustBuilder.makePackageSet {
# rustVersion = "1.61.0";
# packageFun = import ./Cargo.nix;
# };
# The workspace defines a development shell with all of the dependencies
# and environment settings necessary for a regular `cargo build`.
# Passes through all arguments to pkgs.mkShell for adding supplemental
# dependencies.
# workspaceShell = rustPkgs.workspaceShell {
# packages = with pkgs; [
# gcc
# stdenv
# bintools
# gnumake
# gdb
# qtcreator
# cmake
# extra-cmake-modules
# pkg-config
# libsForQt5.wrapQtAppsHook
# makeWrapper
outputs = inputs:
with inputs;
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
inherit system;
overlays = [ fenix.overlays.default ];
# overlays = [cargo2nix.overlays.default];
};
naersk' = pkgs.callPackage naersk { };
# clang-tools
# clang
# libclang
# qt5.qtbase
# qt5.qttools
# qt5.qtquickcontrols2
# qt5.qtx11extras
# qt5.qtmultimedia
# qt5.qtwayland
# qt5.qtwebengine
# libsForQt5.kirigami2
# libsForQt5.qqc2-desktop-style
# libsForQt5.karchive
# mpv
# ffmpeg_6-full
# # Rust tools
# clippy
# rustc
# cargo
# rustfmt
# rust-analyzer
# corrosion
# ];
# # shellHook = ''
# # export PS1="\033[0;31m☠dev-shell☠ $ \033[0m"
# # '';
# };
nbi = with pkgs; [
# ffmpeg
alejandra
(pkgs.fenix.stable.withComponents [
"cargo"
"clippy"
"rust-src"
"rustc"
"rustfmt"
])
rust-analyzer
];
in rec
{
# packages = {
# crate = (rustPkgs.workspace.libre-presenter { }).bin;
# default = packages.crate;
# };
devShell = import ./shell.nix { inherit pkgs; };
defaultPackage = pkgs.libsForQt5.callPackage ./default.nix { };
}
);
bi = with pkgs; [
gcc
stdenv
gnumake
gdb
qtcreator
cmake
kdePackages.extra-cmake-modules
pkg-config
qt6.wrapQtAppsHook
makeWrapper
openssl.dev
openssl.out
clang-tools
clang
libclang
# libwebp
# clang-format
qt6.full
qt6.qttools
qt6.qtbase
# qt6.qtquickcontrols2
# qt6.qtx11extras
qt6.qtmultimedia
qt6.qtwayland
qt6.qtwebengine
qt6.qtimageformats
kdePackages.kirigami
# kdePackages.kfilemetadata
# libsForQt5.breeze-icons
# libsForQt5.breeze-qt5
kdePackages.qqc2-desktop-style
# libsForQt5.kirigami-addons
# libsForQt5.ki18n
kdePackages.kcoreaddons
# libsForQt5.kguiaddons
# libsForQt5.kconfig
# podofo
mpv
kdePackages.mpvqt
ffmpeg-full
# yt-dlp
# Rust tools
just
clippy
rustc
cargo
rustfmt
rust-analyzer
sqlx-cli
cargo-watch
corrosion
];
in rec {
# packages = {
# crate = (rustPkgs.workspace.libre-presenter { }).bin;
# default = packages.crate;
# };
devShell = pkgs.mkShell {
nativeBuildInputs = nbi;
buildInputs = bi;
RUST_BACKTRACE = "1";
LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib";
CMAKE_C_COMPILER = "${pkgs.gcc}/bin/gcc";
CMAKE_CXX_COMPILER = "${pkgs.gcc}/bin/g++";
CARGO_PROFILE_RELEASE_BUILD_OVERRIDE_DEBUG = true;
# KDE_INCLUDEDIR = "${pkgs.kdePackages.kirigami.dev}/include";
# KDE_QMLDIR = "${pkgs.kdePackages.kirigami.dev}/lib/qt-6/qml/org/kde/kirigami/";
# This creates the proper qt env so that plugins are found right.
shellHook = ''
setQtEnvironment=$(mktemp --suffix .setQtEnvironment.sh)
echo "shellHook: setQtEnvironment = $setQtEnvironment"
makeQtWrapper "/bin/sh" "$setQtEnvironment" "''${qtWrapperArgs[@]}"
sed "/^exec/d" -i "$setQtEnvironment"
source "$setQtEnvironment"
'';
DATABASE_URL =
"sqlite:///home/chris/.local/share/lumina/library-db.sqlite3";
};
# devShell = import ./shell.nix { inherit pkgs; };
# defaultPackage = pkgs.libsForQt5.callPackage ./default.nix { };
});
}

View file

@ -15,7 +15,7 @@ test:
RUST_LOG=debug cargo test --benches --tests --all-features -- --nocapture
testcore:
RUST_LOG=debug cargo test -p core --benches --tests --all-features -- --nocapture
RUST_LOG=debug cargo test -p lumina_core --benches --tests --all-features -- --nocapture
alias b := build
alias r := run

17
smdview/blah/Cargo.toml Normal file
View file

@ -0,0 +1,17 @@
[package]
name = "blah"
version = "0.1.0"
edition = "2024"
[dependencies]
cxx = "1.0.122"
cxx-qt = "0.7.1"
cxx-qt-lib = { version = "0.7.1", features = [ "qt_full" ] }
cxx-qt-lib-extras = "0.7.1"
markdown = "=1.0.0-alpha.17"
[build-dependencies]
# The link_qt_object_files feature is required for statically linking Qt 6.
cxx-qt-build = { version = "0.7.1", features = [ "link_qt_object_files" ] }
qt-build-utils = "0.7.1"

12
smdview/blah/build.rs Normal file
View file

@ -0,0 +1,12 @@
use cxx_qt_build::{CxxQtBuilder, QmlModule};
fn main() {
CxxQtBuilder::new()
.qml_module(QmlModule {
uri: "org.kde.simplemdviewer",
qml_files: &["src/qml/Main.qml"],
rust_files: &["src/main.rs"],
..Default::default()
})
.build();
}

42
smdview/blah/src/main.rs Normal file
View file

@ -0,0 +1,42 @@
#[cxx_qt::bridge]
mod ffi {
extern "RustQt" {
#[qobject]
type DummyQObject = super::DummyRustStruct;
}
}
#[derive(Default)]
pub struct DummyRustStruct;
use cxx_qt_lib::{
QGuiApplication, QQmlApplicationEngine, QQuickStyle, QString,
QUrl,
};
use cxx_qt_lib_extras::QApplication;
use std::env;
fn main() {
let mut app = QApplication::new();
// To associate the executable to the installed desktop file
QGuiApplication::set_desktop_file_name(&QString::from(
"org.kde.simplemdviewer",
));
// To ensure the style is set correctly
if env::var("QT_QUICK_CONTROLS_STYLE").is_err() {
QQuickStyle::set_style(&QString::from("org.kde.desktop"));
}
let mut engine = QQmlApplicationEngine::new();
if let Some(engine) = engine.as_mut() {
engine.load(&QUrl::from(
"qrc:/qt/qml/org/kde/simplemdviewer/src/qml/Main.qml",
));
}
if let Some(app) = app.as_mut() {
app.exec();
}
}

View file

@ -0,0 +1,71 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls as Controls
import org.kde.kirigami as Kirigami
Kirigami.ApplicationWindow {
id: root
title: "Simple Markdown Viewer in Rust 🦀"
minimumWidth: Kirigami.Units.gridUnit * 20
minimumHeight: Kirigami.Units.gridUnit * 20
width: minimumWidth
height: minimumHeight
pageStack.initialPage: initPage
Component {
id: initPage
Kirigami.Page {
title: "Markdown Viewer"
ColumnLayout {
anchors {
top: parent.top
left: parent.left
right: parent.right
}
Controls.TextArea {
id: sourceArea
placeholderText: "Write some Markdown code here"
wrapMode: Text.WrapAnywhere
Layout.fillWidth: true
Layout.minimumHeight: Kirigami.Units.gridUnit * 5
}
RowLayout {
Layout.fillWidth: true
Controls.Button {
text: "Format"
onClicked: formattedText.text = sourceArea.text
}
Controls.Button {
text: "Clear"
onClicked: {
sourceArea.text = ""
formattedText.text = ""
}
}
}
Controls.Label {
id: formattedText
textFormat: Text.RichText
wrapMode: Text.WordWrap
text: sourceArea.text
Layout.fillWidth: true
Layout.minimumHeight: Kirigami.Units.gridUnit * 5
}
}
}
}
}

View file

@ -1,49 +1,49 @@
#include <QApplication>
#include <QQmlApplicationEngine>
// #include <QApplication>
#include <QtQml/QQmlApplicationEngine>
#include <QtCore/qstringliteral.h>
#include <QtQml>
#include <QUrl>
#include <QDebug>
#include <KLocalizedContext>
#include <KLocalizedString>
#include <KAboutData>
#include <QtQml/QtQml>
#include <QtCore/QUrl>
#include <QtCore/QDebug>
// #include <K/KLocalizedContext>
// #include <KLocalizedString>
// #include <KAboutData>
#include <iostream>
#include <QQmlEngine>
#include <QtWebEngineQuick>
#include <QObject>
#include <QtGlobal>
#include <QOpenGLContext>
#include <QGuiApplication>
#include <QQuickStyle>
#include <QSurfaceFormat>
#include <QtQml/QQmlEngine>
#include <QtWebEngineQuick/QtWebEngineQuick>
#include <QtCore/QObject>
#include <QtCore/QtGlobal>
#include <QtGui/QOpenGLContext>
#include <QtGui/QGuiApplication>
#include <QtQuickControls2/QQuickStyle>
// #include <QSurfaceFormat>
#include <QtQuick/QQuickWindow>
#include <QtQuick/QQuickView>
#include <qapplication.h>
#include <qcoreapplication.h>
#include <qdir.h>
#include <qglobal.h>
#include <qguiapplication.h>
#include <qqml.h>
#include <qquickstyle.h>
#include <qstringliteral.h>
// #include <qapplication.h>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qdir.h>
#include <QtCore/qglobal.h>
#include <QtGui/qguiapplication.h>
#include <QtQml/qqml.h>
#include <QtQuickControls2/qquickstyle.h>
#include <QtCore/qstringliteral.h>
// #include <MpvAbstractItem>
// #include "cpp/mpv/mpvitem.h"
// #include "cpp/mpv/mpvproperties.h"
// RUST
#include "cxx-qt-gen/file_helper.cxxqt.h"
#include "cxx-qt-gen/slide_object.cxxqt.h"
#include "cxx-qt-gen/slide_model.cxxqt.h"
#include "cxx-qt-gen/service_item_model.cxxqt.h"
#include "cxx-qt-gen/settings.cxxqt.h"
#include "cxx-qt-gen/ytdl.cxxqt.h"
#include "cxx-qt-gen/presentation_model.cxxqt.h"
#include "cxx-qt-gen/song_model.cxxqt.h"
#include "cxx-qt-gen/video_model.cxxqt.h"
#include "cxx-qt-gen/image_model.cxxqt.h"
#include "cxx-qt-gen/utils.cxxqt.h"
#include "cxx-qt-gen/song_editor.cxxqt.h"
#include "cxx-qt-gen/obs.cxxqt.h"
#include <liblumina/src/rust/file_helper.cxxqt.h>
#include <liblumina/src/rust/slide_object.cxxqt.h>
#include <liblumina/src/rust/slide_model.cxxqt.h>
#include <liblumina/src/rust/service_item_model.cxxqt.h>
#include <liblumina/src/rust/settings.cxxqt.h>
#include <liblumina/src/rust/ytdl.cxxqt.h>
#include <liblumina/src/rust/presentation_model.cxxqt.h>
#include <liblumina/src/rust/songs/song_model.cxxqt.h>
#include <liblumina/src/rust/video_model.cxxqt.h>
#include <liblumina/src/rust/image_model.cxxqt.h>
#include <liblumina/src/rust/utils.cxxqt.h>
#include <liblumina/src/rust/songs/song_editor.cxxqt.h>
#include <liblumina/src/rust/obs.cxxqt.h>
static QWindow *windowFromEngine(QQmlApplicationEngine *engine)
{
@ -60,22 +60,22 @@ int main(int argc, char *argv[])
QGuiApplication::setWindowIcon(QIcon::fromTheme(QStringLiteral("video-display")));
QtWebEngineQuick::initialize();
QGuiApplication app(argc, argv);
KLocalizedString::setApplicationDomain("lumina");
KAboutData about;
about.setComponentName(QStringLiteral("lumina"));
about.setDisplayName(i18n("lumina"));
about.setVersion(QByteArray("0.1"));
about.setShortDescription(i18n("A churchpresentation app build with KDE tech."));
about.setLicense(KAboutLicense::GPL_V3);
// KLocalizedString::setApplicationDomain("lumina");
// KAboutData about;
// about.setComponentName(QStringLiteral("lumina"));
// about.setDisplayName(i18n("lumina"));
// about.setVersion(QByteArray("0.1"));
// about.setShortDescription(i18n("A churchpresentation app build with KDE tech."));
// about.setLicense(KAboutLicense::GPL_V3);
// overwrite default-generated values of organizationDomain & desktopFileName
about.setOrganizationDomain("tfcconnection.org");
about.setDesktopFileName(QStringLiteral("org.tfcconneciton.lumina"));
// about.setOrganizationDomain("tfcconnection.org");
// about.setDesktopFileName(QStringLiteral("org.tfcconneciton.lumina"));
QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL);
// set the application metadata
KAboutData::setApplicationData(about);
// KAboutData::setApplicationData(about);
QCoreApplication::setOrganizationName(QStringLiteral("lumina"));
QCoreApplication::setOrganizationDomain(QStringLiteral("tfcconnection.org"));
QCoreApplication::setApplicationName(QStringLiteral("lumina"));
@ -93,7 +93,7 @@ int main(int argc, char *argv[])
// qDebug() << QQuickStyle::availableStyles();
qDebug() << QIcon::themeName();
qDebug() << QApplication::platformName();
// qDebug() << QApplication::platformName();
//Need to instantiate our slide
QScopedPointer<SlideModel> slideModel(new SlideModel);
@ -108,6 +108,7 @@ int main(int argc, char *argv[])
settings->setup();
QQuickView *PresWindow = new QQuickView;
PresWindow->setSource(QUrl(QStringLiteral("qrc:qml/presenter/PresentationWindow.qml")));
qDebug() << PresWindow;
qDebug() << PresWindow->isVisible();
@ -169,9 +170,9 @@ int main(int argc, char *argv[])
qmlRegisterSingletonInstance("org.presenter", 1, 0, "SlideModel", slideModel.get());
qmlRegisterSingletonInstance("org.presenter", 1, 0, "Utils", utils);
qmlRegisterSingletonInstance("org.presenter", 1, 0, "SlideObject", slideobject.get());
qmlRegisterSingletonInstance("org.presenter", 1, 0, "PresWindow", PresWindow);
qmlRegisterSingletonInstance("org.presenter", 1, 0, "RSettings", settings);
qmlRegisterSingletonInstance("org.presenter", 1, 0, "ObsModel", obsModel.get());
qmlRegisterSingletonInstance("org.presenter", 1, 0, "PresWindow", PresWindow);
// This is the same slideobject, however to enusre that the PresWindow can have it
// we need to set it as a separate context so that it can change it's slides too.
@ -184,7 +185,7 @@ int main(int argc, char *argv[])
QQmlApplicationEngine engine;
qDebug() << app.allWindows();
engine.rootContext()->setContextObject(new KLocalizedContext(&engine));
// engine.rootContext()->setContextObject(new KLocalizedContext(&engine));
engine.load(QUrl(QStringLiteral("qrc:qml/main.qml")));
qDebug() << "Engine loaded";

View file

@ -252,6 +252,7 @@ Item {
}
function isDragFile(item) {
console.log(item);
console.log(item.toString());
var extension = item.toString().split('.').pop();
var valid = false;

View file

@ -188,6 +188,10 @@ Controls.Page {
songModel: songModel
}
Presenter.PresentationWindow {
id: presWindow
}
Connections {
target: ServiceItemModel
function onSaveProgressChanged() {
@ -352,19 +356,20 @@ Controls.Page {
function present(present) {
if (present)
{
PresWindow.showFullScreen();
PresWindow.setSource("qrc:qml/presenter/PresentationWindow.qml")
console.log(PresWindow);
/* presWindow.slideObj = SlideObject; */
presWindow.showFullScreen();
/* presWindow.setSource("qrc:qml/presenter/PresentationWindow.qml") */
console.log(presWindow);
/* presWinLoader.active = true; */
}
else {
PresWindow.close();
presWindow.close();
/* presWinLoader.active = false; */
}
}
function closeAll() { PresWindow.close() }
function closeAll() { presWindow.close() }
function changeVidPos(pos) {
presentation.slide.seek(pos);

View file

@ -479,6 +479,11 @@ FocusScope {
previewSlide.stopVideo()
}
function playVideo() {
/* showPassiveNotification("Stopping Video") */
previewSlide.playVideo()
}
function nextSlideAction() {
keyHandler.forceActiveFocus();
SlideModel.next()

View file

@ -6,13 +6,13 @@ import org.kde.kirigami 2.13 as Kirigami
import "./" as Presenter
import org.presenter 1.0
Item {
Window {
id: presentationWindow
property Item slide: presentationSlide
/* property var slideObj */
/* property var SlideObject: SlideObject; */
property var pWin
anchors.fill: parent
/* anchors.fill: parent */
/* title: "presentation-window" */
/* height: maximumHeight */
@ -25,7 +25,7 @@ Item {
/* onClosing: { */
/* presentationSlide.stopVideo(); */
/* SlideObj.pause(); */
/* SlideObject.pause(); */
/* presentationSlide.stopAudio(); */
/* presenting = false; */
/* } */
@ -34,7 +34,7 @@ Item {
target: PresWindow
function onClosing() {
presentationSlide.stopVideo();
SlideObj.pause();
SlideObject.pause();
presentationSlide.stopAudio();
presenting = false;
}
@ -48,25 +48,25 @@ Item {
Presenter.Slide {
id: presentationSlide
anchors.fill: parent
imageSource: SlideObj.html ? "" : SlideObj.imageBackground
webSource: SlideObj.html ? SlideObj.imageBackground : ""
htmlVisible: SlideObj.html
videoSource: presentationWindow.visible ? SlideObj.videoBackground : ""
audioSource: SlideObj.audio
text: SlideObj.text
chosenFont: SlideObj.font
textSize: SlideObj.fontSize
pdfIndex: SlideObj.slideIndex
itemType: SlideObj.ty
vidLoop: SlideObj.looping
vidStartTime: SlideObj.videoStartTime
vidEndTime: SlideObj.videoEndTime
imageSource: SlideObject.html ? "" : SlideObject.imageBackground
webSource: SlideObject.html ? SlideObject.imageBackground : ""
htmlVisible: SlideObject.html
videoSource: presentationWindow.visible ? SlideObject.videoBackground : ""
audioSource: SlideObject.audio
text: SlideObject.text
chosenFont: SlideObject.font
textSize: SlideObject.fontSize
pdfIndex: SlideObject.slideIndex
itemType: SlideObject.ty
vidLoop: SlideObject.looping
vidStartTime: SlideObject.videoStartTime
vidEndTime: SlideObject.videoEndTime
}
Connections {
target: SlideObj
target: SlideObject
function onVideoBackgroundChanged() {
if (SlideObj.videoBackground === "")
if (SlideObject.videoBackground === "")
stopVideo();
else {
loadVideo();
@ -74,12 +74,12 @@ Item {
}
}
function onIsPlayingChanged() {
if(SlideObj.isPlaying)
if(SlideObject.isPlaying)
presentationSlide.playVideo();
pauseVideo();
}
function onLoopingChanged() {
if(SlideObj.looping)
if(SlideObject.looping)
presentationSlide.loopVideo();
}
function onAudioChanged() {

View file

@ -21,6 +21,7 @@ Kirigami.OverlaySheet {
Kirigami.FormLayout {
implicitHeight: Kirigami.Units.gridUnit * 30
implicitWidth: Kirigami.Units.gridUnit * 30
Controls.ComboBox {
id: screenSelectionField
Kirigami.FormData.label: i18nc("@label:textbox", "Presentation Screen:")

View file

@ -1,5 +1,5 @@
[package]
name = "lumina-core"
name = "lumina_core"
version = "0.1.0"
edition = "2021"
authors = [

View file

@ -1,82 +1,133 @@
use crate::{
images::{get_image_from_db, Image},
kinds::ServiceItemKind,
model::get_db,
presentations::{
get_presentation_from_db, PresKind, Presentation,
},
service_items::ServiceItem,
slides::Background,
songs::{get_song_from_db, Song},
videos::{get_video_from_db, Video},
};
use color_eyre::eyre::{eyre, Context, Result};
use serde_json::Value;
use sqlx::{query, query_as, FromRow, SqliteConnection};
use std::{
fs::{self, File},
iter,
path::{Path, PathBuf},
};
use tar::{Archive, Builder};
use tracing::error;
use zstd::Encoder;
use std::{fs::{self, File}, future::Future, iter, path::{Path, PathBuf}};
use color_eyre::eyre::{eyre, Result};
use serde_json::Value;
use sqlx::{query, query_as, FromRow, SqliteConnection};
use crate::{images::{get_image_from_db, Image}, kinds::ServiceItemKind, model::get_db, presentations::{get_presentation_from_db, PresKind, Presentation}, service_items::ServiceItem, slides::Background, songs::{get_song_from_db, Song}, videos::{get_video_from_db, Video}};
pub async fn save(list: Vec<ServiceItem>, path: impl AsRef<Path>) -> Result<()> {
pub async fn save(
list: Vec<ServiceItem>,
path: impl AsRef<Path>,
) -> Result<()> {
let path = path.as_ref();
let save_file = File::create(path)?;
let mut db = get_db().await;
let json = process_service_items(&list, &mut db).await?;
let archive = store_service_items(&list, &mut db, &save_file, &json).await?;
let archive =
store_service_items(&list, &mut db, &save_file, &json)
.await?;
Ok(())
}
async fn store_service_items(items: &Vec<ServiceItem>, db: &mut SqliteConnection, save_file: &File, json: &Value) -> Result<()> {
async fn store_service_items(
items: &Vec<ServiceItem>,
db: &mut SqliteConnection,
save_file: &File,
json: &Value,
) -> Result<()> {
let encoder = Encoder::new(save_file, 3).unwrap();
let mut tar = Builder::new(encoder);
let mut temp_dir = dirs::data_dir().unwrap();
temp_dir.push("lumina");
let mut s: String =
iter::repeat_with(fastrand::alphanumeric)
.take(5)
.collect();
iter::repeat_with(fastrand::alphanumeric).take(5).collect();
s.insert_str(0, "temp_");
temp_dir.push(s);
fs::create_dir_all(&temp_dir)?;
let service_file = temp_dir.join("serviceitems.json");
fs::File::create(&service_file)?;
match fs::File::options().read(true).write(true).open(service_file) {
match fs::File::options()
.read(true)
.write(true)
.open(service_file)
{
Ok(f) => {
serde_json::to_writer_pretty(f, json)?;
},
Err(e) => error!("There were problems making a file i guess: {e}"),
}
Err(e) => {
error!("There were problems making a file i guess: {e}")
}
};
for item in items {
let background;
let audio: Option<PathBuf>;
match item.kind {
ServiceItemKind::Song => {
let song = get_song_from_db(item.database_id, db).await?;
let song =
get_song_from_db(item.database_id, db).await?;
background = song.background;
audio = song.audio;
},
}
ServiceItemKind::Image => {
let image = get_image_from_db(item.database_id, db).await?;
let image =
get_image_from_db(item.database_id, db).await?;
background = Some(Background::try_from(image.path)?);
audio = None;
},
}
ServiceItemKind::Video => {
let video = get_video_from_db(item.database_id, db).await?;
let video =
get_video_from_db(item.database_id, db).await?;
background = Some(Background::try_from(video.path)?);
audio = None;
},
}
ServiceItemKind::Presentation(_) => {
let presentation = get_presentation_from_db(item.database_id, db).await?;
background = Some(Background::try_from(presentation.path)?);
let presentation =
get_presentation_from_db(item.database_id, db)
.await?;
background =
Some(Background::try_from(presentation.path)?);
audio = None;
},
}
ServiceItemKind::Content => {
todo!()
},
}
};
if let Some(file) = audio {
let audio_file = temp_dir.join(file.file_name().expect("Audio file couldn't be added to temp_dir"));
match fs::File::create(&audio_file) {
Ok(_) => Ok(fs::copy(file, &audio_file)?),
Err(e) => Err(eyre!("Couldn't create audio file: {e}")),
}?;
let audio_file =
temp_dir.join(file.file_name().expect(
"Audio file couldn't be added to temp_dir",
));
if let Ok(file) = file.strip_prefix("file://") {
fs::File::create(&audio_file)
.wrap_err("Couldn't create audio file")?;
fs::copy(file, audio_file).wrap_err("Audio file could not be copied, the source file doesn't exist not be found");
} else {
fs::File::create(&audio_file)
.wrap_err("Couldn't create audio file")?;
fs::copy(file, audio_file).wrap_err("Audio file could not be copied, the source file doesn't exist not be found");
}
};
if let Some(file) = background {
let background_file = temp_dir.join(file.path.file_name().expect("Background file couldn't be added to temp_dir"));
match fs::File::create(&background_file) {
Ok(_) => Ok(fs::copy(file.path, &background_file)?),
Err(e) => Err(eyre!("Couldn't create background file: {e}")),
}?;
let background_file =
temp_dir.join(file.path.file_name().expect(
"Background file couldn't be added to temp_dir",
));
if let Ok(file) = file.path.strip_prefix("file://") {
fs::File::create(&background_file)
.wrap_err("Couldn't create background file")?;
fs::copy(file, background_file).wrap_err("Background file could not be copied, the source file doesn't exist not be found");
} else {
fs::File::create(&background_file)
.wrap_err("Couldn't create background file")?;
fs::copy(file.path, background_file).wrap_err("Background file could not be copied, the source file doesn't exist not be found");
}
}
}
Ok(())
@ -86,66 +137,96 @@ async fn clear_temp_dir(temp_dir: &Path) -> Result<()> {
todo!()
}
async fn process_service_items(items: &Vec<ServiceItem>, db: &mut SqliteConnection) -> Result<Value> {
async fn process_service_items(
items: &Vec<ServiceItem>,
db: &mut SqliteConnection,
) -> Result<Value> {
let mut values: Vec<Value> = vec![];
for item in items {
match item.kind {
ServiceItemKind::Song => {
let value = process_song(item.database_id, db).await?;
let value =
process_song(item.database_id, db).await?;
values.push(value);
},
}
ServiceItemKind::Image => {
let value = process_image(item.database_id, db).await?;
let value =
process_image(item.database_id, db).await?;
values.push(value);
},
}
ServiceItemKind::Video => {
let value = process_video(item.database_id, db).await?;
let value =
process_video(item.database_id, db).await?;
values.push(value);
},
}
ServiceItemKind::Presentation(_) => {
let value = process_presentation(item.database_id, db).await?;
let value =
process_presentation(item.database_id, db)
.await?;
values.push(value);
},
}
ServiceItemKind::Content => {
todo!()
},
}
}
}
let json = Value::from(values);
Ok(json)
}
async fn process_song(database_id: i32, db: &mut SqliteConnection) -> Result<Value> {
async fn process_song(
database_id: i32,
db: &mut SqliteConnection,
) -> Result<Value> {
let song = get_song_from_db(database_id, db).await?;
let song_json = serde_json::to_value(&song)?;
let kind_json = serde_json::to_value(ServiceItemKind::Song)?;
let json = serde_json::json!({"item": song_json, "kind": kind_json});
let json =
serde_json::json!({"item": song_json, "kind": kind_json});
Ok(json)
}
async fn process_image(database_id: i32, db: &mut SqliteConnection) -> Result<Value> {
async fn process_image(
database_id: i32,
db: &mut SqliteConnection,
) -> Result<Value> {
let image = get_image_from_db(database_id, db).await?;
let image_json = serde_json::to_value(&image)?;
let kind_json = serde_json::to_value(ServiceItemKind::Image)?;
let json = serde_json::json!({"item": image_json, "kind": kind_json});
let json =
serde_json::json!({"item": image_json, "kind": kind_json});
Ok(json)
}
async fn process_video(database_id: i32, db: &mut SqliteConnection) -> Result<Value> {
async fn process_video(
database_id: i32,
db: &mut SqliteConnection,
) -> Result<Value> {
let video = get_video_from_db(database_id, db).await?;
let video_json = serde_json::to_value(&video)?;
let kind_json = serde_json::to_value(ServiceItemKind::Video)?;
let json = serde_json::json!({"item": video_json, "kind": kind_json});
let json =
serde_json::json!({"item": video_json, "kind": kind_json});
Ok(json)
}
async fn process_presentation(database_id: i32, db: &mut SqliteConnection) -> Result<Value> {
let presentation = get_presentation_from_db(database_id, db).await?;
async fn process_presentation(
database_id: i32,
db: &mut SqliteConnection,
) -> Result<Value> {
let presentation =
get_presentation_from_db(database_id, db).await?;
let presentation_json = serde_json::to_value(&presentation)?;
let kind_json = match presentation.kind {
PresKind::Html => serde_json::to_value(ServiceItemKind::Presentation(PresKind::Html))?,
PresKind::Pdf => serde_json::to_value(ServiceItemKind::Presentation(PresKind::Pdf))?,
PresKind::Generic => serde_json::to_value(ServiceItemKind::Presentation(PresKind::Generic))?,
PresKind::Html => serde_json::to_value(
ServiceItemKind::Presentation(PresKind::Html),
)?,
PresKind::Pdf => serde_json::to_value(
ServiceItemKind::Presentation(PresKind::Pdf),
)?,
PresKind::Generic => serde_json::to_value(
ServiceItemKind::Presentation(PresKind::Generic),
)?,
};
let json = serde_json::json!({"item": presentation_json, "kind": kind_json});
Ok(json)
@ -155,11 +236,11 @@ async fn process_presentation(database_id: i32, db: &mut SqliteConnection) -> Re
mod test {
use std::path::PathBuf;
use fs::canonicalize;
use sqlx::Connection;
use pretty_assertions::{assert_eq, assert_ne};
use tracing::debug;
use super::*;
use fs::canonicalize;
use pretty_assertions::assert_eq;
use sqlx::Connection;
use tracing::debug;
async fn get_db() -> SqliteConnection {
let mut data = dirs::data_local_dir().unwrap();
@ -167,9 +248,7 @@ mod test {
data.push("library-db.sqlite3");
let mut db_url = String::from("sqlite://");
db_url.push_str(data.to_str().unwrap());
SqliteConnection::connect(&db_url)
.await
.expect("problems")
SqliteConnection::connect(&db_url).await.expect("problems")
}
#[tokio::test(flavor = "current_thread")]
@ -239,7 +318,8 @@ mod test {
async fn test_process_presentation() {
let mut db = get_db().await;
let result = process_presentation(54, &mut db).await;
let json_presentation_file = PathBuf::from("./test/test_presentation.json");
let json_presentation_file =
PathBuf::from("./test/test_presentation.json");
if let Ok(path) = canonicalize(json_presentation_file) {
debug!(file = ?&path);
if let Ok(s) = fs::read_to_string(path) {
@ -252,7 +332,9 @@ mod test {
panic!("String wasn't read from file");
}
} else {
panic!("Cannot find absolute path to test_presentation.json");
panic!(
"Cannot find absolute path to test_presentation.json"
);
}
}
@ -281,7 +363,8 @@ mod test {
async fn test_service_items() {
let mut db = get_db().await;
let items = get_items();
let json_item_file = PathBuf::from("./test/test_service_items.json");
let json_item_file =
PathBuf::from("./test/test_service_items.json");
let result = process_service_items(&items, &mut db).await;
if let Ok(path) = canonicalize(json_item_file) {
if let Ok(s) = fs::read_to_string(path) {
@ -305,16 +388,23 @@ mod test {
#[tokio::test]
async fn test_store() {
let path = PathBuf::from("/home/chris/dev/lumina/src/rust/core/test.pres");
let save_file = match File::create(path) {
let path = PathBuf::from(
"/home/chris/dev/lumina/src/rust/core/test.pres",
);
let save_file = match File::create(path) {
Ok(f) => f,
Err(e) => panic!("Couldn't create save_file: {e}"),
};
let mut db = get_db().await;
let list = get_items();
if let Ok(json) = process_service_items(&list, &mut db).await {
if let Ok(json) = process_service_items(&list, &mut db).await
{
println!("{:?}", json);
match store_service_items(&list, &mut db, &save_file, &json).await {
match store_service_items(
&list, &mut db, &save_file, &json,
)
.await
{
Ok(_) => assert!(true),
Err(e) => panic!("There was an error: {e}"),
}

View file

@ -5,7 +5,9 @@ use sqlx::{query_as, SqliteConnection};
use std::path::PathBuf;
use tracing::error;
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
#[derive(
Clone, Debug, Default, PartialEq, Serialize, Deserialize,
)]
pub struct Image {
pub id: i32,
pub title: String,
@ -16,7 +18,7 @@ impl Model<Image> {
pub fn load_from_db(&mut self) {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let result = query_as!(Image, r#"SELECT title as "title!", filePath as "path!", id as "id: i32" from images"#).fetch_all(&mut self.db).await;
let result = query_as!(Image, r#"SELECT title as "title!", file_path as "path!", id as "id: i32" from images"#).fetch_all(&mut self.db).await;
match result {
Ok(v) => {
for image in v.into_iter() {
@ -29,9 +31,11 @@ impl Model<Image> {
}
}
pub async fn get_image_from_db(database_id: i32, db: &mut SqliteConnection) -> Result<Image> {
Ok(query_as!(Image, r#"SELECT title as "title!", filePath as "path!", id as "id: i32" from images where id = ?"#, database_id).fetch_one(db).await?)
pub async fn get_image_from_db(
database_id: i32,
db: &mut SqliteConnection,
) -> Result<Image> {
Ok(query_as!(Image, r#"SELECT title as "title!", file_path as "path!", id as "id: i32" from images where id = ?"#, database_id).fetch_one(db).await?)
}
#[cfg(test)]
@ -67,7 +71,10 @@ mod test {
let new_image = test_image("A newer image".into());
match result {
Ok(_) => {
assert_eq!(&image, image_model.find(|i| i.id == 0).unwrap());
assert_eq!(
&image,
image_model.find(|i| i.id == 0).unwrap()
);
assert_ne!(
&new_image,
image_model.find(|i| i.id == 0).unwrap()

View file

@ -4,7 +4,9 @@ use serde::{Deserialize, Serialize};
use crate::presentations::PresKind;
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[derive(
Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize,
)]
pub enum ServiceItemKind {
#[default]
Song,

View file

@ -1,3 +1,4 @@
pub mod file;
pub mod images;
pub mod kinds;
pub mod model;
@ -6,4 +7,3 @@ pub mod service_items;
pub mod slides;
pub mod songs;
pub mod videos;
pub mod file;

1
src/rust/core/mod.rs Normal file
View file

@ -0,0 +1 @@
mod service_items;

View file

@ -36,8 +36,7 @@ impl<T> Model<T> {
Ok(())
}
pub fn get_item(&self, index: i32) -> Option<&T>
{
pub fn get_item(&self, index: i32) -> Option<&T> {
self.items.get(index as usize)
}
@ -60,10 +59,8 @@ impl<T> Default for Model<T> {
items: vec![],
db: {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
get_db().await
})
}
rt.block_on(async { get_db().await })
},
}
}
}
@ -74,9 +71,7 @@ pub async fn get_db() -> SqliteConnection {
data.push("library-db.sqlite3");
let mut db_url = String::from("sqlite://");
db_url.push_str(data.to_str().unwrap());
SqliteConnection::connect(&db_url)
.await
.expect("problems")
SqliteConnection::connect(&db_url).await.expect("problems")
}
pub trait Modeling {

View file

@ -1,12 +1,16 @@
use std::path::PathBuf;
use color_eyre::eyre::Result;
use serde::{Deserialize, Serialize};
use sqlx::{prelude::FromRow, query, sqlite::SqliteRow, Row, SqliteConnection};
use sqlx::{
prelude::FromRow, query, sqlite::SqliteRow, Row, SqliteConnection,
};
use std::path::PathBuf;
use tracing::error;
use crate::model::Model;
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[derive(
Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize,
)]
pub enum PresKind {
Html,
#[default]
@ -14,7 +18,9 @@ pub enum PresKind {
Generic,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[derive(
Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize,
)]
pub struct Presentation {
pub id: i32,
pub title: String,
@ -57,7 +63,7 @@ impl Model<Presentation> {
pub fn load_from_db(&mut self) {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let result = query!(r#"SELECT id as "id: i32", title, filePath as "path", html from presentations"#).fetch_all(&mut self.db).await;
let result = query!(r#"SELECT id as "id: i32", title, file_path as "path", html from presentations"#).fetch_all(&mut self.db).await;
match result {
Ok(v) => {
for presentation in v.into_iter() {
@ -79,8 +85,11 @@ impl Model<Presentation> {
}
}
pub async fn get_presentation_from_db(database_id: i32, db: &mut SqliteConnection) -> Result<Presentation> {
let row = query(r#"SELECT id as "id: i32", title, filePath as "path", html from presentations where id = $1"#).bind(database_id).fetch_one(db).await?;
pub async fn get_presentation_from_db(
database_id: i32,
db: &mut SqliteConnection,
) -> Result<Presentation> {
let row = query(r#"SELECT id as "id: i32", title, file_path as "path", html from presentations where id = $1"#).bind(database_id).fetch_one(db).await?;
Ok(Presentation::from_row(&row)?)
}
@ -111,7 +120,9 @@ mod test {
let mut presentation_model: Model<Presentation> =
Model::default();
presentation_model.load_from_db();
if let Some(presentation) = presentation_model.find(|p| p.id == 54) {
if let Some(presentation) =
presentation_model.find(|p| p.id == 54)
{
let test_presentation = test_presentation();
assert_eq!(&test_presentation, presentation);
} else {

View file

@ -15,7 +15,7 @@ pub struct ServiceItem {
}
#[derive(Debug, Default, PartialEq)]
struct ServiceItemModel {
pub struct ServiceItemModel {
items: Vec<ServiceItem>,
}
@ -52,7 +52,9 @@ impl From<&Image> for ServiceItem {
impl From<&Presentation> for ServiceItem {
fn from(presentation: &Presentation) -> Self {
Self {
kind: ServiceItemKind::Presentation(presentation.kind.clone()),
kind: ServiceItemKind::Presentation(
presentation.kind.clone(),
),
database_id: presentation.id,
..Default::default()
}
@ -60,7 +62,10 @@ impl From<&Presentation> for ServiceItem {
}
impl ServiceItemModel {
fn add_item(&mut self, item: impl Into<ServiceItem>) -> Result<()> {
fn add_item(
&mut self,
item: impl Into<ServiceItem>,
) -> Result<()> {
let service_item: ServiceItem = item.into();
self.items.push(service_item);
Ok(())
@ -94,7 +99,6 @@ mod test {
}
}
#[test]
pub fn test_service_item() {
let song = test_song();
@ -104,10 +108,16 @@ mod test {
let mut service_model = ServiceItemModel::default();
match service_model.add_item(&song) {
Ok(_) => {
assert_eq!(ServiceItemKind::Song, service_model.items[0].kind);
assert_eq!(ServiceItemKind::Presentation(PresKind::Html), pres_item.kind);
assert_eq!(
ServiceItemKind::Song,
service_model.items[0].kind
);
assert_eq!(
ServiceItemKind::Presentation(PresKind::Html),
pres_item.kind
);
assert_eq!(service_item, service_model.items[0]);
},
}
Err(e) => panic!("Problem adding item: {:?}", e),
}
}

View file

@ -1,4 +1,8 @@
use std::{error::Error, fmt::Display, path::{Path, PathBuf}};
use std::{
error::Error,
fmt::Display,
path::{Path, PathBuf},
};
use color_eyre::eyre::{eyre, Result};
use serde::{Deserialize, Serialize};
@ -10,7 +14,9 @@ use crate::{
presentations::Presentation, songs::Song, videos::Video,
};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[derive(
Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize,
)]
pub enum TextAlignment {
TopLeft,
TopCenter,
@ -24,7 +30,9 @@ pub enum TextAlignment {
BottomRight,
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[derive(
Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize,
)]
pub struct Background {
pub path: PathBuf,
pub kind: BackgroundKind,
@ -36,7 +44,7 @@ impl TryFrom<String> for Background {
let value = value.trim_start_matches("file://");
let path = PathBuf::from(value);
if !path.exists() {
return Err(ParseError::DoesNotExist)
return Err(ParseError::DoesNotExist);
}
let extension = value.rsplit_once('.').unwrap_or_default();
match extension.1 {
@ -106,11 +114,15 @@ impl DatabaseError for ParseError {
todo!()
}
fn as_error_mut(&mut self) -> &mut (dyn Error + Send + Sync + 'static) {
fn as_error_mut(
&mut self,
) -> &mut (dyn Error + Send + Sync + 'static) {
todo!()
}
fn into_error(self: Box<Self>) -> Box<dyn Error + Send + Sync + 'static> {
fn into_error(
self: Box<Self>,
) -> Box<dyn Error + Send + Sync + 'static> {
todo!()
}
@ -128,15 +140,15 @@ impl Display for ParseError {
Self::NonBackgroundFile => {
"The file is not a recognized image or video type"
}
Self::DoesNotExist => {
"This file doesn't exist"
}
Self::DoesNotExist => "This file doesn't exist",
};
write!(f, "Error: {message}")
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[derive(
Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize,
)]
pub enum BackgroundKind {
#[default]
Image,

View file

@ -2,7 +2,10 @@ use std::{collections::HashMap, path::PathBuf};
use color_eyre::eyre::{eyre, Context, Result};
use serde::{Deserialize, Serialize};
use sqlx::{query, query_as, sqlite::SqliteRow, FromRow, Row, SqliteConnection};
use sqlx::{
query, query_as, sqlite::SqliteRow, FromRow, Row,
SqliteConnection,
};
use tracing::{debug, error};
use crate::{
@ -10,7 +13,9 @@ use crate::{
slides::{Background, TextAlignment},
};
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[derive(
Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize,
)]
pub struct Song {
pub id: i32,
pub title: String,
@ -56,17 +61,24 @@ impl FromRow<'_, SqliteRow> for Song {
text_alignment: Some({
let horizontal_alignment: String = row.try_get(3)?;
let vertical_alignment: String = row.try_get(4)?;
match (horizontal_alignment.to_lowercase().as_str(), vertical_alignment.to_lowercase().as_str()) {
match (
horizontal_alignment.to_lowercase().as_str(),
vertical_alignment.to_lowercase().as_str(),
) {
("left", "top") => TextAlignment::TopLeft,
("left", "center") => TextAlignment::MiddleLeft,
("left", "bottom") => TextAlignment::BottomLeft,
("center", "top") => TextAlignment::TopCenter,
("center", "center") => TextAlignment::MiddleCenter,
("center", "bottom") => TextAlignment::BottomCenter,
("center", "center") => {
TextAlignment::MiddleCenter
}
("center", "bottom") => {
TextAlignment::BottomCenter
}
("right", "top") => TextAlignment::TopRight,
("right", "center") => TextAlignment::MiddleRight,
("right", "bottom") => TextAlignment::BottomRight,
_ => TextAlignment::MiddleCenter
_ => TextAlignment::MiddleCenter,
}
}),
font: row.try_get(6)?,
@ -75,19 +87,20 @@ impl FromRow<'_, SqliteRow> for Song {
}
}
pub async fn get_song_from_db(index: i32, db: &mut SqliteConnection) -> Result<Song> {
let row = query(r#"SELECT vorder as "verse_order!", fontSize as "font_size!: i32", backgroundType as "background_type!", horizontalTextAlignment as "horizontal_text_alignment!", verticalTextAlignment 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 where id = $1"#).bind(index).fetch_one(db).await?;
pub async fn get_song_from_db(
index: i32,
db: &mut SqliteConnection,
) -> Result<Song> {
let row = 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 where id = $1"#).bind(index).fetch_one(db).await?;
Ok(Song::from_row(&row)?)
}
impl Model<Song> {
pub fn load_from_db(&mut self) {
// static DATABASE_URL: &str = "sqlite:///home/chris/.local/share/lumina/library-db.sqlite3";
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let result = query(r#"SELECT vorder as "verse_order!", fontSize as "font_size!: i32", backgroundType as "background_type!", horizontalTextAlignment as "horizontal_text_alignment!", verticalTextAlignment 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(&mut self.db).await;
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(&mut self.db).await;
match result {
Ok(s) => {
for song in s.into_iter() {

0
src/rust/core/test.pres Normal file
View file

View file

@ -5,7 +5,9 @@ use sqlx::{query_as, SqliteConnection};
use std::path::PathBuf;
use tracing::error;
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
#[derive(
Clone, Debug, Default, PartialEq, Serialize, Deserialize,
)]
pub struct Video {
pub id: i32,
pub title: String,
@ -19,7 +21,7 @@ impl Model<Video> {
pub fn load_from_db(&mut self) {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let result = query_as!(Video, r#"SELECT title as "title!", filePath as "path!", startTime as "start_time!: f32", endTime 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;
match result {
Ok(v) => {
for video in v.into_iter() {
@ -32,12 +34,13 @@ impl Model<Video> {
}
}
pub async fn get_video_from_db(database_id: i32, db: &mut SqliteConnection) -> Result<Video> {
Ok(query_as!(Video, r#"SELECT title as "title!", filePath as "path!", startTime as "start_time!: f32", endTime as "end_time!: f32", loop as "looping!", id as "id: i32" from videos where id = ?"#, database_id).fetch_one(db).await?)
pub async fn get_video_from_db(
database_id: i32,
db: &mut SqliteConnection,
) -> Result<Video> {
Ok(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 where id = ?"#, database_id).fetch_one(db).await?)
}
#[cfg(test)]
mod test {
use super::*;
@ -71,7 +74,10 @@ mod test {
let new_video = test_video("A newer video".into());
match result {
Ok(_) => {
assert_eq!(&video, video_model.find(|v| v.id == 0).unwrap());
assert_eq!(
&video,
video_model.find(|v| v.id == 0).unwrap()
);
assert_ne!(
&new_video,
video_model.find(|v| v.id == 0).unwrap()

View file

@ -10,6 +10,9 @@ mod file_helper {
// include!("cxx-qt-lib/qvariant.h");
// type QVariant = cxx_qt_lib::QVariant;
}
#[auto_cxx_name]
#[auto_rust_name]
unsafe extern "RustQt" {
#[qobject]
#[qml_element]
@ -96,7 +99,9 @@ impl file_helper::FileHelper {
QUrl::from(string.as_str())
}
} else {
error!("There was an error, is xdg-desktop-portals correctly setup?");
error!(
"There was an error, is xdg-desktop-portals correctly setup?"
);
QUrl::default()
}
}
@ -145,7 +150,9 @@ impl file_helper::FileHelper {
QUrl::from(string.as_str())
}
} else {
error!("Couldn't load file, is xdg-desktop-portals correctly setup?");
error!(
"Couldn't load file, is xdg-desktop-portals correctly setup?"
);
QUrl::default()
}
}

View file

@ -2,6 +2,7 @@
pub mod image_model {
unsafe extern "C++" {
include!(< QAbstractListModel >);
type QAbstractListModel;
include!("cxx-qt-lib/qhash.h");
type QHash_i32_QByteArray =
cxx_qt_lib::QHash<cxx_qt_lib::QHashPair_i32_QByteArray>;
@ -31,15 +32,18 @@ pub mod image_model {
Title,
}
#[auto_cxx_name]
#[auto_rust_name]
unsafe extern "RustQt" {
#[qobject]
#[base = "QAbstractListModel"]
#[base = QAbstractListModel]
#[qml_element]
#[qproperty(i32, count)]
type ImageModel = super::ImageModelRust;
#[inherit]
#[qsignal]
#[cxx_name = "dataChanged"]
fn data_changed(
self: Pin<&mut ImageModel>,
top_left: &QModelIndex,
@ -81,8 +85,11 @@ pub mod image_model {
impl cxx_qt::Threading for ImageModel {}
#[auto_cxx_name]
#[auto_rust_name]
unsafe extern "RustQt" {
#[inherit]
#[cxx_name = "beginInsertRows"]
unsafe fn begin_insert_rows(
self: Pin<&mut ImageModel>,
parent: &QModelIndex,
@ -91,9 +98,11 @@ pub mod image_model {
);
#[inherit]
#[cxx_name = "endInsertRows"]
unsafe fn end_insert_rows(self: Pin<&mut ImageModel>);
#[inherit]
#[cxx_name = "beginRemoveRows"]
unsafe fn begin_remove_rows(
self: Pin<&mut ImageModel>,
parent: &QModelIndex,
@ -102,6 +111,7 @@ pub mod image_model {
);
#[inherit]
#[cxx_name = "beginMoveRows"]
unsafe fn begin_move_rows(
self: Pin<&mut ImageModel>,
source_parent: &QModelIndex,
@ -112,18 +122,23 @@ pub mod image_model {
) -> bool;
#[inherit]
#[cxx_name = "endMoveRows"]
unsafe fn end_move_rows(self: Pin<&mut ImageModel>);
#[inherit]
#[cxx_name = "endRemoveRows"]
unsafe fn end_remove_rows(self: Pin<&mut ImageModel>);
#[inherit]
#[cxx_name = "beginResetModel"]
unsafe fn begin_reset_model(self: Pin<&mut ImageModel>);
#[inherit]
#[cxx_name = "endResetModel"]
unsafe fn end_reset_model(self: Pin<&mut ImageModel>);
#[inherit]
#[cxx_name = "canFetchMore"]
fn can_fetch_more(
self: &ImageModel,
parent: &QModelIndex,
@ -147,18 +162,20 @@ pub mod image_model {
#[qinvokable]
#[cxx_override]
#[cxx_name = "roleNames"]
fn role_names(self: &ImageModel) -> QHash_i32_QByteArray;
#[qinvokable]
#[cxx_override]
#[cxx_name = "rowCount"]
fn row_count(self: &ImageModel, _parent: &QModelIndex)
-> i32;
-> i32;
}
}
use cxx_qt::CxxQtType;
use cxx_qt_lib::{QModelIndex, QString, QUrl, QVariant};
use sqlx::{query, query_as, Connection, SqliteConnection};
use sqlx::{Connection, SqliteConnection, query, query_as};
use std::path::PathBuf;
use std::pin::Pin;
use tracing::{debug, error};
@ -181,7 +198,7 @@ pub struct ImageModelRust {
highest_id: i32,
images: Vec<Image>,
inner_images: Vec<Image>,
db: SqliteConnection
db: SqliteConnection,
}
impl Default for ImageModelRust {
@ -199,9 +216,11 @@ impl Default for ImageModelRust {
let mut db_url = String::from("sqlite://");
db_url.push_str(data.to_str().unwrap());
rt.block_on(async {
SqliteConnection::connect(&db_url).await.expect("problems")
SqliteConnection::connect(&db_url)
.await
.expect("problems")
})
}
},
}
}
}
@ -215,11 +234,13 @@ pub fn get_image(index: i32) -> color_eyre::Result<Image> {
let mut db_url = String::from("sqlite://");
db_url.push_str(data.to_str().unwrap());
rt.block_on(async {
SqliteConnection::connect(&db_url).await.expect("problems")
SqliteConnection::connect(&db_url)
.await
.expect("problems")
})
};
rt.block_on(async {
let result = query_as!(Image, r#"SELECT id as "id: i32", title as "title!", filePath as "path!" from images where id = ?"#, index).fetch_one(&mut db).await?;
let result = query_as!(Image, r#"SELECT id as "id: i32", title as "title!", file_path as "path!" from images where id = ?"#, index).fetch_one(&mut db).await?;
Ok(result)
})
}
@ -236,7 +257,7 @@ impl image_model::ImageModel {
pub fn setup(mut self: Pin<&mut Self>) {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let result = query_as!(Image, r#"SELECT id as "id: i32", title as "title!", filePath as "path!" from images"#).fetch_all(&mut self.as_mut().rust_mut().db).await;
let result = query_as!(Image, r#"SELECT id as "id: i32", title as "title!", file_path as "path!" from images"#).fetch_all(&mut self.as_mut().rust_mut().db).await;
match result {
Ok(i) => i.into_iter().for_each(|i| self.as_mut().add_image(i)),
Err(e) => error!("There was an error in converting songs: {e}"),
@ -251,7 +272,10 @@ impl image_model::ImageModel {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let result = query!("delete from images where id = ?", index).execute(&mut self.as_mut().rust_mut().db).await;
let result =
query!("delete from images where id = ?", index)
.execute(&mut self.as_mut().rust_mut().db)
.await;
match result {
Ok(_i) => {
unsafe {
@ -308,7 +332,7 @@ impl image_model::ImageModel {
rt.block_on(async {
let image_title = image_title.to_string();
let image_path = image_path.to_string();
let result = query!(r#"INSERT into images (id, title, filePath) VALUES (?, ?, ?)"#,
let result = query!(r#"INSERT into images (id, title, file_path) VALUES (?, ?, ?)"#,
image_id,
image_title,
image_path)
@ -367,9 +391,13 @@ impl image_model::ImageModel {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let title = updated_title.to_string();
let result = query!("UPDATE images SET title = ? where id = ?", title, index)
.execute(&mut self.as_mut().rust_mut().db)
.await;
let result = query!(
"UPDATE images SET title = ? where id = ?",
title,
index
)
.execute(&mut self.as_mut().rust_mut().db)
.await;
match result {
Ok(_i) => {
for image in self
@ -380,9 +408,11 @@ impl image_model::ImageModel {
.filter(|x| x.id == index)
{
image.title = title.clone();
debug!(title = image.title,
title = title,
"updated image title");
debug!(
title = image.title,
title = title,
"updated image title"
);
}
self.as_mut().data_changed(
model_index,
@ -394,7 +424,7 @@ impl image_model::ImageModel {
Err(e) => {
error!("Error connecting to db: {e}");
false
},
}
}
});
true
@ -414,9 +444,13 @@ impl image_model::ImageModel {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let updated_path = updated_path.to_string();
let result = query!("UPDATE images SET filePath = ? where id = ?", updated_path, index)
.execute(&mut self.as_mut().rust_mut().db)
.await;
let result = query!(
"UPDATE images SET file_path = ? where id = ?",
updated_path,
index
)
.execute(&mut self.as_mut().rust_mut().db)
.await;
match result {
Ok(_i) => {
for image in self
@ -427,9 +461,11 @@ impl image_model::ImageModel {
.filter(|x| x.id == index)
{
image.path = updated_path.clone();
debug!(title = image.title,
path = updated_path,
"updated image path");
debug!(
title = image.title,
path = updated_path,
"updated image path"
);
}
self.as_mut().data_changed(
model_index,
@ -441,7 +477,7 @@ impl image_model::ImageModel {
Err(e) => {
error!("Error connecting to db: {e}");
false
},
}
}
});
true

View file

@ -8,10 +8,9 @@ pub mod service_item_model;
pub mod settings;
pub mod slide_model;
pub mod slide_object;
pub mod slide_types;
pub mod songs;
pub mod utils;
pub mod video_model;
pub mod ytdl;
pub mod slide_types;
// pub mod core;
// mod video_thumbnail;

View file

@ -4,8 +4,20 @@ pub mod settings;
fn main() {
let mut app = QGuiApplication::new();
let mut engine = QQmlApplicationEngine::new();
let mut kde_qml_dir =
String::from("/usr/lib/x86_64-linux-gnu/qt6/qml/");
if let Ok(qml_dir) = env::var("NIXPKGS_QT6_QML_IMPORT_PATH") {
kde_qml_dir = qml_dir;
} else {
println!(
"cargo:warning=KDE_QMLDIR is not defined, used default value: {}",
kde_qml_dir
);
}
if let Some(engine) = engine.as_mut() {
engine.add_import_path(&QString::from(kde_qml_dir.as_str()));
engine.load(&QUrl::from(
"qrc:/qt/qml/com/cochrun/xyz/qml/main.qml",
));

View file

@ -1,9 +1,8 @@
use core::fmt;
use cxx_qt::CxxQtType;
use cxx_qt_lib::{QString, QStringList};
use obws::responses::scenes::{CurrentProgramScene, Scenes};
use obws::Client;
use obws::responses::scenes::{CurrentProgramScene, Scenes};
use std::{error::Error, pin::Pin};
use tracing::{debug, error};
@ -22,7 +21,10 @@ impl fmt::Debug for Obs {
f.debug_struct("Client")
.field("scenes", &self.scenes)
.field("client", &self.client.is_some())
.field("current_program_scene", &self.current_program_scene)
.field(
"current_program_scene",
&self.current_program_scene,
)
.finish()
}
}
@ -37,8 +39,6 @@ impl Clone for Obs {
}
}
impl Obs {
pub async fn new() -> Result<Self, Box<dyn Error>> {
let client =
@ -112,7 +112,7 @@ impl Obs {
fn make_client() -> Client {
let runtime = tokio::runtime::Runtime::new().unwrap();
let future = Client::connect("localhost", 4455, Some(""));
runtime.block_on(future).unwrap()
}
@ -127,6 +127,8 @@ mod obs {
type QList_QString = cxx_qt_lib::QList<QString>;
}
#[auto_cxx_name]
#[auto_rust_name]
unsafe extern "RustQt" {
#[qobject]
#[qml_element]
@ -218,7 +220,7 @@ impl obs::ObsModel {
#[cfg(test)]
mod test {
use super::*;
#[ignore]
#[test]
pub fn test_obs_setting_scene() {

View file

@ -2,6 +2,7 @@
pub mod presentation_model {
unsafe extern "C++" {
include!(< QAbstractListModel >);
type QAbstractListModel;
include!("cxx-qt-lib/qhash.h");
type QHash_i32_QByteArray =
cxx_qt_lib::QHash<cxx_qt_lib::QHashPair_i32_QByteArray>;
@ -33,15 +34,18 @@ pub mod presentation_model {
PageCount,
}
#[auto_cxx_name]
#[auto_rust_name]
unsafe extern "RustQt" {
#[qobject]
#[base = "QAbstractListModel"]
#[base = QAbstractListModel]
#[qml_element]
#[qproperty(i32, count)]
type PresentationModel = super::PresentationModelRust;
#[inherit]
#[qsignal]
#[cxx_name = "dataChanged"]
fn data_changed(
self: Pin<&mut PresentationModel>,
top_left: &QModelIndex,
@ -99,8 +103,11 @@ pub mod presentation_model {
impl cxx_qt::Threading for PresentationModel {}
#[auto_cxx_name]
#[auto_rust_name]
unsafe extern "RustQt" {
#[inherit]
#[cxx_name = "beginInsertRows"]
unsafe fn begin_insert_rows(
self: Pin<&mut PresentationModel>,
parent: &QModelIndex,
@ -109,9 +116,11 @@ pub mod presentation_model {
);
#[inherit]
#[cxx_name = "endInsertRows"]
unsafe fn end_insert_rows(self: Pin<&mut PresentationModel>);
#[inherit]
#[cxx_name = "beginRemoveRows"]
unsafe fn begin_remove_rows(
self: Pin<&mut PresentationModel>,
parent: &QModelIndex,
@ -120,6 +129,7 @@ pub mod presentation_model {
);
#[inherit]
#[cxx_name = "beginMoveRows"]
unsafe fn begin_move_rows(
self: Pin<&mut PresentationModel>,
source_parent: &QModelIndex,
@ -130,20 +140,25 @@ pub mod presentation_model {
) -> bool;
#[inherit]
#[cxx_name = "endMoveRows"]
unsafe fn end_move_rows(self: Pin<&mut PresentationModel>);
#[inherit]
#[cxx_name = "endRemoveRows"]
unsafe fn end_remove_rows(self: Pin<&mut PresentationModel>);
#[inherit]
#[cxx_name = "beginResetModel"]
unsafe fn begin_reset_model(
self: Pin<&mut PresentationModel>,
);
#[inherit]
#[cxx_name = "endResetModel"]
unsafe fn end_reset_model(self: Pin<&mut PresentationModel>);
#[inherit]
#[cxx_name = "canFetchMore"]
fn can_fetch_more(
self: &PresentationModel,
parent: &QModelIndex,
@ -167,12 +182,14 @@ pub mod presentation_model {
#[qinvokable]
#[cxx_override]
#[cxx_name = "roleNames"]
fn role_names(
self: &PresentationModel,
) -> QHash_i32_QByteArray;
#[qinvokable]
#[cxx_override]
#[cxx_name = "rowCount"]
fn row_count(
self: &PresentationModel,
_parent: &QModelIndex,
@ -184,7 +201,7 @@ use crate::presentation_model::presentation_model::QMap_QString_QVariant;
use crate::reveal_js;
use cxx_qt::CxxQtType;
use cxx_qt_lib::{QModelIndex, QString, QUrl, QVariant};
use sqlx::{query, query_as, Connection, SqliteConnection};
use sqlx::{Connection, SqliteConnection, query, query_as};
use std::path::PathBuf;
use std::pin::Pin;
use tracing::{debug, error};
@ -208,7 +225,7 @@ pub struct PresentationModelRust {
highest_id: i32,
presentations: Vec<Presentation>,
inner_presentations: Vec<Presentation>,
db: SqliteConnection
db: SqliteConnection,
}
impl Default for PresentationModelRust {
@ -226,14 +243,18 @@ impl Default for PresentationModelRust {
let mut db_url = String::from("sqlite://");
db_url.push_str(data.to_str().unwrap());
rt.block_on(async {
SqliteConnection::connect(&db_url).await.expect("problems")
SqliteConnection::connect(&db_url)
.await
.expect("problems")
})
}
},
}
}
}
pub fn get_presentation(index: i32) -> color_eyre::Result<Presentation> {
pub fn get_presentation(
index: i32,
) -> color_eyre::Result<Presentation> {
let rt = tokio::runtime::Runtime::new().unwrap();
let mut db = {
let mut data = dirs::data_local_dir().unwrap();
@ -242,11 +263,13 @@ pub fn get_presentation(index: i32) -> color_eyre::Result<Presentation> {
let mut db_url = String::from("sqlite://");
db_url.push_str(data.to_str().unwrap());
rt.block_on(async {
SqliteConnection::connect(&db_url).await.expect("problems")
SqliteConnection::connect(&db_url)
.await
.expect("problems")
})
};
rt.block_on(async {
let result = query_as!(Presentation, r#"SELECT id as "id: i32", title as "title!", filePath as "path!", html as "html!", pageCount as "page_count!: i32" from presentations where id = ?"#, index).fetch_one(&mut db).await?;
let result = query_as!(Presentation, r#"SELECT id as "id: i32", title as "title!", file_path as "path!", html as "html!", pageCount as "page_count!: i32" from presentations where id = ?"#, index).fetch_one(&mut db).await?;
Ok(result)
})
}
@ -263,7 +286,7 @@ impl presentation_model::PresentationModel {
pub fn setup(mut self: Pin<&mut Self>) {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let result = query_as!(Presentation, r#"SELECT id as "id: i32", title as "title!", filePath as "path!", html as "html!", pageCount as "page_count!: i32" from presentations"#).fetch_all(&mut self.as_mut().rust_mut().db).await;
let result = query_as!(Presentation, r#"SELECT id as "id: i32", title as "title!", file_path as "path!", html as "html!", pageCount as "page_count!: i32" from presentations"#).fetch_all(&mut self.as_mut().rust_mut().db).await;
match result {
Ok(p) => p.into_iter().for_each(|p| self.as_mut().add_presentation(p)),
Err(e) => error!("There was an error in converting songs: {e}"),
@ -278,7 +301,12 @@ impl presentation_model::PresentationModel {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let result = query!("delete from presentations where id = ?", index).execute(&mut self.as_mut().rust_mut().db).await;
let result = query!(
"delete from presentations where id = ?",
index
)
.execute(&mut self.as_mut().rust_mut().db)
.await;
match result {
Ok(_i) => {
unsafe {
@ -365,7 +393,7 @@ impl presentation_model::PresentationModel {
rt.block_on(async {
let presentation_title = presentation_title.to_string();
let presentation_path = presentation_path.to_string();
let result = query!(r#"INSERT into presentations (id, title, filePath, html, pageCount) VALUES (?, ?, ?, ?, ?)"#,
let result = query!(r#"INSERT into presentations (id, title, file_path, html, pageCount) VALUES (?, ?, ?, ?, ?)"#,
presentation_id,
presentation_title,
presentation_path,
@ -503,9 +531,13 @@ impl presentation_model::PresentationModel {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let title = updated_title.to_string();
let result = query!("UPDATE presentations SET title = ? where id = ?", title, index)
.execute(&mut self.as_mut().rust_mut().db)
.await;
let result = query!(
"UPDATE presentations SET title = ? where id = ?",
title,
index
)
.execute(&mut self.as_mut().rust_mut().db)
.await;
match result {
Ok(_i) => {
for presentation in self
@ -516,9 +548,11 @@ impl presentation_model::PresentationModel {
.filter(|x| x.id == index)
{
presentation.title = title.clone();
debug!(title = presentation.title,
title = title,
"updated presentation title");
debug!(
title = presentation.title,
title = title,
"updated presentation title"
);
}
self.as_mut().data_changed(
model_index,
@ -530,7 +564,7 @@ impl presentation_model::PresentationModel {
Err(e) => {
error!("Error connecting to db: {e}");
false
},
}
}
});
true
@ -549,9 +583,13 @@ impl presentation_model::PresentationModel {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let result = query!("UPDATE presentations SET pageCount = ? where id = ?", updated_page_count, index)
.execute(&mut self.as_mut().rust_mut().db)
.await;
let result = query!(
"UPDATE presentations SET pageCount = ? where id = ?",
updated_page_count,
index
)
.execute(&mut self.as_mut().rust_mut().db)
.await;
match result {
Ok(_i) => {
for presentation in self
@ -561,10 +599,13 @@ impl presentation_model::PresentationModel {
.iter_mut()
.filter(|x| x.id == index)
{
presentation.page_count = updated_page_count.clone();
debug!(title = presentation.title,
page_count = updated_page_count,
"updated presentation page_count");
presentation.page_count =
updated_page_count.clone();
debug!(
title = presentation.title,
page_count = updated_page_count,
"updated presentation page_count"
);
}
self.as_mut().data_changed(
model_index,
@ -576,7 +617,7 @@ impl presentation_model::PresentationModel {
Err(e) => {
error!("Error connecting to db: {e}");
false
},
}
}
});
true

View file

@ -2,6 +2,7 @@
mod service_item_model {
unsafe extern "C++" {
include!(< QAbstractListModel >);
type QAbstractListModel;
include!("cxx-qt-lib/qhash.h");
type QHash_i32_QByteArray =
cxx_qt_lib::QHash<cxx_qt_lib::QHashPair_i32_QByteArray>;
@ -43,9 +44,11 @@ mod service_item_model {
Id,
}
#[auto_cxx_name]
#[auto_rust_name]
unsafe extern "RustQt" {
#[qobject]
#[base = "QAbstractListModel"]
#[base = QAbstractListModel]
#[qml_element]
#[qproperty(i32, count)]
#[qproperty(f32, save_progress)]
@ -54,6 +57,7 @@ mod service_item_model {
#[inherit]
#[qsignal]
#[cxx_name = "dataChanged"]
fn data_changed(
self: Pin<&mut ServiceItemModel>,
top_left: &QModelIndex,
@ -182,17 +186,20 @@ mod service_item_model {
#[qinvokable]
fn save(self: Pin<&mut ServiceItemModel>, file: QUrl)
-> bool;
-> bool;
#[qinvokable]
fn load(self: Pin<&mut ServiceItemModel>, file: QUrl)
-> bool;
-> bool;
}
impl cxx_qt::Threading for ServiceItemModel {}
#[auto_cxx_name]
#[auto_rust_name]
unsafe extern "RustQt" {
#[inherit]
#[cxx_name = "beginInsertRows"]
unsafe fn begin_insert_rows(
self: Pin<&mut ServiceItemModel>,
parent: &QModelIndex,
@ -201,9 +208,11 @@ mod service_item_model {
);
#[inherit]
#[cxx_name = "endInsertRows"]
unsafe fn end_insert_rows(self: Pin<&mut ServiceItemModel>);
#[inherit]
#[cxx_name = "beginRemoveRows"]
unsafe fn begin_remove_rows(
self: Pin<&mut ServiceItemModel>,
parent: &QModelIndex,
@ -212,6 +221,7 @@ mod service_item_model {
);
#[inherit]
#[cxx_name = "beginMoveRows"]
unsafe fn begin_move_rows(
self: Pin<&mut ServiceItemModel>,
source_parent: &QModelIndex,
@ -222,18 +232,23 @@ mod service_item_model {
) -> bool;
#[inherit]
#[cxx_name = "endMoveRows"]
unsafe fn end_move_rows(self: Pin<&mut ServiceItemModel>);
#[inherit]
#[cxx_name = "endRemoveRows"]
unsafe fn end_remove_rows(self: Pin<&mut ServiceItemModel>);
#[inherit]
#[cxx_name = "beginResetModel"]
unsafe fn begin_reset_model(self: Pin<&mut ServiceItemModel>);
#[inherit]
#[cxx_name = "endResetModel"]
unsafe fn end_reset_model(self: Pin<&mut ServiceItemModel>);
#[inherit]
#[cxx_name = "canFetchMore"]
fn can_fetch_more(
self: &ServiceItemModel,
parent: &QModelIndex,
@ -257,12 +272,14 @@ mod service_item_model {
#[qinvokable]
#[cxx_override]
#[cxx_name = "roleNames"]
fn role_names(
self: &ServiceItemModel,
) -> QHash_i32_QByteArray;
#[qinvokable]
#[cxx_override]
#[cxx_name = "rowCount"]
fn row_count(
self: &ServiceItemModel,
_parent: &QModelIndex,
@ -271,15 +288,21 @@ mod service_item_model {
}
}
use self::service_item_model::{
QHash_i32_QByteArray, QMap_QString_QVariant, QVector_i32,
ServiceRoles,
};
use crate::service_item_model::service_item_model::QList_QString;
use crate::songs::song_model::{get_song, Song};
use crate::slide_types::SlideType;
use crate::songs::song_model::{Song, get_song};
use crate::{image_model, presentation_model, video_model};
use cxx_qt::{CxxQtType, Threading};
use cxx_qt_lib::{
QByteArray, QModelIndex, QString, QStringList, QUrl, QVariant,
};
use dirs;
use serde_json::{json, Value};
// use lumina_core::service_items::ServiceItem as SI;
use serde_json::{Value, json};
use std::iter;
use std::path::{Path, PathBuf};
use std::pin::Pin;
@ -288,12 +311,6 @@ use std::{fs, println};
use tar::{Archive, Builder};
use tracing::{debug, error};
use zstd::{Decoder, Encoder};
use self::service_item_model::{
QHash_i32_QByteArray, QMap_QString_QVariant, QVector_i32,
ServiceRoles,
};
use crate::slide_types::SlideType;
use lumina_core::service_items::ServiceItem as SI;
use super::service_item_model::service_item_model::ServiceItemModel;
@ -377,7 +394,9 @@ impl service_item_model::ServiceItemModel {
pub fn remove_items(mut self: Pin<&mut Self>) {
let mut indices = vec![];
let mut items = self.service_items.clone();
for (index, _item) in items.iter_mut().enumerate().filter(|(_y, x)| x.selected) {
for (index, _item) in
items.iter_mut().enumerate().filter(|(_y, x)| x.selected)
{
let index = index as i32;
indices.push(index);
}
@ -436,7 +455,10 @@ impl service_item_model::ServiceItemModel {
self.as_mut().end_insert_rows();
}
debug!("ADDING: {:?}", &service_item);
self.as_mut().item_added(&service_item.database_id.unwrap_or_default(), &QString::from(&service_item.ty.to_string()));
self.as_mut().item_added(
&service_item.database_id.unwrap_or_default(),
&QString::from(&service_item.ty.to_string()),
);
}
pub fn insert_item(
@ -475,7 +497,11 @@ impl service_item_model::ServiceItemModel {
self.as_mut().end_insert_rows();
}
debug!("ADDING: {:?}", &service_item);
self.as_mut().item_inserted(&index, &service_item.database_id.unwrap_or_default(), &QString::from(&service_item.ty.to_string()));
self.as_mut().item_inserted(
&index,
&service_item.database_id.unwrap_or_default(),
&QString::from(&service_item.ty.to_string()),
);
}
pub fn get_item(
@ -876,8 +902,7 @@ impl service_item_model::ServiceItemModel {
Some(name) => {
println!("audio: {:?}", &name);
if name.to_str().unwrap() != "temp" {
flat_audio =
name.to_str().unwrap()
flat_audio = name.to_str().unwrap()
} else {
flat_audio = "";
}
@ -961,12 +986,18 @@ impl service_item_model::ServiceItemModel {
debug!(time = ?now.elapsed(), "file written");
std::thread::spawn(move || {
debug!(time = ?now.elapsed(), "idk");
let dir = fs::read_dir(&temp_dir).expect("idk");
let dir = fs::read_dir(&temp_dir)
.expect("idk");
for (index, file) in dir.enumerate() {
if let Ok(file) = file {
let file_name = file.file_name();
let file_name =
file.file_name();
debug!(?file, ?file_name);
let mut file = std::fs::File::open(file.path()).expect("missing file");
let mut file =
std::fs::File::open(
file.path(),
)
.expect("missing file");
tar.append_file(file_name, &mut file).expect("Error in moving file to tar");
thread.queue(move |mut service| {
service
@ -977,10 +1008,11 @@ impl service_item_model::ServiceItemModel {
)
}).expect("Problem queuing on cxx thread");
}
}
if let Ok(encoder) = tar.into_inner() {
if let Ok(done) = encoder.finish() {
if let Ok(encoder) = tar.into_inner()
{
if let Ok(done) = encoder.finish()
{
debug!(time = ?now.elapsed(), ?done, "tar finished");
thread.queue(move |mut service| {
service.as_mut().set_save_progress(100.0);
@ -1000,9 +1032,7 @@ impl service_item_model::ServiceItemModel {
}
} else {
fs::remove_dir_all(&temp_dir)
.expect(
"error in removal",
);
.expect("error in removal");
false
}
});
@ -1058,7 +1088,9 @@ impl service_item_model::ServiceItemModel {
if !file_path.exists() {
match file.unpack_in(&datadir) {
Ok(_t) => (),
Err(e) => error!("Error unpacking archive: {}", e),
Err(e) => {
error!("Error unpacking archive: {}", e)
}
}
}
}
@ -1280,7 +1312,9 @@ impl service_item_model::ServiceItemModel {
..Default::default()
};
self.as_mut().add_service_item(&service_item);
error!("Loaded service item with generic type since it was an unknown type.");
error!(
"Loaded service item with generic type since it was an unknown type."
);
// // files implement the Read trait
}
}
@ -1341,7 +1375,9 @@ impl service_item_model::ServiceItemModel {
let mut vector_roles = QVector_i32::default();
vector_roles.append(self.as_ref().get_role(role));
if let Some(index) =
self.as_ref().service_items.iter().position(|x| x.database_id.unwrap_or_default() == item_id)
self.as_ref().service_items.iter().position(|x| {
x.database_id.unwrap_or_default() == item_id
})
{
let model_index = self.as_ref().index(
index as i32,
@ -1355,7 +1391,6 @@ impl service_item_model::ServiceItemModel {
}
}
pub fn load_last_saved(self: Pin<&mut Self>) -> bool {
todo!();
// Don't actually need
@ -1394,9 +1429,9 @@ impl service_item_model::ServiceItemModel {
ServiceRoles::Name => {
QVariant::from(&service_item.name)
}
ServiceRoles::Type => {
QVariant::from(&QString::from(&service_item.ty.clone().to_string()))
}
ServiceRoles::Type => QVariant::from(&QString::from(
&service_item.ty.clone().to_string(),
)),
ServiceRoles::Audio => {
QVariant::from(&service_item.audio)
}
@ -1433,9 +1468,9 @@ impl service_item_model::ServiceItemModel {
ServiceRoles::VideoEndTime => {
QVariant::from(&service_item.video_end_time)
}
ServiceRoles::Id => {
QVariant::from(&service_item.database_id.unwrap_or_default())
}
ServiceRoles::Id => QVariant::from(
&service_item.database_id.unwrap_or_default(),
),
_ => QVariant::default(),
};
}
@ -1503,22 +1538,21 @@ impl service_item_model::ServiceItemModel {
ServiceRoles::VideoEndTime.repr,
QByteArray::from("videoEndTime"),
);
roles.insert(
ServiceRoles::Id.repr,
QByteArray::from("id"),
);
roles.insert(ServiceRoles::Id.repr, QByteArray::from("id"));
roles
}
pub fn row_count(&self, _parent: &QModelIndex) -> i32 {
// println!("row count is {cnt}");
self.service_items.len() as i32
}
}
impl ServiceItemModelRust {
pub fn save(_model: Pin<&mut ServiceItemModel>, _file: QUrl) -> bool {
pub fn save(
_model: Pin<&mut ServiceItemModel>,
_file: QUrl,
) -> bool {
todo!()
}
}

View file

@ -7,6 +7,8 @@ mod settings {
type QUrl = cxx_qt_lib::QUrl;
}
#[auto_cxx_name]
#[auto_rust_name]
unsafe extern "RustQt" {
#[qobject]
#[qml_element]

View file

@ -2,6 +2,7 @@
pub mod slide_model {
unsafe extern "C++" {
include!(< QAbstractListModel >);
type QAbstractListModel;
include!("cxx-qt-lib/qhash.h");
type QHash_i32_QByteArray =
cxx_qt_lib::QHash<cxx_qt_lib::QHashPair_i32_QByteArray>;
@ -18,18 +19,23 @@ pub mod slide_model {
type QVector_i32 = cxx_qt_lib::QVector<i32>;
include!("cxx-qt-lib/qlist.h");
type QList_QString = cxx_qt_lib::QList<QString>;
include!("cxx-qt-gen/slide_object.cxxqt.h");
type SlideObject = crate::slide_object::slide_object::SlideObject;
include!("cxx-qt-gen/song_model.cxxqt.h");
type SongModel = crate::songs::song_model::song_model::SongModel;
include!("cxx-qt-gen/video_model.cxxqt.h");
include!("liblumina/src/rust/slide_object.cxxqt.h");
type SlideObject = crate::slide_object::qobject::SlideObject;
include!("liblumina/src/rust/songs/song_model.cxxqt.h");
type SongModel =
crate::songs::song_model::song_model::SongModel;
include!("liblumina/src/rust/video_model.cxxqt.h");
type VideoModel = crate::video_model::video_model::VideoModel;
include!("cxx-qt-gen/image_model.cxxqt.h");
include!("liblumina/src/rust/image_model.cxxqt.h");
type ImageModel = crate::image_model::image_model::ImageModel;
include!("cxx-qt-gen/presentation_model.cxxqt.h");
include!("liblumina/src/rust/presentation_model.cxxqt.h");
type PresentationModel = crate::presentation_model::presentation_model::PresentationModel;
}
// extern "C++" {
// type SlideObject = crate::slide_object::qobject::SlideObject;
// }
#[qenum(SlideModel)]
enum SlideRoles {
Ty,
@ -54,9 +60,11 @@ pub mod slide_model {
ObsScene,
}
#[auto_cxx_name]
#[auto_rust_name]
unsafe extern "RustQt" {
#[qobject]
#[base = "QAbstractListModel"]
#[base = QAbstractListModel]
#[qml_element]
#[qproperty(i32, count)]
#[qproperty(*mut SlideObject, slide_object)]
@ -68,6 +76,7 @@ pub mod slide_model {
#[inherit]
#[qsignal]
#[cxx_name = "dataChanged"]
fn data_changed(
self: Pin<&mut SlideModel>,
top_left: &QModelIndex,
@ -151,8 +160,11 @@ pub mod slide_model {
impl cxx_qt::Threading for SlideModel {}
#[auto_cxx_name]
#[auto_rust_name]
unsafe extern "RustQt" {
#[inherit]
#[cxx_name = "beginInsertRows"]
unsafe fn begin_insert_rows(
self: Pin<&mut SlideModel>,
parent: &QModelIndex,
@ -161,9 +173,11 @@ pub mod slide_model {
);
#[inherit]
#[cxx_name = "endInsertRows"]
unsafe fn end_insert_rows(self: Pin<&mut SlideModel>);
#[inherit]
#[cxx_name = "beginRemoveRows"]
unsafe fn begin_remove_rows(
self: Pin<&mut SlideModel>,
parent: &QModelIndex,
@ -172,15 +186,19 @@ pub mod slide_model {
);
#[inherit]
#[cxx_name = "endRemoveRows"]
unsafe fn end_remove_rows(self: Pin<&mut SlideModel>);
#[inherit]
#[cxx_name = "beginResetModel"]
unsafe fn begin_reset_model(self: Pin<&mut SlideModel>);
#[inherit]
#[cxx_name = "endResetModel"]
unsafe fn end_reset_model(self: Pin<&mut SlideModel>);
#[inherit]
#[cxx_name = "beginMoveRows"]
unsafe fn begin_move_rows(
self: Pin<&mut SlideModel>,
source_parent: &QModelIndex,
@ -191,9 +209,11 @@ pub mod slide_model {
) -> bool;
#[inherit]
#[cxx_name = "endMoveRows"]
unsafe fn end_move_rows(self: Pin<&mut SlideModel>);
#[inherit]
#[cxx_name = "canFetchMore"]
fn can_fetch_more(
self: &SlideModel,
parent: &QModelIndex,
@ -217,38 +237,43 @@ pub mod slide_model {
#[qinvokable]
#[cxx_override]
#[cxx_name = "roleNames"]
fn role_names(self: &SlideModel) -> QHash_i32_QByteArray;
#[qinvokable]
#[cxx_override]
#[cxx_name = "rowCount"]
fn row_count(self: &SlideModel, _parent: &QModelIndex)
-> i32;
-> i32;
}
}
use crate::image_model::image_model::ImageModel;
use crate::image_model::{self, Image, ImageModelRust};
use crate::obs::Obs;
use crate::presentation_model::presentation_model::PresentationModel;
use crate::presentation_model::{self, Presentation, PresentationModelRust};
use crate::presentation_model::{
self, Presentation, PresentationModelRust,
};
use crate::slide_model::slide_model::QList_QString;
use crate::songs::song_model::song_model::{self, SongModel};
use crate::songs::song_model::{get_song, Song, SongModelRust};
use crate::songs::song_model::{Song, SongModelRust, get_song};
use crate::video_model::video_model::VideoModel;
use crate::video_model::{self, Video, VideoModelRust};
use crate::{ffmpeg, slide_types::SlideType};
use crate::obs::Obs;
use crate::slide_model::slide_model::QList_QString;
use color_eyre::eyre::Result;
use color_eyre::Section;
use color_eyre::eyre::Result;
use cxx_qt::{CxxQtType, Threading};
use cxx_qt_lib::{
CaseSensitivity, QByteArray, QList, QModelIndex, QString, QStringList, QVariant
CaseSensitivity, QByteArray, QList, QModelIndex, QString,
QStringList, QVariant,
};
use slide_model::SlideObject;
use std::error::Error;
use std::fmt::{Display};
use std::fmt::Display;
use std::{path::PathBuf, pin::Pin};
use tracing::{debug, error};
use tracing::{debug, error, warn};
use self::slide_model::{
QHash_i32_QByteArray, QMap_QString_QVariant, QVector_i32,
@ -319,7 +344,9 @@ impl Display for ParseSlideError {
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}")
}
@ -336,45 +363,40 @@ impl Slide {
fn slides_from_song(song: Song) -> Result<Vec<Self>> {
let list = song.get_lyric_list();
let total = list.len();
let mut vector: Vec<Slide> = vec![];
list.iter().map(|t| t.to_string()).enumerate()
.for_each(|(i, s)| {
if song.background_type == "image" {
vector.push(
Self {
text: s,
ty: SlideType::Song,
audio: song.audio.clone(),
image_background: song.background.clone(),
video_background: "".to_owned(),
htext_alignment: song.horizontal_text_alignment.clone(),
vtext_alignment: song.vertical_text_alignment.clone(),
font: song.font.clone(),
font_size: song.font_size,
slide_count: total as i32,
slide_index: i as i32,
..Default::default()
}
);
} else {
vector.push(
Self {
text: s,
ty: SlideType::Song,
audio: song.audio.clone(),
image_background: "".to_owned(),
video_background: song.background.clone(),
htext_alignment: song.horizontal_text_alignment.clone(),
vtext_alignment: song.vertical_text_alignment.clone(),
font: song.font.clone(),
font_size: song.font_size,
slide_count: total as i32,
slide_index: i as i32,
..Default::default()
}
)
}
});
let vector = list
.iter()
.map(|t| {
let s = t.to_string();
warn!(s);
s
})
.enumerate()
.map(|(i, s)| Self {
text: s,
ty: SlideType::Song,
audio: song.audio.clone(),
image_background: if song.background_type == "image" {
song.background.clone()
} else {
"".into()
},
video_background: if song.background_type == "video" {
song.background.clone()
} else {
"".to_owned()
},
htext_alignment: song
.horizontal_text_alignment
.clone(),
vtext_alignment: song.vertical_text_alignment.clone(),
font: song.font.clone(),
font_size: song.font_size,
slide_count: total as i32,
slide_index: i as i32,
..Default::default()
})
.collect();
warn!(?vector);
Ok(vector)
}
@ -395,7 +417,9 @@ impl Slide {
})
}
fn slides_from_presentation(presentation: Presentation) -> Result<Vec<Self>> {
fn slides_from_presentation(
presentation: Presentation,
) -> Result<Vec<Self>> {
let total = presentation.page_count;
let mut slides: Vec<Slide> = vec![];
for i in 0..total {
@ -406,7 +430,7 @@ impl Slide {
image_background: presentation.path.clone(),
..Default::default()
})
};
}
Ok(slides)
}
}
@ -456,9 +480,12 @@ impl slide_model::SlideModel {
index: i32,
) -> bool {
let mut vector_roles = QVector_i32::default();
vector_roles.append(self.get_role(SlideRoles::VideoThumbnail));
vector_roles.append(self.get_role(SlideRoles::VideoBackground));
vector_roles.append(self.get_role(SlideRoles::ImageBackground));
vector_roles
.append(self.get_role(SlideRoles::VideoThumbnail));
vector_roles
.append(self.get_role(SlideRoles::VideoBackground));
vector_roles
.append(self.get_role(SlideRoles::ImageBackground));
let rc = self.as_ref().count() - 1;
let tl = self.as_ref().index(0, 0, &QModelIndex::default());
let br = self.as_ref().index(rc, 0, &QModelIndex::default());
@ -470,7 +497,11 @@ impl slide_model::SlideModel {
let path =
PathBuf::from(slide.video_background.to_string());
let screenshot = ffmpeg::bg_path_from_video(&path);
let mut screenshot_string = screenshot.clone().into_os_string().into_string().unwrap_or_default();
let mut screenshot_string = screenshot
.clone()
.into_os_string()
.into_string()
.unwrap_or_default();
screenshot_string.insert_str(0, "file://");
slide.video_thumbnail = screenshot_string;
std::thread::spawn(move || {
@ -483,7 +514,7 @@ impl slide_model::SlideModel {
"Error making video background"
),
};
match thread.queue(move |mut slide_model| {
slide_model.as_mut().data_changed(
&tl,
@ -616,37 +647,56 @@ impl slide_model::SlideModel {
SlideType::Song => {
let song = get_song(item_model_id)?;
let slides = Slide::slides_from_song(song)?;
slides.iter().for_each(|slide| self.as_mut().insert_slide(slide, index));
slides.iter().enumerate().for_each(|(i, slide)| {
self.as_mut()
.insert_slide(slide, index + i as i32)
});
Ok(())
},
}
SlideType::Video => {
let video = video_model::get_video(item_model_id)?;
self.insert_slide(&Slide::slide_from_video(video)?, index);
self.insert_slide(
&Slide::slide_from_video(video)?,
index,
);
Ok(())
},
}
SlideType::Image => {
let result = image_model::get_image(item_model_id);
match result {
Ok(image) => self.insert_slide(&Slide::slide_from_image(image)?, index),
Ok(image) => self.insert_slide(
&Slide::slide_from_image(image)?,
index,
),
Err(e) => {
e.with_note(|| {
format!("This might fail if we are loading the items from a file")
});
let mut slide = Slide::default();
slide.image_background = "qrc:/assets/black.jpg".to_owned();
slide.image_background =
"qrc:/assets/black.jpg".to_owned();
self.insert_slide(&slide, index);
},
}
}
Ok(())
},
}
SlideType::Content => {
todo!();
Ok(())
},
}
SlideType::Presentation(_) => {
let presentation = presentation_model::get_presentation(item_model_id)?;
let slides = Slide::slides_from_presentation(presentation)?;
slides.iter().for_each(|slide| self.as_mut().insert_slide(slide, slide.slide_index + index));
let presentation =
presentation_model::get_presentation(
item_model_id,
)?;
let slides =
Slide::slides_from_presentation(presentation)?;
slides.iter().for_each(|slide| {
self.as_mut().insert_slide(
slide,
slide.slide_index + index,
)
});
Ok(())
}
}
@ -656,7 +706,7 @@ impl slide_model::SlideModel {
self: Pin<&mut Self>,
item_model_id: i32,
kind: &QString,
) -> Result<()>{
) -> Result<()> {
let index = self.count;
self.insert_item_from_service(index, item_model_id, kind)
}
@ -692,7 +742,8 @@ impl slide_model::SlideModel {
if let Some((i, slide)) = slides_iter
.clone()
.enumerate().find(|slide| slide.1.service_item_id == source_index)
.enumerate()
.find(|slide| slide.1.service_item_id == source_index)
{
debug!(index = i, ?slide);
first_slide = i as i32;
@ -705,11 +756,8 @@ impl slide_model::SlideModel {
// lets get the dest_slide and count
if move_down {
if let Some((i, slide)) = slides_iter
.clone()
.enumerate()
.rev()
.find(|slide| {
if let Some((i, slide)) =
slides_iter.clone().enumerate().rev().find(|slide| {
slide.1.service_item_id == destination_index
})
{
@ -724,9 +772,8 @@ impl slide_model::SlideModel {
dest_slide, dest_count
);
}
} else if let Some((i, slide)) = slides_iter
.enumerate()
.find(|slide| {
} else if let Some((i, slide)) =
slides_iter.enumerate().find(|slide| {
slide.1.service_item_id == destination_index
})
{
@ -773,7 +820,9 @@ impl slide_model::SlideModel {
// Change the service_item_id of the moved slide
if count > 1 {
if move_down {
debug!("While moving down, change service items id of moved slide");
debug!(
"While moving down, change service items id of moved slide"
);
for (i, _slide) in slides_iter
.clone()
.enumerate()
@ -791,13 +840,16 @@ impl slide_model::SlideModel {
debug!(
?slide,
"rust: these ones right here officer. from {:?} to {:?}",
slide.service_item_id, destination_index
slide.service_item_id,
destination_index
);
slide.service_item_id = destination_index;
}
}
} else {
debug!("While moving up, change service items id of moved slide");
debug!(
"While moving up, change service items id of moved slide"
);
for (i, _slide) in slides_iter
.clone()
.enumerate()
@ -810,7 +862,8 @@ impl slide_model::SlideModel {
debug!(
?slide,
"rust: these ones right here officer. from {:?} to {:?}",
slide.service_item_id, destination_index
slide.service_item_id,
destination_index
);
slide.service_item_id = destination_index;
}
@ -882,7 +935,9 @@ impl slide_model::SlideModel {
// self.as_mut().end_reset_model();
// }
debug!("rust-move: {first_slide} to {dest_slide} with {count} slides");
debug!(
"rust-move: {first_slide} to {dest_slide} with {count} slides"
);
}
fn move_items(
@ -1001,7 +1056,8 @@ impl slide_model::SlideModel {
debug!(service_item = index, "Getting slide from this item");
let mut id = 0;
if let Some((i, slide)) = slides_iter
.enumerate().find(|(_i, slide)| slide.service_item_id == index)
.enumerate()
.find(|(_i, slide)| slide.service_item_id == index)
{
debug!(slide_id = i, ?slide);
id = i as i32;
@ -1105,7 +1161,6 @@ impl slide_model::SlideModel {
(0, QModelIndex::default(), vector_roles)
}
}
}
// QAbstractListModel implementation
@ -1114,22 +1169,30 @@ impl slide_model::SlideModel {
let role = SlideRoles { repr: role };
if let Some(slide) = self.slides.get(index.row() as usize) {
return match role {
SlideRoles::Ty => QVariant::from(&QString::from(&slide.ty.to_string())),
SlideRoles::Text => QVariant::from(&QString::from(&slide.text)),
SlideRoles::Audio => QVariant::from(&QString::from(&slide.audio)),
SlideRoles::ImageBackground => {
QVariant::from(&QString::from(&slide.image_background))
SlideRoles::Ty => QVariant::from(&QString::from(
&slide.ty.to_string(),
)),
SlideRoles::Text => {
QVariant::from(&QString::from(&slide.text))
}
SlideRoles::VideoBackground => {
QVariant::from(&QString::from(&slide.video_background))
SlideRoles::Audio => {
QVariant::from(&QString::from(&slide.audio))
}
SlideRoles::HTextAlignment => {
QVariant::from(&QString::from(&slide.htext_alignment))
SlideRoles::ImageBackground => QVariant::from(
&QString::from(&slide.image_background),
),
SlideRoles::VideoBackground => QVariant::from(
&QString::from(&slide.video_background),
),
SlideRoles::HTextAlignment => QVariant::from(
&QString::from(&slide.htext_alignment),
),
SlideRoles::VTextAlignment => QVariant::from(
&QString::from(&slide.vtext_alignment),
),
SlideRoles::Font => {
QVariant::from(&QString::from(&slide.font))
}
SlideRoles::VTextAlignment => {
QVariant::from(&QString::from(&slide.vtext_alignment))
}
SlideRoles::Font => QVariant::from(&QString::from(&slide.font)),
SlideRoles::FontSize => {
QVariant::from(&slide.font_size)
}
@ -1147,9 +1210,9 @@ impl slide_model::SlideModel {
QVariant::from(&slide.selected)
}
SlideRoles::Looping => QVariant::from(&slide.looping),
SlideRoles::VideoThumbnail => {
QVariant::from(&QString::from(&slide.video_thumbnail))
}
SlideRoles::VideoThumbnail => QVariant::from(
&QString::from(&slide.video_thumbnail),
),
SlideRoles::VideoStartTime => {
QVariant::from(&slide.video_start_time)
}
@ -1256,7 +1319,8 @@ impl slide_model::SlideModel {
fn extract_string(item: &QMap_QString_QVariant, key: &str) -> String {
item.get(&QString::from(key))
.unwrap_or(QVariant::from(&QString::default()))
.value_or_default::<QString>().to_string()
.value_or_default::<QString>()
.to_string()
}
fn extract_value(item: &QMap_QString_QVariant, key: &str) -> i32 {
@ -1277,12 +1341,11 @@ fn extract_bool(item: &QMap_QString_QVariant, key: &str) -> bool {
.value_or_default::<bool>()
}
#[cfg(test)]
mod test {
// #[cfg(test)]
// mod test {
#[test]
pub fn test_obs_setting_scene() {
assert_eq!(true, true)
}
}
// #[test]
// pub fn test_obs_setting_scene() {
// assert_eq!(true, true)
// }
// }

View file

@ -1,5 +1,5 @@
#[cxx_qt::bridge]
pub mod slide_object {
pub mod qobject {
unsafe extern "C++" {
include!("cxx-qt-lib/qstring.h");
type QString = cxx_qt_lib::QString;
@ -10,7 +10,31 @@ pub mod slide_object {
// type SlideModel = crate::slide_model::slide_model::SlideModel;
}
#[auto_cxx_name]
#[auto_rust_name]
unsafe extern "RustQt" {
#[qobject]
#[qml_element]
#[qproperty(i32, slide_index)]
#[qproperty(i32, slide_size)]
#[qproperty(i32, inner_slide_index)]
#[qproperty(bool, is_playing)]
#[qproperty(bool, looping)]
#[qproperty(QString, text)]
#[qproperty(QString, ty)]
#[qproperty(QString, audio)]
#[qproperty(QString, image_background)]
#[qproperty(QString, video_background)]
#[qproperty(bool, html)]
#[qproperty(QString, vtext_alignment)]
#[qproperty(QString, htext_alignment)]
#[qproperty(QString, font)]
#[qproperty(i32, font_size)]
#[qproperty(f32, video_start_time)]
#[qproperty(f32, video_end_time)]
// #[qproperty(*mut SlideModel, slide_model)]
type SlideObject = super::SlideObjectRust;
#[qsignal]
fn playing_changed(
self: Pin<&mut SlideObject>,
@ -35,28 +59,6 @@ pub mod slide_object {
#[qsignal]
fn reveal_prev(self: Pin<&mut SlideObject>);
#[qobject]
#[qml_element]
#[qproperty(i32, slide_index)]
#[qproperty(i32, slide_size)]
#[qproperty(i32, inner_slide_index)]
#[qproperty(bool, is_playing)]
#[qproperty(bool, looping)]
#[qproperty(QString, text)]
#[qproperty(QString, ty)]
#[qproperty(QString, audio)]
#[qproperty(QString, image_background)]
#[qproperty(QString, video_background)]
#[qproperty(bool, html)]
#[qproperty(QString, vtext_alignment)]
#[qproperty(QString, htext_alignment)]
#[qproperty(QString, font)]
#[qproperty(i32, font_size)]
#[qproperty(f32, video_start_time)]
#[qproperty(f32, video_end_time)]
// #[qproperty(*mut SlideModel, slide_model)]
type SlideObject = super::SlideObjectRust;
#[qinvokable]
fn change_slide(
self: Pin<&mut SlideObject>,
@ -89,10 +91,11 @@ use cxx_qt_lib::{CaseSensitivity, QString, QVariant};
use tracing::{debug, error};
use crate::{
slide_model::{slide_model, Slide}, slide_types::SlideType
slide_model::{Slide, slide_model},
slide_types::SlideType,
};
use self::slide_object::QMap_QString_QVariant;
use self::qobject::QMap_QString_QVariant;
#[derive(Clone, Debug)]
pub struct SlideObjectRust {
@ -141,7 +144,7 @@ impl Default for SlideObjectRust {
}
}
impl slide_object::SlideObject {
impl qobject::SlideObject {
pub fn change_slide(
mut self: Pin<&mut Self>,
item: QMap_QString_QVariant,
@ -484,10 +487,18 @@ impl slide_object::SlideObject {
self.as_mut().set_text(QString::from(&slide.text));
self.as_mut().set_ty(QString::from(&slide.ty.to_string()));
self.as_mut().set_audio(QString::from(&slide.audio));
self.as_mut().set_image_background(QString::from(&slide.image_background));
self.as_mut().set_video_background(QString::from(&slide.video_background));
self.as_mut().set_vtext_alignment(QString::from(&slide.vtext_alignment));
self.as_mut().set_htext_alignment(QString::from(&slide.htext_alignment));
self.as_mut().set_image_background(QString::from(
&slide.image_background,
));
self.as_mut().set_video_background(QString::from(
&slide.video_background,
));
self.as_mut().set_vtext_alignment(QString::from(
&slide.vtext_alignment,
));
self.as_mut().set_htext_alignment(QString::from(
&slide.htext_alignment,
));
self.as_mut().set_font(QString::from(&slide.font));
self.as_mut().set_font_size(slide.font_size);
self.as_mut().set_video_start_time(slide.video_start_time);

View file

@ -15,11 +15,13 @@ pub mod song_editor {
include!("cxx-qt-lib/qlist.h");
type QList_QString = cxx_qt_lib::QList<QString>;
include!("cxx-qt-gen/song_model.cxxqt.h");
include!("liblumina/src/rust/songs/song_model.cxxqt.h");
type SongModel =
crate::songs::song_model::song_model::SongModel;
}
#[auto_cxx_name]
#[auto_rust_name]
unsafe extern "RustQt" {
#[qobject]
#[qml_element]

View file

@ -2,6 +2,7 @@
pub mod song_model {
unsafe extern "C++" {
include!(< QAbstractListModel >);
type QAbstractListModel;
include!("cxx-qt-lib/qhash.h");
type QHash_i32_QByteArray =
cxx_qt_lib::QHash<cxx_qt_lib::QHashPair_i32_QByteArray>;
@ -39,15 +40,18 @@ pub mod song_model {
FontSize,
}
#[auto_cxx_name]
#[auto_rust_name]
unsafe extern "RustQt" {
#[qobject]
#[base = "QAbstractListModel"]
#[base = QAbstractListModel]
#[qml_element]
#[qproperty(i32, count)]
type SongModel = super::SongModelRust;
#[inherit]
#[qsignal]
#[cxx_name = "dataChanged"]
fn data_changed(
self: Pin<&mut SongModel>,
top_left: &QModelIndex,
@ -75,7 +79,7 @@ pub mod song_model {
fn setup(self: Pin<&mut SongModel>);
#[qinvokable]
fn remove_item(self: Pin<&mut SongModel>, index: i32)
-> bool;
-> bool;
#[qinvokable]
fn new_song(self: Pin<&mut SongModel>) -> bool;
#[qinvokable]
@ -166,8 +170,11 @@ pub mod song_model {
impl cxx_qt::Threading for SongModel {}
#[auto_cxx_name]
#[auto_rust_name]
unsafe extern "RustQt" {
#[inherit]
#[cxx_name = "beginInsertRows"]
unsafe fn begin_insert_rows(
self: Pin<&mut SongModel>,
parent: &QModelIndex,
@ -176,9 +183,11 @@ pub mod song_model {
);
#[inherit]
#[cxx_name = "endInsertRows"]
unsafe fn end_insert_rows(self: Pin<&mut SongModel>);
#[inherit]
#[cxx_name = "beginRemoveRows"]
unsafe fn begin_remove_rows(
self: Pin<&mut SongModel>,
parent: &QModelIndex,
@ -187,6 +196,7 @@ pub mod song_model {
);
#[inherit]
#[cxx_name = "beginMoveRows"]
unsafe fn begin_move_rows(
self: Pin<&mut SongModel>,
source_parent: &QModelIndex,
@ -197,18 +207,23 @@ pub mod song_model {
) -> bool;
#[inherit]
#[cxx_name = "endMoveRows"]
unsafe fn end_move_rows(self: Pin<&mut SongModel>);
#[inherit]
#[cxx_name = "endRemoveRows"]
unsafe fn end_remove_rows(self: Pin<&mut SongModel>);
#[inherit]
#[cxx_name = "beginResetModel"]
unsafe fn begin_reset_model(self: Pin<&mut SongModel>);
#[inherit]
#[cxx_name = "endResetModel"]
unsafe fn end_reset_model(self: Pin<&mut SongModel>);
#[inherit]
#[cxx_name = "canFetchMore"]
fn can_fetch_more(
self: &SongModel,
parent: &QModelIndex,
@ -232,24 +247,26 @@ pub mod song_model {
#[qinvokable]
#[cxx_override]
#[cxx_name = "roleNames"]
fn role_names(self: &SongModel) -> QHash_i32_QByteArray;
#[qinvokable]
#[cxx_override]
#[cxx_name = "rowCount"]
fn row_count(self: &SongModel, _parent: &QModelIndex) -> i32;
}
}
use crate::songs::song_editor::song_editor::QList_QString;
use color_eyre::Result;
use cxx_qt::{CxxQtType, Threading};
use cxx_qt_lib::{
QByteArray, QModelIndex, QString, QStringList, QVariant,
};
use sqlx::{query, query_as, Connection, SqliteConnection};
use sqlx::{Connection, SqliteConnection, query, query_as};
use std::collections::HashMap;
use std::pin::Pin;
use tracing::{debug, error};
use color_eyre::Result;
use self::song_model::{
QHash_i32_QByteArray, QMap_QString_QVariant, QVector_i32,
@ -288,90 +305,86 @@ impl Default for Song {
impl Song {
pub fn get_lyric_list(&self) -> QList_QString {
let mut lyric_list = QList_QString::default();
if self.lyrics.is_empty() {
return QList_QString::default();
}
let raw_lyrics = self.lyrics.clone();
println!("raw-lyrics: {:?}", raw_lyrics);
let vorder: Vec<&str> =
self.verse_order.split(' ').collect();
let keywords = vec![
"Verse 1", "Verse 2", "Verse 3", "Verse 4",
"Verse 5", "Verse 6", "Verse 7", "Verse 8",
"Chorus 1", "Chorus 2", "Chorus 3", "Chorus 4",
"Bridge 1", "Bridge 2", "Bridge 3", "Bridge 4",
"Intro 1", "Intro 2", "Ending 1", "Ending 2",
"Other 1", "Other 2", "Other 3", "Other 4",
];
let _first_item = true;
if self.lyrics.is_empty() {
return QList_QString::default();
}
let raw_lyrics = self.lyrics.clone();
println!("raw-lyrics: {:?}", raw_lyrics);
let vorder: Vec<&str> = self.verse_order.split(' ').collect();
let keywords = vec![
"Verse 1", "Verse 2", "Verse 3", "Verse 4", "Verse 5",
"Verse 6", "Verse 7", "Verse 8", "Chorus 1", "Chorus 2",
"Chorus 3", "Chorus 4", "Bridge 1", "Bridge 2",
"Bridge 3", "Bridge 4", "Intro 1", "Intro 2", "Ending 1",
"Ending 2", "Other 1", "Other 2", "Other 3", "Other 4",
];
let _first_item = true;
let mut lyric_map = HashMap::new();
let mut verse_title = String::from("");
let mut lyric = String::from("");
for (i, line) in raw_lyrics.split('\n').enumerate() {
if keywords.contains(&line) {
if i != 0 {
// println!("{verse_title}");
// println!("{lyric}");
lyric_map.insert(verse_title, lyric);
lyric = String::from("");
verse_title = line.to_string();
// println!("{line}");
// println!("\n");
} else {
verse_title = line.to_string();
// println!("{line}");
// println!("\n");
}
let mut lyric_map = HashMap::new();
let mut verse_title = String::from("");
let mut lyric = String::from("");
for (i, line) in raw_lyrics.split('\n').enumerate() {
if keywords.contains(&line) {
if i != 0 {
// println!("{verse_title}");
// println!("{lyric}");
lyric_map.insert(verse_title, lyric);
lyric = String::from("");
verse_title = line.to_string();
// println!("{line}");
// println!("\n");
} else {
lyric.push_str(line);
lyric.push('\n');
verse_title = line.to_string();
// println!("{line}");
// println!("\n");
}
} else {
lyric.push_str(line);
lyric.push('\n');
}
}
lyric_map.insert(verse_title, lyric);
// println!("da-map: {:?}", lyric_map);
for verse in vorder {
let mut verse_name = "";
// debug!(verse = verse);
for word in keywords.clone() {
let end_verse = verse.get(1..2).unwrap_or_default();
let beg_verse = verse.get(0..1).unwrap_or_default();
// println!(
// "verse: {:?}, beginning: {:?}, end: {:?}, word: {:?}",
// verse, beg_verse, end_verse, word
// );
if word.starts_with(beg_verse)
&& word.ends_with(end_verse)
{
verse_name = word;
// println!("TITLE: {verse_name}");
continue;
}
}
lyric_map.insert(verse_title, lyric);
// println!("da-map: {:?}", lyric_map);
for verse in vorder {
let mut verse_name = "";
// debug!(verse = verse);
for word in keywords.clone() {
let end_verse =
verse.get(1..2).unwrap_or_default();
let beg_verse =
verse.get(0..1).unwrap_or_default();
// println!(
// "verse: {:?}, beginning: {:?}, end: {:?}, word: {:?}",
// verse, beg_verse, end_verse, word
// );
if word.starts_with(beg_verse)
&& word.ends_with(end_verse)
{
verse_name = word;
// println!("TITLE: {verse_name}");
continue;
}
}
if let Some(lyric) = lyric_map.get(verse_name) {
if lyric.contains("\n\n") {
let split_lyrics: Vec<&str> =
lyric.split("\n\n").collect();
for lyric in split_lyrics {
if lyric.is_empty() {
continue;
}
lyric_list.append(QString::from(lyric));
if let Some(lyric) = lyric_map.get(verse_name) {
if lyric.contains("\n\n") {
let split_lyrics: Vec<&str> =
lyric.split("\n\n").collect();
for lyric in split_lyrics {
if lyric.is_empty() {
continue;
}
continue;
lyric_list.append(QString::from(lyric));
}
lyric_list.append(QString::from(lyric));
} else {
println!("NOT WORKING!");
};
}
for lyric in lyric_list.iter() {
// println!("da-list: {:?}", lyric);
debug!(lyric = ?lyric)
}
continue;
}
lyric_list.append(QString::from(lyric));
} else {
println!("NOT WORKING!");
};
}
for lyric in lyric_list.iter() {
// println!("da-list: {:?}", lyric);
debug!(lyric = ?lyric)
}
lyric_list
}
}
@ -400,9 +413,11 @@ impl Default for SongModelRust {
let mut db_url = String::from("sqlite://");
db_url.push_str(data.to_str().unwrap());
rt.block_on(async {
SqliteConnection::connect(&db_url).await.expect("problems")
SqliteConnection::connect(&db_url)
.await
.expect("problems")
})
}
},
}
}
}
@ -416,12 +431,14 @@ pub fn get_song(id: i32) -> Result<Song> {
let mut db_url = String::from("sqlite://");
db_url.push_str(data.to_str().unwrap());
rt.block_on(async {
SqliteConnection::connect(&db_url).await.expect("problems")
SqliteConnection::connect(&db_url)
.await
.expect("problems")
})
};
debug!("getting song with id: {id}");
rt.block_on(async {
let result = query_as!(Song, r#"SELECT vorder as "verse_order!", fontSize as "font_size!: i32", backgroundType as "background_type!", horizontalTextAlignment as "horizontal_text_alignment!", verticalTextAlignment 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 where id = ?"#, id).fetch_one(&mut db).await?;
let result = query_as!(Song, 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 where id = $1"#, id).fetch_one(&mut db).await?;
debug!(?result);
Ok(result)
})
@ -440,7 +457,7 @@ impl song_model::SongModel {
static DATABASE_URL: &str = "sqlite:///home/chris/.local/share/lumina/library-db.sqlite3";
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let result = query_as!(Song, r#"SELECT vorder as "verse_order!", fontSize as "font_size!: i32", backgroundType as "background_type!", horizontalTextAlignment as "horizontal_text_alignment!", verticalTextAlignment 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(&mut self.as_mut().rust_mut().db).await;
let result = query_as!(Song, 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(&mut self.as_mut().rust_mut().db).await;
match result {
Ok(s) => s.into_iter().for_each(|s| self.as_mut().add_song(s)),
Err(e) => error!("There was an error in converting songs: {e}"),
@ -453,7 +470,13 @@ impl song_model::SongModel {
return false;
}
let song_id = self.as_mut().rust_mut().songs.get(index as usize).unwrap().id;
let song_id = self
.as_mut()
.rust_mut()
.songs
.get(index as usize)
.unwrap()
.id;
let thread = self.qt_thread();
let db = &mut self.as_mut().rust_mut().db;
@ -504,7 +527,7 @@ impl song_model::SongModel {
};
rt.block_on(async {
let result = query!(r#"INSERT into songs (vorder, fontSize, backgroundType, horizontalTextAlignment, verticalTextAlignment, title, font, background, lyrics, ccli, author, audio, id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"#,
let result = query!(r#"INSERT into songs (verse_order, font_size, background_type, horizontal_text_alignment, vertical_text_alignment, title, font, background, lyrics, ccli, author, audio, id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"#,
song.verse_order,
song.font_size,
song.background_type,
@ -519,7 +542,7 @@ impl song_model::SongModel {
song.audio,
song.id)
.fetch_all(&mut self.as_mut().rust_mut().db).await;
match result {
Ok(_i) => {
self.as_mut().add_song(song);
@ -586,16 +609,23 @@ impl song_model::SongModel {
let rt = tokio::runtime::Runtime::new().unwrap();
let db_string = updated_title.clone().to_string();
rt.block_on(async {
let result = query!("UPDATE songs SET title = ? where id = ?", db_string, song_id)
.execute(&mut self.as_mut().rust_mut().db)
.await;
let result = query!(
"UPDATE songs SET title = ? where id = ?",
db_string,
song_id
)
.execute(&mut self.as_mut().rust_mut().db)
.await;
match result {
Ok(_i) => {
if let Some(song) =
self.as_mut().rust_mut().songs.get_mut(index)
{
debug!(?song, title = updated_title.to_string());
debug!(
?song,
title = updated_title.to_string()
);
song.title = updated_title.to_string();
self.as_mut().data_changed(
&model_index,
@ -608,9 +638,11 @@ impl song_model::SongModel {
}
}
Err(e) => {
error!("There was an error updating title in db: {e}");
error!(
"There was an error updating title in db: {e}"
);
false
},
}
}
});
true
@ -714,9 +746,13 @@ impl song_model::SongModel {
let db_string = updated_audio.clone().to_string();
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let result = query!("UPDATE songs SET audio = ? where id = ?", db_string, song_id)
.execute(&mut self.as_mut().rust_mut().db)
.await;
let result = query!(
"UPDATE songs SET audio = ? where id = ?",
db_string,
song_id
)
.execute(&mut self.as_mut().rust_mut().db)
.await;
match result {
Ok(_i) => {
@ -739,9 +775,11 @@ impl song_model::SongModel {
}
}
Err(e) => {
error!("There was an error updating audio in db: {e}");
error!(
"There was an error updating audio in db: {e}"
);
false
},
}
}
});
true
@ -758,9 +796,13 @@ impl song_model::SongModel {
let db_string = updated_ccli.clone().to_string();
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let result = query!("UPDATE songs SET ccli = ? where id = ?", db_string, song_id)
.execute(&mut self.as_mut().rust_mut().db)
.await;
let result = query!(
"UPDATE songs SET ccli = ? where id = ?",
db_string,
song_id
)
.execute(&mut self.as_mut().rust_mut().db)
.await;
match result {
Ok(_i) => {
@ -783,9 +825,11 @@ impl song_model::SongModel {
}
}
Err(e) => {
error!("There was an error updating ccli in db: {e}");
error!(
"There was an error updating ccli in db: {e}"
);
false
},
}
}
});
true
@ -802,7 +846,7 @@ impl song_model::SongModel {
let db_string = updated_verse_order.clone().to_string();
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let result = query!("UPDATE songs SET vorder = ? where id = ?", db_string, song_id)
let result = query!("UPDATE songs SET verse_order = ? where id = ?", db_string, song_id)
.execute(&mut self.as_mut().rust_mut().db)
.await;
@ -891,7 +935,7 @@ impl song_model::SongModel {
let db_string = updated_background_type.clone().to_string();
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let result = query!("UPDATE songs SET backgroundType = ? where id = ?", db_string, song_id)
let result = query!("UPDATE songs SET background_type = ? where id = ?", db_string, song_id)
.execute(&mut self.as_mut().rust_mut().db)
.await;
@ -933,10 +977,11 @@ impl song_model::SongModel {
.as_mut()
.get_indices(song_id, SongRoles::HorizontalTextAlignment);
let db_string = updated_horizontal_text_alignment.clone().to_string();
let db_string =
updated_horizontal_text_alignment.clone().to_string();
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let result = query!("UPDATE songs SET horizontalTextAlignment = ? where id = ?", db_string, song_id)
let result = query!("UPDATE songs SET horizontal_text_alignment = ? where id = ?", db_string, song_id)
.execute(&mut self.as_mut().rust_mut().db)
.await;
@ -978,10 +1023,11 @@ impl song_model::SongModel {
.as_mut()
.get_indices(song_id, SongRoles::VerticalTextAlignment);
let db_string = updated_vertical_text_alignment.clone().to_string();
let db_string =
updated_vertical_text_alignment.clone().to_string();
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let result = query!("UPDATE songs SET verticalTextAlignment = ? where id = ?", db_string, song_id)
let result = query!("UPDATE songs SET vertical_text_alignment = ? where id = ?", db_string, song_id)
.execute(&mut self.as_mut().rust_mut().db)
.await;
@ -1025,9 +1071,13 @@ impl song_model::SongModel {
let db_string = updated_font.clone().to_string();
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let result = query!("UPDATE songs SET font = ? where id = ?", db_string, song_id)
.execute(&mut self.as_mut().rust_mut().db)
.await;
let result = query!(
"UPDATE songs SET font = ? where id = ?",
db_string,
song_id
)
.execute(&mut self.as_mut().rust_mut().db)
.await;
match result {
Ok(_i) => {
@ -1050,9 +1100,11 @@ impl song_model::SongModel {
}
}
Err(e) => {
error!("There was an error updating font in db: {e}");
error!(
"There was an error updating font in db: {e}"
);
false
},
}
}
});
true
@ -1069,7 +1121,7 @@ impl song_model::SongModel {
let db_string = updated_font_size.clone().to_string();
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let result = query!("UPDATE songs SET fontSize = ? where id = ?", db_string, song_id)
let result = query!("UPDATE songs SET font_size = ? where id = ?", db_string, song_id)
.execute(&mut self.as_mut().rust_mut().db)
.await;

View file

@ -2,6 +2,7 @@
pub mod video_model {
unsafe extern "C++" {
include!(< QAbstractListModel >);
type QAbstractListModel;
include!("cxx-qt-lib/qhash.h");
type QHash_i32_QByteArray =
cxx_qt_lib::QHash<cxx_qt_lib::QHashPair_i32_QByteArray>;
@ -34,15 +35,18 @@ pub mod video_model {
Looping,
}
#[auto_cxx_name]
#[auto_rust_name]
unsafe extern "RustQt" {
#[qobject]
#[base = "QAbstractListModel"]
#[base = QAbstractListModel]
#[qml_element]
#[qproperty(i32, count)]
type VideoModel = super::VideoModelRust;
#[inherit]
#[qsignal]
#[cxx_name = "dataChanged"]
fn data_changed(
self: Pin<&mut VideoModel>,
top_left: &QModelIndex,
@ -102,8 +106,11 @@ pub mod video_model {
impl cxx_qt::Threading for VideoModel {}
#[auto_cxx_name]
#[auto_rust_name]
unsafe extern "RustQt" {
#[inherit]
#[cxx_name = "beginInsertRows"]
unsafe fn begin_insert_rows(
self: Pin<&mut VideoModel>,
parent: &QModelIndex,
@ -112,9 +119,11 @@ pub mod video_model {
);
#[inherit]
#[cxx_name = "endInsertRows"]
unsafe fn end_insert_rows(self: Pin<&mut VideoModel>);
#[inherit]
#[cxx_name = "beginRemoveRows"]
unsafe fn begin_remove_rows(
self: Pin<&mut VideoModel>,
parent: &QModelIndex,
@ -123,6 +132,7 @@ pub mod video_model {
);
#[inherit]
#[cxx_name = "beginMoveRows"]
unsafe fn begin_move_rows(
self: Pin<&mut VideoModel>,
source_parent: &QModelIndex,
@ -133,18 +143,23 @@ pub mod video_model {
) -> bool;
#[inherit]
#[cxx_name = "endMoveRows"]
unsafe fn end_move_rows(self: Pin<&mut VideoModel>);
#[inherit]
#[cxx_name = "endRemoveRows"]
unsafe fn end_remove_rows(self: Pin<&mut VideoModel>);
#[inherit]
#[cxx_name = "beginResetModel"]
unsafe fn begin_reset_model(self: Pin<&mut VideoModel>);
#[inherit]
#[cxx_name = "endResetModel"]
unsafe fn end_reset_model(self: Pin<&mut VideoModel>);
#[inherit]
#[cxx_name = "canFetchMore"]
fn can_fetch_more(
self: &VideoModel,
parent: &QModelIndex,
@ -168,19 +183,22 @@ pub mod video_model {
#[qinvokable]
#[cxx_override]
#[cxx_name = "roleNames"]
fn role_names(self: &VideoModel) -> QHash_i32_QByteArray;
#[qinvokable]
#[cxx_override]
#[cxx_name = "rowCount"]
fn row_count(self: &VideoModel, _parent: &QModelIndex)
-> i32;
-> i32;
}
}
use color_eyre::eyre::Error;
use cxx_qt::{CxxQtType, Threading};
use cxx_qt_lib::{QByteArray, QModelIndex, QString, QUrl, QVariant};
use sqlx::{query, query_as, Connection, SqliteConnection};
use sqlx::{Connection, SqliteConnection, query, query_as};
use std::path::PathBuf;
use std::pin::Pin;
use tracing::{debug, error};
@ -224,9 +242,11 @@ impl Default for VideoModelRust {
let mut db_url = String::from("sqlite://");
db_url.push_str(data.to_str().unwrap());
rt.block_on(async {
SqliteConnection::connect(&db_url).await.expect("problems")
SqliteConnection::connect(&db_url)
.await
.expect("problems")
})
}
},
}
}
}
@ -240,12 +260,20 @@ pub fn get_video(index: i32) -> color_eyre::Result<Video> {
let mut db_url = String::from("sqlite://");
db_url.push_str(data.to_str().unwrap());
rt.block_on(async {
SqliteConnection::connect(&db_url).await.expect("problems")
SqliteConnection::connect(&db_url)
.await
.expect("problems")
})
};
rt.block_on(async {
let result = query_as!(Video, r#"SELECT id as "id: i32", title as "title!", filePath as "path!", startTime as "start_time!: f32", endTime as "end_time!: f32", loop as "looping!" from videos where id = ?"#, index).fetch_one(&mut db).await?;
Ok(result)
let result = query_as!(Video, r#"SELECT id as "id: i32", title as "title!", file_path as "path!", start_time as "start_time!: f32", end_time as "end_time!: f32", loop as "looping!" from videos where id = ?"#, index).fetch_one(&mut db).await;
match result {
Ok(v) => Ok(v),
Err(e) => {
error!(?e);
Err(color_eyre::Report::from(e))
},
}
})
}
@ -261,7 +289,7 @@ impl video_model::VideoModel {
pub fn setup(mut self: Pin<&mut Self>) {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let result = query_as!(Video, r#"SELECT id as "id: i32", title as "title!", filePath as "path!", startTime as "start_time!: f32", endTime as "end_time!: f32", loop as "looping!" from videos"#).fetch_all(&mut self.as_mut().rust_mut().db).await;
let result = query_as!(Video, r#"SELECT id as "id: i32", title as "title!", file_path as "path!", start_time as "start_time!: f32", end_time as "end_time!: f32", loop as "looping!" from videos"#).fetch_all(&mut self.as_mut().rust_mut().db).await;
match result {
Ok(v) => v.into_iter().for_each(|v| self.as_mut().add_video(v)),
Err(e) => error!("There was an error in converting songs: {e}"),
@ -277,7 +305,10 @@ impl video_model::VideoModel {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let result = query!("delete from videos where id = ?", video_id).execute(&mut self.as_mut().rust_mut().db).await;
let result =
query!("delete from videos where id = ?", video_id)
.execute(&mut self.as_mut().rust_mut().db)
.await;
match result {
Ok(_i) => {
unsafe {
@ -334,7 +365,7 @@ impl video_model::VideoModel {
rt.block_on(async {
let video_title = video_title.to_string();
let video_path = video_path.to_string();
let result = query!(r#"INSERT into videos (id, title, filePath, startTime, endTime, loop) VALUES (?, ?, ?, ?, ?, ?)"#,
let result = query!(r#"INSERT into videos (id, title, file_path, start_time, end_time, loop) VALUES (?, ?, ?, ?, ?, ?)"#,
video_id,
video_title,
video_path,
@ -435,9 +466,13 @@ impl video_model::VideoModel {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let result = query!("UPDATE videos SET loop = ? where id = ?", loop_value, index)
.execute(&mut self.as_mut().rust_mut().db)
.await;
let result = query!(
"UPDATE videos SET loop = ? where id = ?",
loop_value,
index
)
.execute(&mut self.as_mut().rust_mut().db)
.await;
match result {
Ok(_i) => {
for video in self
@ -448,9 +483,11 @@ impl video_model::VideoModel {
.filter(|x| x.id == index)
{
video.looping = loop_value;
debug!(title = video.title,
looping = loop_value,
"updated video loop");
debug!(
title = video.title,
looping = loop_value,
"updated video loop"
);
}
self.as_mut().data_changed(
model_index,
@ -462,7 +499,7 @@ impl video_model::VideoModel {
Err(e) => {
error!("Error connecting to db: {e}");
false
},
}
}
});
true
@ -481,9 +518,13 @@ impl video_model::VideoModel {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let result = query!("UPDATE videos SET endTime = ? where id = ?", updated_end_time, index)
.execute(&mut self.as_mut().rust_mut().db)
.await;
let result = query!(
"UPDATE videos SET end_time = ? where id = ?",
updated_end_time,
index
)
.execute(&mut self.as_mut().rust_mut().db)
.await;
match result {
Ok(_i) => {
for video in self
@ -494,9 +535,11 @@ impl video_model::VideoModel {
.filter(|x| x.id == index)
{
video.end_time = updated_end_time;
debug!(title = video.title,
end_time = updated_end_time,
"updated video end_time");
debug!(
title = video.title,
end_time = updated_end_time,
"updated video end_time"
);
}
self.as_mut().data_changed(
model_index,
@ -508,7 +551,7 @@ impl video_model::VideoModel {
Err(e) => {
error!("Error connecting to db: {e}");
false
},
}
}
});
true
@ -527,9 +570,13 @@ impl video_model::VideoModel {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let result = query!("UPDATE videos SET startTime = ? where id = ?", updated_start_time, index)
.execute(&mut self.as_mut().rust_mut().db)
.await;
let result = query!(
"UPDATE videos SET start_time = ? where id = ?",
updated_start_time,
index
)
.execute(&mut self.as_mut().rust_mut().db)
.await;
match result {
Ok(_i) => {
for video in self
@ -540,9 +587,11 @@ impl video_model::VideoModel {
.filter(|x| x.id == index)
{
video.start_time = updated_start_time;
debug!(title = video.title,
start_time = updated_start_time,
"updated video start_time");
debug!(
title = video.title,
start_time = updated_start_time,
"updated video start_time"
);
}
self.as_mut().data_changed(
model_index,
@ -554,7 +603,7 @@ impl video_model::VideoModel {
Err(e) => {
error!("Error connecting to db: {e}");
false
},
}
}
});
true
@ -574,9 +623,13 @@ impl video_model::VideoModel {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let title = updated_title.to_string();
let result = query!("UPDATE videos SET title = ? where id = ?", title, index)
.execute(&mut self.as_mut().rust_mut().db)
.await;
let result = query!(
"UPDATE videos SET title = ? where id = ?",
title,
index
)
.execute(&mut self.as_mut().rust_mut().db)
.await;
match result {
Ok(_i) => {
for video in self
@ -587,9 +640,11 @@ impl video_model::VideoModel {
.filter(|x| x.id == index)
{
video.title = title.clone();
debug!(title = video.title,
title = title,
"updated video title");
debug!(
title = video.title,
title = title,
"updated video title"
);
}
self.as_mut().data_changed(
model_index,
@ -601,7 +656,7 @@ impl video_model::VideoModel {
Err(e) => {
error!("Error connecting to db: {e}");
false
},
}
}
});
true
@ -620,9 +675,13 @@ impl video_model::VideoModel {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let updated_path = updated_path.to_string();
let result = query!("UPDATE videos SET filePath = ? where id = ?", updated_path, index)
.execute(&mut self.as_mut().rust_mut().db)
.await;
let result = query!(
"UPDATE videos SET file_path = ? where id = ?",
updated_path,
index
)
.execute(&mut self.as_mut().rust_mut().db)
.await;
match result {
Ok(_i) => {
for video in self
@ -633,9 +692,11 @@ impl video_model::VideoModel {
.filter(|x| x.id == index)
{
video.path = updated_path.clone();
debug!(title = video.title,
path = updated_path,
"updated video path");
debug!(
title = video.title,
path = updated_path,
"updated video path"
);
}
self.as_mut().data_changed(
model_index,
@ -647,7 +708,7 @@ impl video_model::VideoModel {
Err(e) => {
error!("Error connecting to db: {e}");
false
},
}
}
});
true

View file

@ -7,6 +7,8 @@ mod ytdl {
type QString = cxx_qt_lib::QString;
}
#[auto_cxx_name]
#[auto_rust_name]
unsafe extern "RustQt" {
#[qobject]
#[qml_element]
@ -67,14 +69,10 @@ impl ytdl::Ytdl {
.run()
.unwrap();
let output = ytdl.into_single_video().unwrap();
debug!(
output.title,
output.thumbnail, output.url
);
debug!(output.title, output.thumbnail, output.url);
let title = QString::from(&output.title);
let thumbnail = QUrl::from(
&output.thumbnail.unwrap_or_default(),
);
let thumbnail =
QUrl::from(&output.thumbnail.unwrap_or_default());
let mut file = String::from(output_dirs);
file.push('/');
file.push_str(&output.title);
@ -86,12 +84,8 @@ impl ytdl::Ytdl {
qobject_ytdl.as_mut().set_loaded(true);
qobject_ytdl.as_mut().set_loading(false);
qobject_ytdl.as_mut().set_title(title);
qobject_ytdl
.as_mut()
.set_thumbnail(thumbnail);
qobject_ytdl
.as_mut()
.set_file(QUrl::from(&file));
qobject_ytdl.as_mut().set_thumbnail(thumbnail);
qobject_ytdl.as_mut().set_file(QUrl::from(&file));
})
});
true