fix: load svgs with resvg
This commit is contained in:
parent
c62fbfe510
commit
a98eb6f4ce
160
Cargo.lock
generated
160
Cargo.lock
generated
@ -165,9 +165,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.89"
|
version = "1.0.91"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6"
|
checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arboard"
|
name = "arboard"
|
||||||
@ -926,6 +926,12 @@ version = "2.6.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2"
|
checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "data-url"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.10.7"
|
version = "0.10.7"
|
||||||
@ -1036,20 +1042,16 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "egui-video"
|
name = "egui-video"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
source = "git+https://github.com/v0l/egui-video.git?rev=eb2675dd5206d064afdd82ea72c0fac083596d86#eb2675dd5206d064afdd82ea72c0fac083596d86"
|
source = "git+https://github.com/v0l/egui-video.git?rev=ced65d0bb4d2d144b87c70518a04b767ba37c0c1#ced65d0bb4d2d144b87c70518a04b767ba37c0c1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"atomic",
|
"atomic",
|
||||||
"bytemuck",
|
|
||||||
"chrono",
|
|
||||||
"cpal",
|
"cpal",
|
||||||
"egui",
|
"egui",
|
||||||
"egui_inbox",
|
"egui_inbox",
|
||||||
"ffmpeg-sys-the-third",
|
"ffmpeg-rs-raw",
|
||||||
"libc",
|
|
||||||
"log",
|
"log",
|
||||||
"nom",
|
"nom",
|
||||||
"ringbuf",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1207,6 +1209,17 @@ dependencies = [
|
|||||||
"simd-adler32",
|
"simd-adler32",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ffmpeg-rs-raw"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "git+https://git.v0l.io/Kieran/ffmpeg-rs-raw.git?rev=cbaf4069c7fac6577621850a96d296fa42dc11c1#cbaf4069c7fac6577621850a96d296fa42dc11c1"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"ffmpeg-sys-the-third",
|
||||||
|
"libc",
|
||||||
|
"slimbox",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ffmpeg-sys-the-third"
|
name = "ffmpeg-sys-the-third"
|
||||||
version = "2.0.0+ffmpeg-7.0"
|
version = "2.0.0+ffmpeg-7.0"
|
||||||
@ -1240,6 +1253,12 @@ dependencies = [
|
|||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "float-cmp"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fnv"
|
name = "fnv"
|
||||||
version = "1.0.7"
|
version = "1.0.7"
|
||||||
@ -1804,6 +1823,12 @@ dependencies = [
|
|||||||
"png",
|
"png",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "imagesize"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.6.0"
|
version = "2.6.0"
|
||||||
@ -1934,6 +1959,16 @@ version = "3.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
|
checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kurbo"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "89234b2cc610a7dd927ebde6b41dd1a5d4214cffaef4cf1fb2195d592f92518f"
|
||||||
|
dependencies = [
|
||||||
|
"arrayvec",
|
||||||
|
"smallvec",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@ -2745,6 +2780,12 @@ version = "2.3.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pico-args"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project"
|
name = "pin-project"
|
||||||
version = "1.1.6"
|
version = "1.1.6"
|
||||||
@ -2828,12 +2869,6 @@ dependencies = [
|
|||||||
"universal-hash",
|
"universal-hash",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "portable-atomic"
|
|
||||||
version = "1.9.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.20"
|
version = "0.2.20"
|
||||||
@ -3092,6 +3127,29 @@ dependencies = [
|
|||||||
"windows-registry",
|
"windows-registry",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "resvg"
|
||||||
|
version = "0.44.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4a325d5e8d1cebddd070b13f44cec8071594ab67d1012797c121f27a669b7958"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"pico-args",
|
||||||
|
"rgb",
|
||||||
|
"svgtypes",
|
||||||
|
"tiny-skia",
|
||||||
|
"usvg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rgb"
|
||||||
|
version = "0.8.50"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ring"
|
name = "ring"
|
||||||
version = "0.17.8"
|
version = "0.17.8"
|
||||||
@ -3108,14 +3166,10 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ringbuf"
|
name = "roxmltree"
|
||||||
version = "0.4.7"
|
version = "0.20.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "726bb493fe9cac765e8f96a144c3a8396bdf766dedad22e504b70b908dcbceb4"
|
checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
|
||||||
dependencies = [
|
|
||||||
"crossbeam-utils",
|
|
||||||
"portable-atomic",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
@ -3418,6 +3472,21 @@ version = "0.3.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simplecss"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a11be7c62927d9427e9f40f3444d5499d868648e2edbc4e2116de69e7ec0e89d"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "siphasher"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.9"
|
version = "0.4.9"
|
||||||
@ -3427,6 +3496,12 @@ dependencies = [
|
|||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "slimbox"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "26dfcf7e4fe830e4b9245b9e0def30d3df9ea194aca707e9a78b079d2b646b1a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slotmap"
|
name = "slotmap"
|
||||||
version = "1.0.7"
|
version = "1.0.7"
|
||||||
@ -3523,6 +3598,9 @@ name = "strict-num"
|
|||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
|
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
|
||||||
|
dependencies = [
|
||||||
|
"float-cmp",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "subtle"
|
name = "subtle"
|
||||||
@ -3530,6 +3608,16 @@ version = "2.6.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "svgtypes"
|
||||||
|
version = "0.15.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "794de53cc48eaabeed0ab6a3404a65f40b3e38c067e4435883a65d2aa4ca000e"
|
||||||
|
dependencies = [
|
||||||
|
"kurbo",
|
||||||
|
"siphasher",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.109"
|
version = "1.0.109"
|
||||||
@ -3611,6 +3699,7 @@ dependencies = [
|
|||||||
"bytemuck",
|
"bytemuck",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"log",
|
"log",
|
||||||
|
"png",
|
||||||
"tiny-skia-path",
|
"tiny-skia-path",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -3911,6 +4000,28 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "usvg"
|
||||||
|
version = "0.44.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7447e703d7223b067607655e625e0dbca80822880248937da65966194c4864e6"
|
||||||
|
dependencies = [
|
||||||
|
"base64",
|
||||||
|
"data-url",
|
||||||
|
"flate2",
|
||||||
|
"imagesize",
|
||||||
|
"kurbo",
|
||||||
|
"log",
|
||||||
|
"pico-args",
|
||||||
|
"roxmltree",
|
||||||
|
"simplecss",
|
||||||
|
"siphasher",
|
||||||
|
"strict-num",
|
||||||
|
"svgtypes",
|
||||||
|
"tiny-skia-path",
|
||||||
|
"xmlwriter",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf-8"
|
name = "utf-8"
|
||||||
version = "0.7.6"
|
version = "0.7.6"
|
||||||
@ -4751,6 +4862,12 @@ version = "0.8.22"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "af4e2e2f7cba5a093896c1e150fbfe177d1883e7448200efb81d40b9d339ef26"
|
checksum = "af4e2e2f7cba5a093896c1e150fbfe177d1883e7448200efb81d40b9d339ef26"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xmlwriter"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zap_stream_app"
|
name = "zap_stream_app"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -4773,6 +4890,7 @@ dependencies = [
|
|||||||
"nostrdb",
|
"nostrdb",
|
||||||
"pretty_env_logger",
|
"pretty_env_logger",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
|
"resvg",
|
||||||
"sha2",
|
"sha2",
|
||||||
"tokio",
|
"tokio",
|
||||||
"uuid",
|
"uuid",
|
||||||
|
@ -19,14 +19,15 @@ bech32 = "0.11.0"
|
|||||||
libc = "0.2.158"
|
libc = "0.2.158"
|
||||||
uuid = { version = "1.11.0", features = ["v4"] }
|
uuid = { version = "1.11.0", features = ["v4"] }
|
||||||
chrono = "0.4.38"
|
chrono = "0.4.38"
|
||||||
anyhow = "1.0.89"
|
anyhow = "^1.0.91"
|
||||||
async-trait = "0.1.83"
|
async-trait = "0.1.83"
|
||||||
sha2 = "0.10.8"
|
sha2 = "0.10.8"
|
||||||
reqwest = { version = "0.12.7", default-features = false, features = ["rustls-tls-native-roots"] }
|
reqwest = { version = "0.12.7", default-features = false, features = ["rustls-tls-native-roots"] }
|
||||||
itertools = "0.13.0"
|
itertools = "0.13.0"
|
||||||
lru = "0.12.5"
|
lru = "0.12.5"
|
||||||
|
|
||||||
egui-video = { git = "https://github.com/v0l/egui-video.git", rev = "eb2675dd5206d064afdd82ea72c0fac083596d86" }
|
egui-video = { git = "https://github.com/v0l/egui-video.git", rev = "ced65d0bb4d2d144b87c70518a04b767ba37c0c1" }
|
||||||
|
resvg = { version = "0.44.0", default-features = false }
|
||||||
#egui-video = { path = "../egui-video" }
|
#egui-video = { path = "../egui-video" }
|
||||||
|
|
||||||
[target.'cfg(target_os = "android")'.dependencies]
|
[target.'cfg(target_os = "android")'.dependencies]
|
||||||
|
BIN
assets/logo.png
Normal file
BIN
assets/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.0 KiB |
@ -1,6 +1,14 @@
|
|||||||
|
|
||||||
|
icon: "./assets/logo.png"
|
||||||
android:
|
android:
|
||||||
manifest:
|
manifest:
|
||||||
package: stream.zap.app
|
package: stream.zap.app
|
||||||
|
version_code: 1
|
||||||
|
version_name: "0.1.0"
|
||||||
|
application:
|
||||||
|
label: "zap.stream"
|
||||||
|
activities:
|
||||||
|
- hardware_accelerated: true
|
||||||
uses_permission:
|
uses_permission:
|
||||||
- name: "android.permission.WRITE_EXTERNAL_STORAGE"
|
- name: "android.permission.WRITE_EXTERNAL_STORAGE"
|
||||||
- name: "android.permission.READ_EXTERNAL_STORAGE"
|
- name: "android.permission.READ_EXTERNAL_STORAGE"
|
||||||
|
23
src/app.rs
23
src/app.rs
@ -1,18 +1,24 @@
|
|||||||
use crate::route::Router;
|
use crate::route::Router;
|
||||||
use eframe::{App, CreationContext, Frame};
|
use eframe::{App, CreationContext, Frame};
|
||||||
use egui::{Color32, Context};
|
use egui::{Color32, Context, Margin};
|
||||||
use nostr_sdk::database::MemoryDatabase;
|
use nostr_sdk::database::MemoryDatabase;
|
||||||
use nostr_sdk::Client;
|
use nostr_sdk::Client;
|
||||||
use nostrdb::{Config, Ndb};
|
use nostrdb::{Config, Ndb};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
pub struct ZapStreamApp {
|
pub struct ZapStreamApp<T> {
|
||||||
client: Client,
|
client: Client,
|
||||||
router: Router,
|
router: Router,
|
||||||
|
config: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ZapStreamApp {
|
/// Trait to wrap native configuration layers
|
||||||
pub fn new(cc: &CreationContext, data_path: PathBuf) -> Self {
|
pub trait AppConfig {
|
||||||
|
fn frame_margin(&self) -> Margin;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ZapStreamApp<T> {
|
||||||
|
pub fn new(cc: &CreationContext, data_path: PathBuf, config: T) -> Self {
|
||||||
let client = Client::builder()
|
let client = Client::builder()
|
||||||
.database(MemoryDatabase::with_opts(Default::default()))
|
.database(MemoryDatabase::with_opts(Default::default()))
|
||||||
.build();
|
.build();
|
||||||
@ -45,13 +51,20 @@ impl ZapStreamApp {
|
|||||||
Self {
|
Self {
|
||||||
client: client.clone(),
|
client: client.clone(),
|
||||||
router: Router::new(data_path, cc.egui_ctx.clone(), client.clone(), ndb.clone()),
|
router: Router::new(data_path, cc.egui_ctx.clone(), client.clone(), ndb.clone()),
|
||||||
|
config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App for ZapStreamApp {
|
impl<T> App for ZapStreamApp<T>
|
||||||
|
where
|
||||||
|
T: AppConfig,
|
||||||
|
{
|
||||||
fn update(&mut self, ctx: &Context, frame: &mut Frame) {
|
fn update(&mut self, ctx: &Context, frame: &mut Frame) {
|
||||||
let mut app_frame = egui::containers::Frame::default();
|
let mut app_frame = egui::containers::Frame::default();
|
||||||
|
let margin = self.config.frame_margin();
|
||||||
|
|
||||||
|
app_frame.inner_margin = margin;
|
||||||
app_frame.stroke.color = Color32::BLACK;
|
app_frame.stroke.color = Color32::BLACK;
|
||||||
|
|
||||||
//ctx.set_debug_on_hover(true);
|
//ctx.set_debug_on_hover(true);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use eframe::Renderer;
|
use eframe::Renderer;
|
||||||
use egui::Vec2;
|
use egui::{Margin, Vec2};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use zap_stream_app::app::ZapStreamApp;
|
use zap_stream_app::app::{AppConfig, ZapStreamApp};
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
@ -13,10 +13,19 @@ async fn main() {
|
|||||||
options.renderer = Renderer::Glow;
|
options.renderer = Renderer::Glow;
|
||||||
options.viewport = options.viewport.with_inner_size(Vec2::new(360., 720.));
|
options.viewport = options.viewport.with_inner_size(Vec2::new(360., 720.));
|
||||||
|
|
||||||
|
let config = DesktopApp;
|
||||||
let data_path = PathBuf::from("./.data");
|
let data_path = PathBuf::from("./.data");
|
||||||
let _res = eframe::run_native(
|
let _res = eframe::run_native(
|
||||||
"zap.stream",
|
"zap.stream",
|
||||||
options,
|
options,
|
||||||
Box::new(move |cc| Ok(Box::new(ZapStreamApp::new(cc, data_path)))),
|
Box::new(move |cc| Ok(Box::new(ZapStreamApp::new(cc, data_path, config)))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct DesktopApp;
|
||||||
|
|
||||||
|
impl AppConfig for DesktopApp {
|
||||||
|
fn frame_margin(&self) -> Margin {
|
||||||
|
Margin::ZERO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
32
src/lib.rs
32
src/lib.rs
@ -8,9 +8,10 @@ mod stream_info;
|
|||||||
mod theme;
|
mod theme;
|
||||||
mod widgets;
|
mod widgets;
|
||||||
|
|
||||||
use crate::app::ZapStreamApp;
|
use crate::app::{AppConfig, ZapStreamApp};
|
||||||
use eframe::Renderer;
|
use eframe::Renderer;
|
||||||
|
use egui::{Margin, ViewportBuilder};
|
||||||
|
use std::ops::{Div, Mul};
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
use winit::platform::android::activity::AndroidApp;
|
use winit::platform::android::activity::AndroidApp;
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
@ -28,6 +29,8 @@ pub async fn android_main(app: AndroidApp) {
|
|||||||
let mut options = eframe::NativeOptions::default();
|
let mut options = eframe::NativeOptions::default();
|
||||||
options.renderer = Renderer::Glow;
|
options.renderer = Renderer::Glow;
|
||||||
|
|
||||||
|
options.viewport = ViewportBuilder::default().with_fullscreen(true);
|
||||||
|
|
||||||
let app_clone_for_event_loop = app.clone();
|
let app_clone_for_event_loop = app.clone();
|
||||||
options.event_loop_builder = Some(Box::new(move |builder| {
|
options.event_loop_builder = Some(Box::new(move |builder| {
|
||||||
builder.with_android_app(app_clone_for_event_loop);
|
builder.with_android_app(app_clone_for_event_loop);
|
||||||
@ -38,9 +41,32 @@ pub async fn android_main(app: AndroidApp) {
|
|||||||
.expect("external data path")
|
.expect("external data path")
|
||||||
.to_path_buf();
|
.to_path_buf();
|
||||||
|
|
||||||
|
let app = app.clone();
|
||||||
let _res = eframe::run_native(
|
let _res = eframe::run_native(
|
||||||
"zap.stream",
|
"zap.stream",
|
||||||
options,
|
options,
|
||||||
Box::new(move |cc| Ok(Box::new(ZapStreamApp::new(cc, data_path)))),
|
Box::new(move |cc| Ok(Box::new(ZapStreamApp::new(cc, data_path, app)))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
impl AppConfig for AndroidApp {
|
||||||
|
fn frame_margin(&self) -> Margin {
|
||||||
|
if let Some(wd) = self.native_window() {
|
||||||
|
let (w, h) = (wd.width(), wd.height());
|
||||||
|
let c_rect = self.content_rect();
|
||||||
|
let dpi = self.config().density().unwrap_or(160);
|
||||||
|
let dpi_scale = dpi as f32 / 160.0;
|
||||||
|
// TODO: this calc is weird but seems to work on my phone
|
||||||
|
Margin {
|
||||||
|
bottom: (h - c_rect.bottom) as f32,
|
||||||
|
left: c_rect.left as f32,
|
||||||
|
right: (w - c_rect.right) as f32,
|
||||||
|
top: (c_rect.top - (h - c_rect.bottom)) as f32,
|
||||||
|
}
|
||||||
|
.div(dpi_scale)
|
||||||
|
} else {
|
||||||
|
Margin::ZERO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -69,7 +69,10 @@ impl NostrWidget for StreamPage {
|
|||||||
|
|
||||||
let chat_h = 60.0;
|
let chat_h = 60.0;
|
||||||
let w = ui.available_width();
|
let w = ui.available_width();
|
||||||
let h = ui.available_height();
|
let h = ui
|
||||||
|
.available_height()
|
||||||
|
.max(ui.available_rect_before_wrap().height())
|
||||||
|
.max(chat_h);
|
||||||
ui.allocate_ui(Vec2::new(w, h - chat_h), |ui| {
|
ui.allocate_ui(Vec2::new(w, h - chat_h), |ui| {
|
||||||
if let Some(c) = self.chat.as_mut() {
|
if let Some(c) = self.chat.as_mut() {
|
||||||
c.render(ui, services);
|
c.render(ui, services);
|
||||||
@ -77,7 +80,9 @@ impl NostrWidget for StreamPage {
|
|||||||
ui.label("Loading..");
|
ui.label("Loading..");
|
||||||
}
|
}
|
||||||
// consume rest of space
|
// consume rest of space
|
||||||
ui.add_space(ui.available_height());
|
if ui.available_height().is_finite() {
|
||||||
|
ui.add_space(ui.available_height());
|
||||||
|
}
|
||||||
});
|
});
|
||||||
ui.allocate_ui(Vec2::new(w, chat_h), |ui| self.new_msg.render(ui, services))
|
ui.allocate_ui(Vec2::new(w, chat_h), |ui| self.new_msg.render(ui, services))
|
||||||
.response
|
.response
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
use egui::ColorImage;
|
use egui::ColorImage;
|
||||||
|
use egui_video::ffmpeg_rs_raw::{Decoder, Demuxer, Scaler};
|
||||||
|
use egui_video::ffmpeg_sys_the_third::{av_frame_free, av_packet_free, AVPixelFormat};
|
||||||
|
use egui_video::media_player::video_frame_to_image;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
pub struct FfmpegLoader {}
|
pub struct FfmpegLoader {}
|
||||||
@ -10,45 +13,63 @@ impl FfmpegLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_image(&self, path: PathBuf) -> Result<ColorImage, Error> {
|
pub fn load_image(&self, path: PathBuf) -> Result<ColorImage, Error> {
|
||||||
unsafe {
|
let demux = Demuxer::new(path.to_str().unwrap());
|
||||||
let mut demux = egui_video::ffmpeg::demux::Demuxer::new(path.to_str().unwrap());
|
Self::load_image_from_demuxer(demux)
|
||||||
let info = demux.probe_input()?;
|
}
|
||||||
|
|
||||||
let bv = info.best_video();
|
pub fn load_image_bytes<'a>(
|
||||||
if bv.is_none() {
|
&self,
|
||||||
|
key: &str,
|
||||||
|
data: &'static [u8],
|
||||||
|
) -> Result<ColorImage, Error> {
|
||||||
|
let demux = Demuxer::new_custom_io(data, Some(key.to_string()));
|
||||||
|
Self::load_image_from_demuxer(demux)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_image_from_demuxer(mut demuxer: Demuxer) -> Result<ColorImage, Error> {
|
||||||
|
unsafe {
|
||||||
|
let info = demuxer.probe_input()?;
|
||||||
|
|
||||||
|
let bv = if let Some(v) = info.best_video() {
|
||||||
|
v
|
||||||
|
} else {
|
||||||
anyhow::bail!("Not a video/image");
|
anyhow::bail!("Not a video/image");
|
||||||
}
|
};
|
||||||
let bv = bv.unwrap();
|
let mut decode = Decoder::new();
|
||||||
let mut decode = egui_video::ffmpeg::decode::Decoder::new();
|
let rgb = AVPixelFormat::AV_PIX_FMT_RGB24;
|
||||||
let rgb = egui_video::ffmpeg_sys_the_third::AVPixelFormat::AV_PIX_FMT_RGB24;
|
let mut scaler = Scaler::new(rgb);
|
||||||
let mut scaler = egui_video::ffmpeg::scale::Scaler::new(rgb);
|
|
||||||
|
decode.setup_decoder(bv, None)?;
|
||||||
|
|
||||||
let mut n_pkt = 0;
|
let mut n_pkt = 0;
|
||||||
loop {
|
loop {
|
||||||
let (mut pkt, stream) = demux.get_packet()?;
|
let (mut pkt, stream) = demuxer.get_packet()?;
|
||||||
|
if pkt.is_null() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (*stream).index as usize == bv.index {
|
if (*stream).index as usize == bv.index {
|
||||||
let frames = decode.decode_pkt(pkt, stream)?;
|
let frames = decode.decode_pkt(pkt, stream)?;
|
||||||
if let Some((frame, _)) = frames.first() {
|
if let Some((frame, _)) = frames.first() {
|
||||||
let mut frame = *frame;
|
let mut frame = *frame;
|
||||||
let mut frame_rgb = scaler.process_frame(
|
let frame_rgb = scaler.process_frame(
|
||||||
frame,
|
frame,
|
||||||
(*frame).width as u16,
|
(*frame).width as u16,
|
||||||
(*frame).height as u16,
|
(*frame).height as u16,
|
||||||
)?;
|
)?;
|
||||||
egui_video::ffmpeg_sys_the_third::av_frame_free(&mut frame);
|
av_frame_free(&mut frame);
|
||||||
|
|
||||||
let image = egui_video::ffmpeg::video_frame_to_image(frame_rgb);
|
let image = video_frame_to_image(frame_rgb);
|
||||||
egui_video::ffmpeg_sys_the_third::av_frame_free(&mut frame_rgb);
|
|
||||||
return Ok(image);
|
return Ok(image);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
egui_video::ffmpeg_sys_the_third::av_packet_free(&mut pkt);
|
av_packet_free(&mut pkt);
|
||||||
|
|
||||||
n_pkt += 1;
|
n_pkt += 1;
|
||||||
if n_pkt > 10 {
|
if n_pkt > 10 {
|
||||||
anyhow::bail!("No image found");
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
anyhow::bail!("No image found");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,12 @@ use crate::theme::NEUTRAL_800;
|
|||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
use eframe::epaint::Color32;
|
use eframe::epaint::Color32;
|
||||||
use egui::load::SizedTexture;
|
use egui::load::SizedTexture;
|
||||||
use egui::{ColorImage, Context, Image, ImageData, TextureHandle, TextureOptions};
|
use egui::{ColorImage, Context, Image, ImageData, SizeHint, TextureHandle, TextureOptions};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use log::{error, info};
|
use log::{error, info};
|
||||||
use lru::LruCache;
|
use lru::LruCache;
|
||||||
use nostr_sdk::util::hex;
|
use nostr_sdk::util::hex;
|
||||||
|
use resvg::usvg::Transform;
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
@ -57,6 +58,33 @@ impl ImageCache {
|
|||||||
.join(PathBuf::from(hash))
|
.join(PathBuf::from(hash))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn load_bytes_impl(url: &str, bytes: &'static [u8]) -> Result<ColorImage, Error> {
|
||||||
|
if url.ends_with(".svg") {
|
||||||
|
Self::load_svg(bytes)
|
||||||
|
} else {
|
||||||
|
let mut loader = FfmpegLoader::new();
|
||||||
|
loader.load_image_bytes(url, bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_bytes<'a, U>(&self, url: U, bytes: &'static [u8]) -> Image<'a>
|
||||||
|
where
|
||||||
|
U: Into<String>,
|
||||||
|
{
|
||||||
|
let url = url.into();
|
||||||
|
match Self::load_bytes_impl(&url, bytes) {
|
||||||
|
Ok(i) => {
|
||||||
|
let tex = self
|
||||||
|
.ctx
|
||||||
|
.load_texture(url, ImageData::from(i), TextureOptions::default());
|
||||||
|
Image::from_texture(&tex)
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
panic!("Failed to load image: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn load<'a, U>(&self, url: U) -> Image<'a>
|
pub fn load<'a, U>(&self, url: U) -> Image<'a>
|
||||||
where
|
where
|
||||||
U: Into<String>,
|
U: Into<String>,
|
||||||
@ -119,4 +147,25 @@ impl ImageCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn load_svg(svg: &[u8]) -> Result<ColorImage, Error> {
|
||||||
|
use resvg::tiny_skia::Pixmap;
|
||||||
|
use resvg::usvg::{Options, Tree};
|
||||||
|
|
||||||
|
let opt = Options::default();
|
||||||
|
let mut rtree = Tree::from_data(svg, &opt)
|
||||||
|
.map_err(|err| err.to_string())
|
||||||
|
.map_err(|e| Error::msg(e))?;
|
||||||
|
|
||||||
|
let size = rtree.size().to_int_size();
|
||||||
|
let (w, h) = (size.width(), size.height());
|
||||||
|
|
||||||
|
let mut pixmap = Pixmap::new(w, h)
|
||||||
|
.ok_or_else(|| Error::msg(format!("Failed to create SVG Pixmap of size {w}x{h}")))?;
|
||||||
|
|
||||||
|
resvg::render(&rtree, Transform::default(), &mut pixmap.as_mut());
|
||||||
|
let image = ColorImage::from_rgba_unmultiplied([w as _, h as _], pixmap.data());
|
||||||
|
|
||||||
|
Ok(image)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,9 @@ impl NostrWidget for Header {
|
|||||||
Layout::left_to_right(Align::Center),
|
Layout::left_to_right(Align::Center),
|
||||||
|ui| {
|
|ui| {
|
||||||
ui.style_mut().spacing.item_spacing.x = 16.;
|
ui.style_mut().spacing.item_spacing.x = 16.;
|
||||||
if Image::from_bytes("logo.svg", logo_bytes)
|
if services
|
||||||
|
.img_cache
|
||||||
|
.load_bytes("logo.svg", logo_bytes)
|
||||||
.max_height(24.)
|
.max_height(24.)
|
||||||
.sense(Sense::click())
|
.sense(Sense::click())
|
||||||
.ui(ui)
|
.ui(ui)
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
use crate::route::RouteServices;
|
use crate::route::RouteServices;
|
||||||
use crate::theme::NEUTRAL_900;
|
use crate::theme::NEUTRAL_900;
|
||||||
use crate::widgets::NostrWidget;
|
use crate::widgets::NostrWidget;
|
||||||
use egui::{Frame, Image, Margin, Response, Rounding, Sense, Stroke, TextEdit, Ui, Widget};
|
use eframe::emath::Align;
|
||||||
|
use egui::{Frame, Image, Layout, Margin, Response, Rounding, Sense, Stroke, TextEdit, Ui, Widget};
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
pub struct WriteChat {
|
pub struct WriteChat {
|
||||||
@ -25,12 +26,12 @@ impl NostrWidget for WriteChat {
|
|||||||
Frame::none()
|
Frame::none()
|
||||||
.fill(NEUTRAL_900)
|
.fill(NEUTRAL_900)
|
||||||
.rounding(Rounding::same(12.0))
|
.rounding(Rounding::same(12.0))
|
||||||
.inner_margin(Margin::symmetric(12., 12.))
|
.inner_margin(Margin::symmetric(12., 10.))
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
let editor = TextEdit::singleline(&mut self.msg).frame(false);
|
if services
|
||||||
ui.add(editor);
|
.img_cache
|
||||||
if Image::from_bytes("send-03.svg", logo_bytes)
|
.load_bytes("send-03.svg", logo_bytes)
|
||||||
.sense(Sense::click())
|
.sense(Sense::click())
|
||||||
.ui(ui)
|
.ui(ui)
|
||||||
.clicked()
|
.clicked()
|
||||||
@ -38,6 +39,9 @@ impl NostrWidget for WriteChat {
|
|||||||
info!("Sending: {}", self.msg);
|
info!("Sending: {}", self.msg);
|
||||||
self.msg.clear();
|
self.msg.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let editor = TextEdit::singleline(&mut self.msg).frame(false);
|
||||||
|
ui.add_sized(ui.available_size(), editor);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user