feat: resize images
This commit is contained in:
parent
7e9e61d14c
commit
62a7933de0
35
Cargo.lock
generated
35
Cargo.lock
generated
@ -1362,11 +1362,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "directories"
|
||||
version = "5.0.1"
|
||||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35"
|
||||
checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d"
|
||||
dependencies = [
|
||||
"dirs-sys",
|
||||
"dirs-sys 0.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1375,7 +1375,7 @@ version = "5.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
|
||||
dependencies = [
|
||||
"dirs-sys",
|
||||
"dirs-sys 0.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1386,10 +1386,22 @@ checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"option-ext",
|
||||
"redox_users",
|
||||
"redox_users 0.4.6",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"option-ext",
|
||||
"redox_users 0.5.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dispatch"
|
||||
version = "0.2.0"
|
||||
@ -1512,7 +1524,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "egui-video"
|
||||
version = "0.8.0"
|
||||
source = "git+https://github.com/v0l/egui-video.git?rev=d2ea3b4db21eb870a207db19e4cd21c7d1d24836#d2ea3b4db21eb870a207db19e4cd21c7d1d24836"
|
||||
source = "git+https://github.com/v0l/egui-video.git?rev=11db7d0c30070529a36bfb050844cdb75c32902b#11db7d0c30070529a36bfb050844cdb75c32902b"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"atomic",
|
||||
@ -4517,6 +4529,17 @@ dependencies = [
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"libredox",
|
||||
"thiserror 2.0.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.0"
|
||||
|
@ -18,8 +18,8 @@ bech32 = "0.11.0"
|
||||
anyhow = "^1.0.91"
|
||||
itertools = "0.14.0"
|
||||
serde = { version = "1.0.214", features = ["derive"] }
|
||||
directories = "5.0.1"
|
||||
egui-video = { git = "https://github.com/v0l/egui-video.git", rev = "d2ea3b4db21eb870a207db19e4cd21c7d1d24836" }
|
||||
directories = "6.0.0"
|
||||
egui-video = { git = "https://github.com/v0l/egui-video.git", rev = "11db7d0c30070529a36bfb050844cdb75c32902b" }
|
||||
egui_qr = { git = "https://git.v0l.io/Kieran/egui_qr.git", rev = "f9cf52b7eae353fa9e59ed0358151211d48824d1" }
|
||||
|
||||
# notedeck stuff
|
||||
|
@ -62,6 +62,12 @@ impl ZapStreamApp {
|
||||
.insert(FontFamily::Proportional, vec!["Outfit".to_string()]);
|
||||
cc.egui_ctx.set_fonts(fd);
|
||||
|
||||
// ffmpeg log redirect
|
||||
unsafe {
|
||||
egui_video::ffmpeg_sys_the_third::av_log_set_callback(Some(
|
||||
egui_video::ffmpeg_rs_raw::av_log_redirect,
|
||||
));
|
||||
}
|
||||
let (tx, rx) = mpsc::channel();
|
||||
Self {
|
||||
current: RouteType::HomePage,
|
||||
@ -103,6 +109,8 @@ impl notedeck::App for ZapStreamApp {
|
||||
},
|
||||
);
|
||||
|
||||
//ui.ctx().set_debug_on_hover(true);
|
||||
|
||||
let app_frame = egui::containers::Frame::default().outer_margin(self.frame_margin());
|
||||
|
||||
// handle app state changes
|
||||
|
@ -2,7 +2,7 @@ use anyhow::Result;
|
||||
use directories::ProjectDirs;
|
||||
use eframe::Renderer;
|
||||
use egui::{Vec2, ViewportBuilder};
|
||||
use log::error;
|
||||
use log::{error, info};
|
||||
use zap_stream_app::app::ZapStreamApp;
|
||||
|
||||
#[tokio::main]
|
||||
@ -13,11 +13,12 @@ async fn main() -> Result<()> {
|
||||
options.viewport = ViewportBuilder::default().with_inner_size(Vec2::new(1300., 900.));
|
||||
options.renderer = Renderer::Glow;
|
||||
|
||||
let data_path = ProjectDirs::from("stream", "zap", "app")
|
||||
let data_path = ProjectDirs::from("stream", "zap", "zap_stream_app")
|
||||
.unwrap()
|
||||
.config_dir()
|
||||
.data_dir()
|
||||
.to_path_buf();
|
||||
|
||||
info!("Data path: {}", data_path.display());
|
||||
if let Err(e) = eframe::run_native(
|
||||
"zap.stream",
|
||||
options,
|
||||
|
@ -1,13 +1,4 @@
|
||||
<svg width="33" height="24" viewBox="0 0 33 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_4303_1419)">
|
||||
<path d="M4.45977 2.75527L4.44727 2.74902C4.44727 2.74902 4.45352 2.74902 4.45977 2.75527Z" fill="white"/>
|
||||
<path d="M8.97119 1.70361L9.00244 1.71611C8.98994 1.70986 8.98369 1.70361 8.97119 1.70361Z" fill="white"/>
|
||||
<path d="M9.01006 1.71762C9.00381 1.71137 9.00381 1.71762 9.01006 1.71762V1.71762Z" fill="white"/>
|
||||
<path d="M32.7877 2.41234C32.3558 1.36818 31.3439 0.691406 30.216 0.691406H10.6802C10.6738 0.691406 10.6673 0.691406 10.6609 0.691406C10.6545 0.691406 10.648 0.691406 10.6416 0.691406C6.54235 0.691406 3.21012 4.02369 3.21012 8.11654C3.21012 10.2693 4.13825 12.2738 5.70446 13.6661L0.812466 18.5581C0.0132476 19.3574 -0.225229 20.5498 0.206607 21.5875C0.638443 22.6316 1.65036 23.3084 2.77829 23.3084H22.314C22.3205 23.3084 22.3269 23.3084 22.3398 23.3084C22.3463 23.3084 22.3527 23.3084 22.3656 23.3084C26.4584 23.3084 29.7906 19.9761 29.7906 15.8833C29.7906 13.7305 28.8625 11.726 27.2963 10.3338L32.1882 5.44169C32.981 4.64245 33.2195 3.45005 32.7877 2.41234ZM2.71383 20.5498C2.6945 20.5047 2.70739 20.4918 2.73317 20.466L8.10856 15.0905L22.3914 20.1437C22.4043 20.1502 22.4236 20.1566 22.4365 20.1566C22.4558 20.163 22.4752 20.1695 22.4945 20.1824C22.5267 20.2017 22.5525 20.2339 22.5718 20.2726C22.5783 20.2791 22.5783 20.2855 22.5847 20.292C22.5912 20.3048 22.5912 20.3177 22.5912 20.3306C22.5912 20.3435 22.5976 20.3564 22.5976 20.3693C22.5976 20.4853 22.4687 20.5884 22.3269 20.5884H2.78473C2.75251 20.6013 2.73317 20.6013 2.71383 20.5498ZM25.208 19.6474C25.208 19.641 25.2015 19.6281 25.2015 19.6216C25.1757 19.5185 25.1435 19.4218 25.1048 19.3251C25.0984 19.3122 25.0984 19.2994 25.092 19.2865C25.0533 19.1833 25.0017 19.0867 24.9502 18.99C24.9437 18.9771 24.9308 18.9577 24.9244 18.9449C24.8148 18.7515 24.6859 18.571 24.5377 18.4034C24.5248 18.3905 24.5119 18.3777 24.499 18.3648C24.4216 18.2874 24.3443 18.2101 24.2605 18.1392C24.2476 18.1327 24.2347 18.1198 24.2283 18.1134C24.1509 18.0489 24.0672 17.9909 23.9769 17.9329C23.964 17.9265 23.9511 17.9136 23.9382 17.9071C23.848 17.8491 23.7513 17.7976 23.6547 17.746C23.6353 17.7331 23.6095 17.7267 23.5902 17.7138C23.4935 17.6687 23.3904 17.6235 23.2808 17.5913L16.6744 15.2516L9.03668 12.551L9.0109 12.5445C9.00446 12.5445 9.00446 12.5445 8.99801 12.5381C8.8111 12.4672 8.62418 12.3834 8.43727 12.2867C6.88395 11.4682 5.9236 9.86969 5.9236 8.11654C5.9236 6.58253 6.65836 5.2161 7.79918 4.35241C7.79918 4.35885 7.80563 4.37175 7.80563 4.37819C7.83141 4.47487 7.86364 4.57155 7.90231 4.66823C7.9152 4.70046 7.92809 4.73269 7.94098 4.76492C7.97321 4.83582 8.00543 4.90027 8.03766 4.97117C8.05055 5.0034 8.06989 5.03562 8.08278 5.06141C8.1279 5.1452 8.17946 5.22254 8.23747 5.29989C8.26325 5.33211 8.28258 5.36434 8.30836 5.39657C8.35348 5.45458 8.3986 5.51259 8.45016 5.56415C8.4695 5.58993 8.48883 5.61571 8.51462 5.63505C8.57907 5.70595 8.65641 5.7704 8.72731 5.83486C8.75309 5.86064 8.77887 5.87998 8.8111 5.89931C8.882 5.95732 8.9529 6.00888 9.03024 6.06045C9.04313 6.07334 9.05602 6.07978 9.07536 6.09267C9.16559 6.15068 9.26227 6.20225 9.35895 6.24736C9.38473 6.26026 9.41051 6.27315 9.43629 6.28604C9.53942 6.33116 9.64254 6.37627 9.74567 6.4085L24.0091 11.4553C24.0156 11.4553 24.022 11.4617 24.022 11.4617C24.209 11.5326 24.3894 11.61 24.5634 11.7066C26.1168 12.5252 27.0771 14.1237 27.0771 15.8768C27.0836 17.4173 26.3423 18.7837 25.208 19.6474ZM30.2675 3.52739L24.8922 8.90288C24.8793 8.89643 24.8599 8.88999 24.847 8.88354L10.6222 3.85611C10.6029 3.84967 10.5836 3.84322 10.5707 3.83677H10.5642C10.5449 3.83033 10.532 3.82388 10.5127 3.81099C10.5062 3.80455 10.4998 3.7981 10.4933 3.79166C10.4804 3.78521 10.474 3.77232 10.4675 3.76588C10.4611 3.75943 10.4547 3.74654 10.4547 3.74009C10.4482 3.7272 10.4418 3.71431 10.4353 3.70142C10.4353 3.69498 10.4289 3.68208 10.4289 3.66919C10.4289 3.6563 10.4224 3.63697 10.4224 3.61763C10.4224 3.60474 10.4289 3.59829 10.4289 3.5854C10.4482 3.48872 10.5642 3.40493 10.6867 3.40493H30.2289C30.2611 3.40493 30.2804 3.40493 30.2998 3.45005C30.3062 3.48872 30.2933 3.50806 30.2675 3.52739Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_4303_1419">
|
||||
<rect width="33" height="22.617" fill="white" transform="translate(0 0.691406)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<path d="M32.7877 2.41234C32.3558 1.36818 31.3439 0.691406 30.216 0.691406H10.6802C10.6738 0.691406 10.6673 0.691406 10.6609 0.691406C10.6545 0.691406 10.648 0.691406 10.6416 0.691406C6.54235 0.691406 3.21012 4.02369 3.21012 8.11654C3.21012 10.2693 4.13825 12.2738 5.70446 13.6661L0.812466 18.5581C0.0132476 19.3574 -0.225229 20.5498 0.206607 21.5875C0.638443 22.6316 1.65036 23.3084 2.77829 23.3084H22.314C22.3205 23.3084 22.3269 23.3084 22.3398 23.3084C22.3463 23.3084 22.3527 23.3084 22.3656 23.3084C26.4584 23.3084 29.7906 19.9761 29.7906 15.8833C29.7906 13.7305 28.8625 11.726 27.2963 10.3338L32.1882 5.44169C32.981 4.64245 33.2195 3.45005 32.7877 2.41234ZM2.71383 20.5498C2.6945 20.5047 2.70739 20.4918 2.73317 20.466L8.10856 15.0905L22.3914 20.1437C22.4043 20.1502 22.4236 20.1566 22.4365 20.1566C22.4558 20.163 22.4752 20.1695 22.4945 20.1824C22.5267 20.2017 22.5525 20.2339 22.5718 20.2726C22.5783 20.2791 22.5783 20.2855 22.5847 20.292C22.5912 20.3048 22.5912 20.3177 22.5912 20.3306C22.5912 20.3435 22.5976 20.3564 22.5976 20.3693C22.5976 20.4853 22.4687 20.5884 22.3269 20.5884H2.78473C2.75251 20.6013 2.73317 20.6013 2.71383 20.5498ZM25.208 19.6474C25.208 19.641 25.2015 19.6281 25.2015 19.6216C25.1757 19.5185 25.1435 19.4218 25.1048 19.3251C25.0984 19.3122 25.0984 19.2994 25.092 19.2865C25.0533 19.1833 25.0017 19.0867 24.9502 18.99C24.9437 18.9771 24.9308 18.9577 24.9244 18.9449C24.8148 18.7515 24.6859 18.571 24.5377 18.4034C24.5248 18.3905 24.5119 18.3777 24.499 18.3648C24.4216 18.2874 24.3443 18.2101 24.2605 18.1392C24.2476 18.1327 24.2347 18.1198 24.2283 18.1134C24.1509 18.0489 24.0672 17.9909 23.9769 17.9329C23.964 17.9265 23.9511 17.9136 23.9382 17.9071C23.848 17.8491 23.7513 17.7976 23.6547 17.746C23.6353 17.7331 23.6095 17.7267 23.5902 17.7138C23.4935 17.6687 23.3904 17.6235 23.2808 17.5913L16.6744 15.2516L9.03668 12.551L9.0109 12.5445C9.00446 12.5445 9.00446 12.5445 8.99801 12.5381C8.8111 12.4672 8.62418 12.3834 8.43727 12.2867C6.88395 11.4682 5.9236 9.86969 5.9236 8.11654C5.9236 6.58253 6.65836 5.2161 7.79918 4.35241C7.79918 4.35885 7.80563 4.37175 7.80563 4.37819C7.83141 4.47487 7.86364 4.57155 7.90231 4.66823C7.9152 4.70046 7.92809 4.73269 7.94098 4.76492C7.97321 4.83582 8.00543 4.90027 8.03766 4.97117C8.05055 5.0034 8.06989 5.03562 8.08278 5.06141C8.1279 5.1452 8.17946 5.22254 8.23747 5.29989C8.26325 5.33211 8.28258 5.36434 8.30836 5.39657C8.35348 5.45458 8.3986 5.51259 8.45016 5.56415C8.4695 5.58993 8.48883 5.61571 8.51462 5.63505C8.57907 5.70595 8.65641 5.7704 8.72731 5.83486C8.75309 5.86064 8.77887 5.87998 8.8111 5.89931C8.882 5.95732 8.9529 6.00888 9.03024 6.06045C9.04313 6.07334 9.05602 6.07978 9.07536 6.09267C9.16559 6.15068 9.26227 6.20225 9.35895 6.24736C9.38473 6.26026 9.41051 6.27315 9.43629 6.28604C9.53942 6.33116 9.64254 6.37627 9.74567 6.4085L24.0091 11.4553C24.0156 11.4553 24.022 11.4617 24.022 11.4617C24.209 11.5326 24.3894 11.61 24.5634 11.7066C26.1168 12.5252 27.0771 14.1237 27.0771 15.8768C27.0836 17.4173 26.3423 18.7837 25.208 19.6474ZM30.2675 3.52739L24.8922 8.90288C24.8793 8.89643 24.8599 8.88999 24.847 8.88354L10.6222 3.85611C10.6029 3.84967 10.5836 3.84322 10.5707 3.83677H10.5642C10.5449 3.83033 10.532 3.82388 10.5127 3.81099C10.5062 3.80455 10.4998 3.7981 10.4933 3.79166C10.4804 3.78521 10.474 3.77232 10.4675 3.76588C10.4611 3.75943 10.4547 3.74654 10.4547 3.74009C10.4482 3.7272 10.4418 3.71431 10.4353 3.70142C10.4353 3.69498 10.4289 3.68208 10.4289 3.66919C10.4289 3.6563 10.4224 3.63697 10.4224 3.61763C10.4224 3.60474 10.4289 3.59829 10.4289 3.5854C10.4482 3.48872 10.5642 3.40493 10.6867 3.40493H30.2289C30.2611 3.40493 30.2804 3.40493 30.2998 3.45005C30.3062 3.48872 30.2933 3.50806 30.2675 3.52739Z"
|
||||
fill="white"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 3.7 KiB |
@ -23,7 +23,7 @@ impl HomePage {
|
||||
}
|
||||
|
||||
fn get_filters() -> Vec<Filter> {
|
||||
vec![Filter::new().kinds([30_311]).limit(100).build()]
|
||||
vec![Filter::new().kinds([30_311, 30_313]).limit(100).build()]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,8 @@ use crate::services::ffmpeg_loader::FfmpegLoader;
|
||||
use crate::widgets::PlaceholderRect;
|
||||
use anyhow::{anyhow, bail};
|
||||
use egui::load::SizedTexture;
|
||||
use egui::{Context, Id, Image, ImageSource, TextureHandle, Ui};
|
||||
use egui::{Context, Id, Image, ImageSource, TextureHandle, Ui, Vec2};
|
||||
use egui_video::ffmpeg_rs_raw::Transcoder;
|
||||
use ehttp::Response;
|
||||
use enostr::EventClientMessage;
|
||||
use lnurl::lightning_address::LightningAddress;
|
||||
@ -187,18 +188,31 @@ impl<'a, 'ctx> RouteServices<'a, 'ctx> {
|
||||
}
|
||||
}
|
||||
|
||||
const BLACK_PIXEL: [u8; 4] = [0, 0, 0, 0];
|
||||
const LOGO_BYTES: &[u8] = include_bytes!("../resources/logo.svg");
|
||||
|
||||
pub fn image_from_cache<'a>(img_cache: &mut ImageCache, ui: &Ui, url: &str) -> Image<'a> {
|
||||
if let Some(promise) = img_cache.map().get(url) {
|
||||
pub fn image_from_cache<'a>(
|
||||
img_cache: &mut ImageCache,
|
||||
ui: &Ui,
|
||||
url: &str,
|
||||
size: Option<Vec2>,
|
||||
) -> Image<'a> {
|
||||
let cache_key = if let Some(s) = size {
|
||||
format!("{}:{}", url, s)
|
||||
} else {
|
||||
url.to_string()
|
||||
};
|
||||
if url.len() == 0 {
|
||||
return Image::from_bytes(cache_key, LOGO_BYTES);
|
||||
}
|
||||
if let Some(promise) = img_cache.map().get(&cache_key) {
|
||||
match promise.poll() {
|
||||
Poll::Ready(Ok(t)) => Image::new(SizedTexture::from_handle(t)),
|
||||
_ => Image::from_bytes(url.to_string(), &BLACK_PIXEL),
|
||||
_ => Image::from_bytes(url.to_string(), LOGO_BYTES),
|
||||
}
|
||||
} else {
|
||||
let fetch = fetch_img(img_cache, ui.ctx(), url);
|
||||
img_cache.map_mut().insert(url.to_string(), fetch);
|
||||
Image::from_bytes(url.to_string(), &BLACK_PIXEL)
|
||||
let fetch = fetch_img(img_cache, ui.ctx(), url, size);
|
||||
img_cache.map_mut().insert(cache_key.clone(), fetch);
|
||||
Image::from_bytes(cache_key, LOGO_BYTES)
|
||||
}
|
||||
}
|
||||
|
||||
@ -206,52 +220,48 @@ fn fetch_img(
|
||||
img_cache: &ImageCache,
|
||||
ctx: &Context,
|
||||
url: &str,
|
||||
size: Option<Vec2>,
|
||||
) -> Promise<notedeck::Result<TextureHandle>> {
|
||||
let k = ImageCache::key(url);
|
||||
let dst_path = img_cache.cache_dir.join(k);
|
||||
let name = ImageCache::key(url);
|
||||
let dst_path = img_cache.cache_dir.join(&name);
|
||||
if dst_path.exists() {
|
||||
let ctx = ctx.clone();
|
||||
let url = url.to_owned();
|
||||
let dst_path = dst_path.clone();
|
||||
Promise::spawn_blocking(move || {
|
||||
Promise::spawn_thread("load_from_disk", move || {
|
||||
info!("Loading image from disk: {}", dst_path.display());
|
||||
match FfmpegLoader::new().load_image(dst_path) {
|
||||
Ok(img) => Ok(ctx.load_texture(&url, img, Default::default())),
|
||||
match FfmpegLoader::new().load_image(dst_path, size) {
|
||||
Ok(img) => Ok(ctx.load_texture(&name, img, Default::default())),
|
||||
Err(e) => Err(notedeck::Error::Generic(e.to_string())),
|
||||
}
|
||||
})
|
||||
} else {
|
||||
fetch_img_from_net(&dst_path, ctx, url)
|
||||
let url = url.to_string();
|
||||
let ctx = ctx.clone();
|
||||
Promise::spawn_thread("load_from_net", move || {
|
||||
let img = match fetch_img_from_net(&url).block_and_take() {
|
||||
Ok(img) => img,
|
||||
Err(e) => return Err(notedeck::Error::Generic(e.to_string())),
|
||||
};
|
||||
std::fs::create_dir_all(&dst_path.parent().unwrap()).unwrap();
|
||||
std::fs::write(&dst_path, &img.bytes).unwrap();
|
||||
|
||||
info!("Loading image from net: {}", &url);
|
||||
match FfmpegLoader::new().load_image(dst_path, size) {
|
||||
Ok(img) => {
|
||||
ctx.request_repaint();
|
||||
Ok(ctx.load_texture(&name, img, Default::default()))
|
||||
}
|
||||
Err(e) => Err(notedeck::Error::Generic(e.to_string())),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn fetch_img_from_net(
|
||||
cache_path: &Path,
|
||||
ctx: &Context,
|
||||
url: &str,
|
||||
) -> Promise<notedeck::Result<TextureHandle>> {
|
||||
fn fetch_img_from_net(url: &str) -> Promise<ehttp::Result<Response>> {
|
||||
let (sender, promise) = Promise::new();
|
||||
let request = ehttp::Request::get(url);
|
||||
let ctx = ctx.clone();
|
||||
let cloned_url = url.to_owned();
|
||||
let cache_path = cache_path.to_owned();
|
||||
info!("Downloaded image: {}", url);
|
||||
ehttp::fetch(request, move |response| {
|
||||
let handle = response
|
||||
.and_then(|img| {
|
||||
std::fs::create_dir_all(cache_path.parent().unwrap()).unwrap();
|
||||
std::fs::write(&cache_path, &img.bytes).unwrap();
|
||||
info!("Loading image from net: {}", cloned_url);
|
||||
let img_loaded = FfmpegLoader::new()
|
||||
.load_image(cache_path)
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
Ok(ctx.load_texture(&cloned_url, img_loaded, Default::default()))
|
||||
})
|
||||
.map_err(notedeck::Error::Generic);
|
||||
|
||||
sender.send(handle);
|
||||
ctx.request_repaint();
|
||||
sender.send(response);
|
||||
});
|
||||
|
||||
promise
|
||||
}
|
||||
|
@ -35,8 +35,12 @@ impl NostrWidget for ProfilePage {
|
||||
ui.spacing_mut().item_spacing.y = 8.0;
|
||||
|
||||
if let Some(banner) = profile.map(|p| p.banner()).flatten() {
|
||||
image_from_cache(&mut services.ctx.img_cache, ui, banner)
|
||||
.fit_to_exact_size(vec2(ui.available_width(), 360.0))
|
||||
image_from_cache(
|
||||
&mut services.ctx.img_cache,
|
||||
ui,
|
||||
banner,
|
||||
Some(vec2(ui.available_width(), 360.0)),
|
||||
)
|
||||
.rounding(ROUNDING_DEFAULT)
|
||||
.ui(ui);
|
||||
} else {
|
||||
|
@ -1,14 +1,14 @@
|
||||
use crate::link::NostrLink;
|
||||
use crate::route::RouteServices;
|
||||
use crate::stream_info::StreamInfo;
|
||||
use crate::theme::{MARGIN_DEFAULT, NEUTRAL_800, ROUNDING_DEFAULT};
|
||||
use crate::widgets::{
|
||||
sub_or_poll, Chat, NostrWidget, PlaceholderRect, StreamPlayer, StreamTitle, WriteChat,
|
||||
};
|
||||
use egui::{vec2, Align, Frame, Layout, Response, Stroke, Ui, Vec2, Widget};
|
||||
use egui::{vec2, Align, Frame, Layout, Response, ScrollArea, Stroke, Ui, Vec2, Widget};
|
||||
use nostrdb::{Filter, Note};
|
||||
|
||||
use crate::note_ref::NoteRef;
|
||||
use crate::stream_info::StreamInfo;
|
||||
use crate::sub::SubRef;
|
||||
use std::borrow::Borrow;
|
||||
use std::collections::HashSet;
|
||||
@ -95,20 +95,23 @@ impl StreamPage {
|
||||
let video_width = ui.available_width() - chat_w;
|
||||
let video_height = max_h.min((video_width / 16.0) * 9.0);
|
||||
|
||||
ui.with_layout(
|
||||
Layout::left_to_right(Align::TOP).with_main_justify(true),
|
||||
ui.horizontal(|ui| {
|
||||
ui.allocate_ui_with_layout(
|
||||
vec2(video_width, max_h),
|
||||
Layout::top_down_justified(Align::Min),
|
||||
|ui| {
|
||||
ui.vertical(|ui| {
|
||||
ui.allocate_ui(vec2(video_width, video_height), |ui| {
|
||||
ScrollArea::vertical().show(ui, |ui| {
|
||||
if let Some(player) = &mut self.player {
|
||||
player.ui(ui)
|
||||
ui.add_sized(vec2(video_width, video_height), player);
|
||||
} else {
|
||||
ui.add(PlaceholderRect)
|
||||
ui.add_sized(vec2(video_width, video_height), PlaceholderRect);
|
||||
}
|
||||
});
|
||||
|
||||
ui.add_space(10.);
|
||||
StreamTitle::new(event).render(ui, services);
|
||||
});
|
||||
},
|
||||
);
|
||||
ui.allocate_ui_with_layout(
|
||||
vec2(chat_w, max_h),
|
||||
Layout::top_down_justified(Align::Min),
|
||||
@ -136,8 +139,7 @@ impl StreamPage {
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
ui.response()
|
||||
}
|
||||
@ -152,12 +154,17 @@ impl NostrWidget for StreamPage {
|
||||
.collect();
|
||||
|
||||
if let Some(event) = events.first() {
|
||||
if let Some(stream) = event.streaming() {
|
||||
if self.player.is_none() {
|
||||
let p = StreamPlayer::new(ui.ctx(), &stream.to_string());
|
||||
match event.kind() {
|
||||
30_311 => {
|
||||
if let Some(u) = event.streaming().or(event.recording()) {
|
||||
let p = StreamPlayer::new(ui.ctx(), &u.to_string());
|
||||
self.player = Some(p);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
|
||||
if self.chat.is_none() {
|
||||
let ok = event.key().unwrap();
|
||||
|
@ -1,5 +1,5 @@
|
||||
use anyhow::Error;
|
||||
use egui::ColorImage;
|
||||
use egui::{ColorImage, Vec2};
|
||||
use egui_video::ffmpeg_rs_raw::{get_frame_from_hw, 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;
|
||||
@ -12,17 +12,25 @@ impl FfmpegLoader {
|
||||
Self {}
|
||||
}
|
||||
|
||||
pub fn load_image(&self, path: PathBuf) -> Result<ColorImage, Error> {
|
||||
pub fn load_image(&self, path: PathBuf, size: Option<Vec2>) -> Result<ColorImage, Error> {
|
||||
let demux = Demuxer::new(path.to_str().unwrap())?;
|
||||
Self::load_image_from_demuxer(demux)
|
||||
Self::load_image_from_demuxer(demux, size)
|
||||
}
|
||||
|
||||
pub fn load_image_bytes(&self, key: &str, data: &'static [u8]) -> Result<ColorImage, Error> {
|
||||
pub fn load_image_bytes(
|
||||
&self,
|
||||
key: &str,
|
||||
data: &'static [u8],
|
||||
size: Option<Vec2>,
|
||||
) -> Result<ColorImage, Error> {
|
||||
let demux = Demuxer::new_custom_io(data, Some(key.to_string()))?;
|
||||
Self::load_image_from_demuxer(demux)
|
||||
Self::load_image_from_demuxer(demux, size)
|
||||
}
|
||||
|
||||
fn load_image_from_demuxer(mut demuxer: Demuxer) -> Result<ColorImage, Error> {
|
||||
fn load_image_from_demuxer(
|
||||
mut demuxer: Demuxer,
|
||||
size: Option<Vec2>,
|
||||
) -> Result<ColorImage, Error> {
|
||||
unsafe {
|
||||
let info = demuxer.probe_input()?;
|
||||
|
||||
@ -46,8 +54,8 @@ impl FfmpegLoader {
|
||||
let mut frame = get_frame_from_hw(*frame)?;
|
||||
let frame_rgb = scaler.process_frame(
|
||||
frame,
|
||||
(*frame).width as u16,
|
||||
(*frame).height as u16,
|
||||
size.map(|s| s.x as u16).unwrap_or((*frame).width as u16),
|
||||
size.map(|s| s.y as u16).unwrap_or((*frame).height as u16),
|
||||
rgb,
|
||||
)?;
|
||||
av_frame_free(&mut frame);
|
||||
|
@ -89,6 +89,9 @@ impl StreamInfo for Note<'_> {
|
||||
|
||||
/// Is the stream playable by this app
|
||||
fn can_play(&self) -> bool {
|
||||
if self.kind() == 30_313 {
|
||||
return true; // n94-stream can always be played
|
||||
}
|
||||
if let Some(stream) = self.streaming() {
|
||||
stream.contains(".m3u8")
|
||||
} else {
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::route::image_from_cache;
|
||||
use egui::{vec2, Color32, Pos2, Response, Rounding, Sense, Ui, Vec2, Widget};
|
||||
use crate::theme::NEUTRAL_800;
|
||||
use egui::{vec2, Response, Rounding, Sense, Ui, Vec2, Widget};
|
||||
use nostrdb::{Ndb, NdbProfile, Transaction};
|
||||
use notedeck::ImageCache;
|
||||
|
||||
@ -42,11 +43,8 @@ impl Avatar {
|
||||
|
||||
fn placeholder(ui: &mut Ui, size: f32) -> Response {
|
||||
let (response, painter) = ui.allocate_painter(vec2(size, size), Sense::click());
|
||||
painter.circle_filled(
|
||||
Pos2::new(size / 2., size / 2.),
|
||||
size / 2.,
|
||||
Color32::from_rgb(200, 200, 200),
|
||||
);
|
||||
let pos = response.rect.min + vec2(size / 2., size / 2.);
|
||||
painter.circle_filled(pos, size / 2., NEUTRAL_800);
|
||||
response
|
||||
}
|
||||
|
||||
@ -57,9 +55,7 @@ impl Avatar {
|
||||
return Self::placeholder(ui, size_v);
|
||||
}
|
||||
match &self.image {
|
||||
Some(img) => image_from_cache(img_cache, ui, img)
|
||||
.max_size(size)
|
||||
.fit_to_exact_size(size)
|
||||
Some(img) => image_from_cache(img_cache, ui, img, Some(size))
|
||||
.rounding(Rounding::same(size_v))
|
||||
.sense(Sense::click())
|
||||
.ui(ui),
|
||||
|
@ -64,7 +64,7 @@ impl NostrWidget for Chat {
|
||||
1311 => {
|
||||
let profile = services.profile(ev.pubkey());
|
||||
ChatMessage::new(&stream, &ev, &profile)
|
||||
.render(ui, services.ctx.img_cache);
|
||||
.render(ui, services);
|
||||
}
|
||||
9735 => {
|
||||
if let Ok(zap) = Zap::from_receipt(ev) {
|
||||
|
@ -1,3 +1,5 @@
|
||||
use crate::link::NostrLink;
|
||||
use crate::route::{RouteServices, RouteType};
|
||||
use crate::stream_info::StreamInfo;
|
||||
use crate::theme::{NEUTRAL_500, PRIMARY};
|
||||
use crate::widgets::Avatar;
|
||||
@ -5,7 +7,6 @@ use eframe::epaint::text::TextWrapMode;
|
||||
use egui::text::LayoutJob;
|
||||
use egui::{Align, Color32, Label, Response, TextFormat, Ui};
|
||||
use nostrdb::{NdbProfile, Note};
|
||||
use notedeck::ImageCache;
|
||||
|
||||
pub struct ChatMessage<'a> {
|
||||
stream: &'a Note<'a>,
|
||||
@ -26,7 +27,7 @@ impl<'a> ChatMessage<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render(self, ui: &mut Ui, img_cache: &mut ImageCache) -> Response {
|
||||
pub fn render(self, ui: &mut Ui, services: &mut RouteServices) -> Response {
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
let mut job = LayoutJob::default();
|
||||
// TODO: avoid this somehow
|
||||
@ -48,9 +49,15 @@ impl<'a> ChatMessage<'a> {
|
||||
format.color = Color32::WHITE;
|
||||
job.append(self.ev.content(), 5.0, format.clone());
|
||||
|
||||
Avatar::from_profile(self.profile)
|
||||
if Avatar::from_profile(self.profile)
|
||||
.size(24.)
|
||||
.render(ui, img_cache);
|
||||
.render(ui, services.ctx.img_cache)
|
||||
.clicked()
|
||||
{
|
||||
services.navigate(RouteType::ProfilePage {
|
||||
link: NostrLink::profile(self.ev.pubkey()),
|
||||
})
|
||||
}
|
||||
ui.add(Label::new(job).wrap_mode(TextWrapMode::Wrap));
|
||||
|
||||
// consume reset of space
|
||||
|
@ -5,10 +5,9 @@ use crate::theme::{NEUTRAL_800, NEUTRAL_900, PRIMARY, ROUNDING_DEFAULT};
|
||||
use crate::widgets::avatar::Avatar;
|
||||
use eframe::epaint::{Rounding, Vec2};
|
||||
use egui::epaint::RectShape;
|
||||
use egui::load::TexturePoll;
|
||||
use egui::{
|
||||
vec2, Color32, CursorIcon, FontId, Label, Pos2, Rect, Response, RichText, Sense, TextWrapMode,
|
||||
Ui,
|
||||
vec2, Color32, CursorIcon, FontId, ImageSource, Label, Pos2, Rect, Response, RichText, Sense,
|
||||
TextWrapMode, Ui,
|
||||
};
|
||||
use nostrdb::Note;
|
||||
|
||||
@ -34,33 +33,27 @@ impl<'a> StreamEvent<'a> {
|
||||
let (response, painter) = ui.allocate_painter(Vec2::new(w, h), Sense::click());
|
||||
|
||||
let cover = if ui.is_rect_visible(response.rect) {
|
||||
self.event
|
||||
.image()
|
||||
.map(|p| image_from_cache(services.ctx.img_cache, ui, p))
|
||||
self.event.image().map(|p| {
|
||||
image_from_cache(services.ctx.img_cache, ui, p, Some(Vec2::new(w, h)))
|
||||
.rounding(ROUNDING_DEFAULT)
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(cover) = cover.map(|c| {
|
||||
c.rounding(Rounding::same(12.))
|
||||
.load_for_size(painter.ctx(), Vec2::new(w, h))
|
||||
}) {
|
||||
match cover {
|
||||
Ok(TexturePoll::Ready { texture }) => {
|
||||
if let Some(cover) = cover {
|
||||
painter.add(RectShape {
|
||||
rect: response.rect,
|
||||
rounding: Rounding::same(ROUNDING_DEFAULT),
|
||||
fill: Color32::WHITE,
|
||||
stroke: Default::default(),
|
||||
blur_width: 0.0,
|
||||
fill_texture_id: texture.id,
|
||||
fill_texture_id: match cover.source(ui.ctx()) {
|
||||
ImageSource::Texture(t) => t.id,
|
||||
_ => Default::default(),
|
||||
},
|
||||
uv: Rect::from_min_max(Pos2::new(0.0, 0.0), Pos2::new(1.0, 1.0)),
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
painter.rect_filled(response.rect, ROUNDING_DEFAULT, NEUTRAL_800);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
painter.rect_filled(response.rect, ROUNDING_DEFAULT, NEUTRAL_800);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user