Skip to content

Commit

Permalink
Merge commit 'b291079f20929a0ac45c7c53587eba9cb9571129'
Browse files Browse the repository at this point in the history
  • Loading branch information
mikedilger committed Nov 12, 2024
2 parents 2a3c6d1 + b291079 commit 8d5af1e
Show file tree
Hide file tree
Showing 18 changed files with 343 additions and 121 deletions.
13 changes: 12 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion gossip-bin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ appimage = [ "gossip-lib/appimage" ]

[dependencies]
bech32 = "0.11"
blurhash = { version = "0.2", features = [ "image" ] }
eframe = { git = "https://github.com/bu5hm4nn/egui", rev = "f40370c48ae2e07d2bc1d7ec33d094d29dc34e70", features = [ "persistence", "wayland", "wgpu" ] }
egui-winit = { git = "https://github.com/bu5hm4nn/egui", rev = "f40370c48ae2e07d2bc1d7ec33d094d29dc34e70", features = [ "default" ] }
egui_extras = { git = "https://github.com/bu5hm4nn/egui", rev = "f40370c48ae2e07d2bc1d7ec33d094d29dc34e70", features = [ "syntect" ] }
Expand All @@ -30,7 +31,7 @@ humansize = "2.1"
image = { version = "0.25", features = [ "png", "jpeg" ] }
lazy_static = "1.5"
memoize = "0.4"
nostr-types = { git = "https://github.com/mikedilger/nostr-types", rev = "071af42b008a8ba0eff3683fec6dda0812819e4f", features = [ "speedy" ] }
nostr-types = { git = "https://github.com/mikedilger/nostr-types", rev = "78cb912d72c6ed39123da4c32d308cfe3ae35d6d", features = [ "speedy" ] }
paste = "1.0"
qrcode = "0.14"
resvg = "0.35.0"
Expand Down
1 change: 0 additions & 1 deletion gossip-bin/src/ui/feed/note/content/media.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,6 @@ fn try_render_image(
.rounding(ui.style().noninteractive().rounding)
.show(ui, |ui| {
let text = if let Some(fm) = &file_metadata {
// FIXME do blurhash
if let Some(alt) = &fm.alt {
&format!("Loading image: {alt}")
} else if let Some(summary) = &fm.summary {
Expand Down
27 changes: 6 additions & 21 deletions gossip-bin/src/ui/feed/note/content/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,10 +231,12 @@ pub(super) fn render_hyperlink(
let privacy_issue = note.direct_message;

if let (Ok(url), Some(nurl)) = (url::Url::try_from(link), app.try_check_url(link)) {
if is_image_url(&url) {
media::show_image(app, ui, nurl, privacy_issue, note.volatile, file_metadata);
} else if is_video_url(&url) {
media::show_video(app, ui, nurl, privacy_issue, note.volatile, file_metadata);
if let Some(mimetype) = gossip_lib::media_url_mimetype(url.path()) {
if mimetype.starts_with("image/") {
media::show_image(app, ui, nurl, privacy_issue, note.volatile, file_metadata);
} else if mimetype.starts_with("video/") {
media::show_video(app, ui, nurl, privacy_issue, note.volatile, file_metadata);
}
} else {
crate::ui::widgets::break_anywhere_hyperlink_to(ui, link, link);
}
Expand Down Expand Up @@ -376,20 +378,3 @@ pub(super) fn render_unknown_reference(ui: &mut Ui, num: usize) {
.write("Gossip can't handle this kind of tag link yet.".to_owned());
}
}

fn is_image_url(url: &url::Url) -> bool {
let lower = url.path().to_lowercase();
lower.ends_with(".jpg")
|| lower.ends_with(".jpeg")
|| lower.ends_with(".png")
|| lower.ends_with(".gif")
|| lower.ends_with(".webp")
}

fn is_video_url(url: &url::Url) -> bool {
let lower = url.path().to_lowercase();
lower.ends_with(".mov")
|| lower.ends_with(".mp4")
|| lower.ends_with(".mkv")
|| lower.ends_with(".webm")
}
36 changes: 35 additions & 1 deletion gossip-bin/src/ui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,7 @@ struct GossipUi {
theme: Theme,
avatars: HashMap<PublicKey, TextureHandle>,
images: HashMap<Url, TextureHandle>,
blurs: HashMap<Url, TextureHandle>,
/// used when settings.show_media=false to explicitly show
media_show_list: HashSet<Url>,
/// used when settings.show_media=true to explicitly hide
Expand Down Expand Up @@ -738,6 +739,7 @@ impl GossipUi {
theme,
avatars: HashMap::new(),
images: HashMap::new(),
blurs: HashMap::new(),
media_show_list: HashSet::new(),
media_hide_list: HashSet::new(),
media_full_width_list: HashSet::new(),
Expand Down Expand Up @@ -1619,7 +1621,39 @@ impl GossipUi {

match GLOBALS.media.get_image(&url, volatile, file_metadata) {
MediaLoadingResult::Disabled => MediaLoadingResult::Disabled,
MediaLoadingResult::Loading => MediaLoadingResult::Loading,
MediaLoadingResult::Loading => {
if let Some(fm) = file_metadata {
let (w, h) = match fm.dim {
Some((w, h)) => (w, h),
None => (400, 400),
};
if let Some(bh) = &fm.blurhash {
if let Some(texture_handle) = self.blurs.get(&url) {
return MediaLoadingResult::Ready(texture_handle.clone());
} else {
if let Ok(rgba_image) =
blurhash::decode_image(bh, w as u32, h as u32, 1.0)
{
let current_size =
[rgba_image.width() as usize, rgba_image.height() as usize];
let pixels = rgba_image.as_flat_samples();
let color_image = ColorImage::from_rgba_unmultiplied(
current_size,
pixels.as_slice(),
);
let texture_handle = ctx.load_texture(
url.as_str().to_owned(),
color_image,
TextureOptions::default(),
);
self.blurs.insert(url, texture_handle.clone());
return MediaLoadingResult::Ready(texture_handle);
}
}
}
}
MediaLoadingResult::Loading
}
MediaLoadingResult::Ready(rgba_image) => {
let current_size = [rgba_image.width() as usize, rgba_image.height() as usize];
let pixels = rgba_image.as_flat_samples();
Expand Down
22 changes: 14 additions & 8 deletions gossip-bin/src/ui/theme/default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ impl ThemeDef for DefaultTheme {
fn neutral_800() -> Color32 {
Color32::from_rgb(0x26, 0x26, 0x26)
} // #262626
fn neutral_850() -> Color32 {
Color32::from_rgb(0x1e, 0x1e, 0x1e)
} // #1E1E1E
fn neutral_875() -> Color32 {
Color32::from_rgb(0x1b, 0x1b, 0x1b)
} // #1B1B1B
fn neutral_900() -> Color32 {
Color32::from_rgb(0x17, 0x17, 0x17)
} // #171717
Expand Down Expand Up @@ -142,17 +148,17 @@ impl ThemeDef for DefaultTheme {

fn main_content_bgcolor(dark_mode: bool) -> Color32 {
if dark_mode {
Color32::BLACK
Self::neutral_800()
} else {
Color32::WHITE
Self::neutral_50()
}
}

fn hovered_content_bgcolor(dark_mode: bool) -> Color32 {
if dark_mode {
Self::neutral_950()
Self::neutral_850()
} else {
Self::neutral_50()
Color32::WHITE
}
}

Expand Down Expand Up @@ -280,7 +286,7 @@ impl ThemeDef for DefaultTheme {

// Background colors
window_fill: Self::neutral_950(), // pulldown menus and tooltips
panel_fill: Color32::from_gray(0x29), // panel backgrounds, even-table-rows
panel_fill: Self::neutral_875(), // panel backgrounds, even-table-rows
faint_bg_color: Color32::from_gray(20), // odd-table-rows
extreme_bg_color: Color32::from_gray(45), // text input background; scrollbar background
code_bg_color: Color32::from_gray(64), // ???
Expand Down Expand Up @@ -372,7 +378,7 @@ impl ThemeDef for DefaultTheme {

// Background colors
window_fill: Self::neutral_100(), // pulldown menus and tooltips
panel_fill: Color32::from_gray(0xF4), // panel backgrounds, even-table-rows
panel_fill: Self::neutral_200(), // panel backgrounds, even-table-rows
faint_bg_color: Color32::from_gray(248), // odd-table-rows
extreme_bg_color: Color32::from_gray(246), // text input background; scrollbar background
code_bg_color: Color32::from_gray(230), // ???
Expand Down Expand Up @@ -695,9 +701,9 @@ impl ThemeDef for DefaultTheme {

fn navigation_bg_fill(dark_mode: bool) -> eframe::egui::Color32 {
if dark_mode {
Color32::from_gray(0x26)
Self::neutral_800()
} else {
Color32::from_gray(0xE1)
Self::neutral_100()
}
}

Expand Down
2 changes: 2 additions & 0 deletions gossip-bin/src/ui/theme/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,8 @@ pub trait ThemeDef: Send + Sync {
fn neutral_700() -> Color32;
fn neutral_800() -> Color32;
fn neutral_900() -> Color32;
fn neutral_850() -> Color32;
fn neutral_875() -> Color32;
fn neutral_950() -> Color32;
fn accent_dark() -> Color32;
fn accent_dark_b20() -> Color32; // overlay 20% black
Expand Down
3 changes: 2 additions & 1 deletion gossip-lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ appimage = []
[dependencies]
base64 = "0.22"
bech32 = "0.11"
blurhash = { version = "0.2", features = [ "image" ] }
dashmap = "6.0"
dirs = "5.0"
encoding_rs = "0.8"
Expand All @@ -55,7 +56,7 @@ kamadak-exif = "0.5"
lazy_static = "1.5"
linkify = "0.10"
mime = "0.3"
nostr-types = { git = "https://github.com/mikedilger/nostr-types", rev = "071af42b008a8ba0eff3683fec6dda0812819e4f", features = [ "speedy" ] }
nostr-types = { git = "https://github.com/mikedilger/nostr-types", rev = "78cb912d72c6ed39123da4c32d308cfe3ae35d6d", features = [ "speedy" ] }
parking_lot = { version = "0.12", features = [ "arc_lock", "send_guard" ] }
paste = "1.0"
rand = "0.8"
Expand Down
1 change: 1 addition & 0 deletions gossip-lib/src/feed/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,7 @@ pub fn enabled_event_kinds() -> Vec<EventKind> {
.filter(|k| {
*k == EventKind::Metadata
|| *k == EventKind::TextNote
|| *k == EventKind::Comment
//|| *k == EventKind::RecommendRelay
|| *k == EventKind::ContactList
|| ((*k == EventKind::EncryptedDirectMessage) && direct_messages)
Expand Down
4 changes: 3 additions & 1 deletion gossip-lib/src/fetcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,9 @@ impl Fetcher {
Ok(None)
}

async fn fetch(&self, url: Url, use_temp_cache: bool) {
/// This causes the fetcher to fetch the resource. After it completes, you can pick it up
/// the result using try_get()
pub async fn fetch(&self, url: Url, use_temp_cache: bool) {
// Do not fetch if offline
if GLOBALS.db().read_setting_offline() {
tracing::debug!("FETCH {url}: Failed: offline mode");
Expand Down
2 changes: 1 addition & 1 deletion gossip-lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ pub use gossip_identity::GossipIdentity;
pub mod manager;

mod media;
pub use media::{Media, MediaLoadingResult};
pub use media::{media_url_mimetype, Media, MediaLoadingResult};

mod minion;

Expand Down
78 changes: 49 additions & 29 deletions gossip-lib/src/media.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ pub struct Media {
// and hand them over. This way we can do the work that takes
// longer and the UI can do as little work as possible.
image_temp: DashMap<Url, RgbaImage>,
data_temp: DashMap<Url, Vec<u8>>,
media_pending_processing: DashSet<Url>,
failed_media: DashMap<UncheckedUrl, String>,
}
Expand All @@ -49,7 +48,6 @@ impl Media {
pub(crate) fn new() -> Media {
Media {
image_temp: DashMap::new(),
data_temp: DashMap::new(),
media_pending_processing: DashSet::new(),
failed_media: DashMap::new(),
}
Expand Down Expand Up @@ -164,30 +162,6 @@ impl Media {
return MediaLoadingResult::Failed(s.to_string());
}

// If we have it, hand it over (we won't need a copy anymore)
if let Some(th) = self.data_temp.remove(url) {
// Verify metadata hash
if let Some(file_metadata) = &file_metadata {
if let Some(x) = &file_metadata.x {
use sha2::{Digest, Sha256};
let mut hasher = Sha256::new();
hasher.update(&th.1);
let sha256hash = hasher.finalize();
let hash_str = hex::encode(sha256hash);
if hash_str != *x {
if url.as_str() == "https://mikedilger.com/bs.png" {
tracing::error!("Hash Mismatch Computed");
}
let error = "Hash Mismatch".to_string();
self.set_has_failed(&url.to_unchecked_url(), error.clone());
return MediaLoadingResult::Failed(error);
}
}
}

return MediaLoadingResult::Ready(th.1);
}

// Do not fetch if disabled
if !GLOBALS.db().read_setting_load_media() {
return MediaLoadingResult::Disabled;
Expand All @@ -200,9 +174,26 @@ impl Media {
) {
Ok(None) => MediaLoadingResult::Loading,
Ok(Some(bytes)) => {
// FIXME: Why not just return it right here?
self.data_temp.insert(url.clone(), bytes);
MediaLoadingResult::Loading
// Verify metadata hash
if let Some(file_metadata) = &file_metadata {
if let Some(x) = &file_metadata.x {
use sha2::{Digest, Sha256};
let mut hasher = Sha256::new();
hasher.update(&bytes);
let sha256hash = hasher.finalize();
let hash_str = hex::encode(sha256hash);
if hash_str != *x {
if url.as_str() == "https://mikedilger.com/bs.png" {
tracing::error!("Hash Mismatch Computed");
}
let error = "Hash Mismatch".to_string();
self.set_has_failed(&url.to_unchecked_url(), error.clone());
return MediaLoadingResult::Failed(error);
}
}
}

MediaLoadingResult::Ready(bytes)
}
Err(e) => {
let error = format!("{e}");
Expand Down Expand Up @@ -364,3 +355,32 @@ fn round_image(image: &mut RgbaImage) {
}
}
}

pub fn media_url_mimetype(s: &str) -> Option<&'static str> {
let lower = s.to_lowercase();
if lower.ends_with(".jpg") || lower.ends_with(".jpeg") {
Some("image/jpeg")
} else if lower.ends_with(".png") {
Some("image/png")
} else if lower.ends_with(".gif") {
Some("image/gif")
} else if lower.ends_with(".webp") {
Some("image/webp")
} else if lower.ends_with(".mov") {
Some("video/quicktime")
} else if lower.ends_with(".mp4") {
Some("video/mp4")
} else if lower.ends_with(".webm") {
Some("video/webm")
} else if lower.ends_with(".mkv") {
Some("video/x-matroska")
} else if lower.ends_with(".avi") {
Some("video/x-msvideo")
} else if lower.ends_with(".wmv") {
Some("video/x-ms-wmv")
} else if lower.ends_with(".3gp") {
Some("video/3gpp")
} else {
None
}
}
Loading

0 comments on commit 8d5af1e

Please sign in to comment.