black screen dead once and for all. now its just loss of input for a bit on first load

This commit is contained in:
RabbyDevs 2025-05-09 21:38:03 +03:00
parent 4c87d13eb8
commit 2e3c44b421
7 changed files with 107 additions and 115 deletions

19
Cargo.lock generated
View file

@ -816,12 +816,6 @@ dependencies = [
"simd-adler32", "simd-adler32",
] ]
[[package]]
name = "file-format"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7ef3d5e8ae27277c8285ac43ed153158178ef0f79567f32024ca8140a0c7cd8"
[[package]] [[package]]
name = "flate2" name = "flate2"
version = "1.1.1" version = "1.1.1"
@ -1627,13 +1621,11 @@ dependencies = [
"glib", "glib",
"gstreamer", "gstreamer",
"gstreamer-app", "gstreamer-app",
"gstreamer-base",
"iced", "iced",
"iced_wgpu", "iced_wgpu",
"log", "log",
"thiserror 2.0.12", "thiserror 2.0.12",
"url", "url",
"yuvutils-rs",
] ]
[[package]] [[package]]
@ -3102,7 +3094,6 @@ name = "reversed-rooms-launcher"
version = "0.5.0" version = "0.5.0"
dependencies = [ dependencies = [
"directories", "directories",
"file-format",
"iced", "iced",
"iced_video_player", "iced_video_player",
"image", "image",
@ -3111,7 +3102,6 @@ dependencies = [
"serde_json", "serde_json",
"strum 0.27.1", "strum 0.27.1",
"strum_macros 0.27.1", "strum_macros 0.27.1",
"tempfile",
"url", "url",
] ]
@ -5051,15 +5041,6 @@ dependencies = [
"synstructure", "synstructure",
] ]
[[package]]
name = "yuvutils-rs"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b699b6503cd14c70b258eaffedd7ada5a781ea23206eeb9066736b99ba37af1"
dependencies = [
"num-traits",
]
[[package]] [[package]]
name = "zeno" name = "zeno"
version = "0.3.2" version = "0.3.2"

View file

@ -10,10 +10,8 @@ iced = { git = "https://github.com/RabbyDevs/iced.git", features = ["tokio", "wg
image = "0.25.6" image = "0.25.6"
serde = { version = "1.0.219", features = ["serde_derive"] } serde = { version = "1.0.219", features = ["serde_derive"] }
serde_json = "1.0.140" serde_json = "1.0.140"
tempfile = "3.19.1"
iced_video_player = {path = "./iced_video_player"} iced_video_player = {path = "./iced_video_player"}
url = "2.5.4" url = "2.5.4"
file-format = "0.26.0"
strum = "0.27.1" strum = "0.27.1"
strum_macros = "0.27.1" strum_macros = "0.27.1"
reqwest = { version = "0.12.15", features = [ reqwest = { version = "0.12.15", features = [

View file

@ -20,12 +20,12 @@ iced = { git = "https://github.com/RabbyDevs/iced.git", features = ["wgpu", "ima
iced_wgpu = { git = "https://github.com/RabbyDevs/iced.git" } iced_wgpu = { git = "https://github.com/RabbyDevs/iced.git" }
gstreamer = "0.23.5" gstreamer = "0.23.5"
gstreamer-app = "0.23.5" # appsink gstreamer-app = "0.23.5" # appsink
gstreamer-base = "0.23.5" # basesrc # gstreamer-base = "0.23.5" # basesrc
glib = "0.20.9" # gobject traits and error type glib = "0.20.9" # gobject traits and error type
log = "0.4.27" log = "0.4.27"
thiserror = "2.0.12" thiserror = "2.0.12"
url = "2.5.4" # media uri url = "2.5.4" # media uri
yuvutils-rs = "0.8.2" # yuvutils-rs = "0.8.2"
[package.metadata.nix] [package.metadata.nix]
systems = ["x86_64-linux"] systems = ["x86_64-linux"]

View file

@ -336,7 +336,7 @@ impl Video {
frame, frame,
upload_frame, upload_frame,
looping: false, looping: true,
is_eos: false, is_eos: false,
restart_stream: false, restart_stream: false,
}))) })))

View file

@ -156,6 +156,7 @@ async fn refresh_kuro_install(proj_dirs: &ProjectDirs, client: &Client) -> Resul
update_file_if_needed(data_dir, client, &bg_information.background_file, "background").await?; update_file_if_needed(data_dir, client, &bg_information.background_file, "background").await?;
update_file_if_needed(data_dir, client, &bg_information.slogan, "splash").await?; update_file_if_needed(data_dir, client, &bg_information.slogan, "splash").await?;
update_file_if_needed(data_dir, client, &bg_information.first_frame_image, "first_frame_image").await?;
Ok(()) Ok(())
} }

View file

@ -96,9 +96,15 @@ enum Launcher {
Loaded(Box<State>), Loaded(Box<State>),
} }
#[derive(Debug)]
struct VideoBackground {
video: Option<Video>,
first_frame: DynamicImage
}
#[derive(Debug)] #[derive(Debug)]
enum LauncherBackground { enum LauncherBackground {
Video(Video), Video(VideoBackground),
Image(DynamicImage), Image(DynamicImage),
} }
@ -117,8 +123,14 @@ impl State {
fn get_background_element(&self) -> Option<Element<Message>> { fn get_background_element(&self) -> Option<Element<Message>> {
if let Some(background) = &self.background { if let Some(background) = &self.background {
match background { match background {
LauncherBackground::Video(video) => Some(VideoPlayer::new(video).into()), LauncherBackground::Video(video_object) => {
LauncherBackground::Image(img) => Some(image(image::Handle::from_rgba(img.width(), img.height(), img.to_rgba8().into_raw())).into()), if let Some(video) = &video_object.video {
Some(VideoPlayer::new(video).into())
} else {
Some(image(video_object.first_frame.clone().into_handle()).into())
}
},
LauncherBackground::Image(img) => Some(image(img.clone().into_handle()).into()),
} }
} else { } else {
None None
@ -252,6 +264,7 @@ enum Message {
LoadIcons(HashMap<PossibleGames, DynamicImage>), LoadIcons(HashMap<PossibleGames, DynamicImage>),
LoadSplashes(HashMap<PossibleGames, DynamicImage>), LoadSplashes(HashMap<PossibleGames, DynamicImage>),
LoadBackground(()), LoadBackground(()),
LoadVideo(()),
RefreshInstall(Result<(), String>), RefreshInstall(Result<(), String>),
GameSelected(PossibleGames), GameSelected(PossibleGames),
Close, Close,
@ -328,12 +341,33 @@ impl Launcher {
Task::none() Task::none()
}, },
Message::LoadBackground(()) => { Message::LoadBackground(()) => {
state.background = Some(get_game_background(&state.selected_game));
let (width, height) = state.selected_game.get_game_preferred_size(); let (width, height) = state.selected_game.get_game_preferred_size();
window::get_latest().and_then(move |id: window::Id| { let task = window::get_latest().and_then(move |id: window::Id| {
window::resize(id, Size { width: width as f32, height: height as f32 }) window::resize(id, Size { width: width as f32, height: height as f32 })
}) });
if let Ok(background) = get_game_background(&state.selected_game, false) {
if let LauncherBackground::Video(_) = background {
state.background = Some(background);
return task.chain(Task::perform(empty(), Message::LoadVideo));
}
state.background = Some(background);
} else {
state.background = None
}
task
},
Message::LoadVideo(()) => {
if let Ok(background) = get_game_background(&state.selected_game, true) {
state.background = Some(background);
} else {
state.background = None
}
Task::none()
}, },
Message::LoadIcons(icons) => { Message::LoadIcons(icons) => {
state.icon_images = icons; state.icon_images = icons;
@ -344,12 +378,22 @@ impl Launcher {
}, },
Message::GameSelected(game) => { Message::GameSelected(game) => {
state.selected_game = game; state.selected_game = game;
state.background = Some(get_game_background(&state.selected_game));
let (width, height) = state.selected_game.get_game_preferred_size(); let (width, height) = state.selected_game.get_game_preferred_size();
window::get_latest().and_then(move |id: window::Id| { let task = window::get_latest().and_then(move |id: window::Id| {
window::resize(id, Size { width: width as f32, height: height as f32 }) window::resize(id, Size { width: width as f32, height: height as f32 })
}) });
if let Ok(background) = get_game_background(&state.selected_game, false) {
if let LauncherBackground::Video(_) = background {
state.background = Some(background);
return task.chain(Task::perform(empty(), Message::LoadVideo));
}
state.background = Some(background)
} else {
state.background = None
}
task
}, },
} }
} }

View file

@ -1,47 +1,60 @@
use std::{fs::DirEntry, io::{Cursor, Write}, sync::Arc}; use std::{io::Cursor, path::PathBuf};
use directories::ProjectDirs; use directories::ProjectDirs;
use file_format::FileFormat;
use iced_video_player::Video; use iced_video_player::Video;
use ::image::{DynamicImage, ImageReader}; use ::image::{DynamicImage, ImageReader};
use iced::{gradient, widget::container, Color}; use iced::{gradient, widget::container, Color};
use tempfile::NamedTempFile;
use crate::{LauncherBackground, PossibleGames}; use crate::{LauncherBackground, PossibleGames, VideoBackground};
use super::img_utils::round_image; use super::img_utils::round_image;
pub fn get_game_background(game: &PossibleGames) -> LauncherBackground { pub fn get_game_background(game: &PossibleGames, make_video: bool) -> Result<LauncherBackground, std::io::Error> {
let proj_dirs = ProjectDirs::from("com", "RabbyDevs", "rr-launcher").unwrap(); let proj_dirs = ProjectDirs::from("com", "RabbyDevs", "rr-launcher").unwrap();
let background_bytes = get_background_file(&proj_dirs, game).unwrap(); let background_path = get_asset_file(&proj_dirs, game, "background")?;
let data = Arc::new(background_bytes);
let file_format = FileFormat::from_bytes(&*data);
if file_format.extension() == "mp4" { if background_path.extension().unwrap().to_str().unwrap() == "mp4" {
let mut temp_file = NamedTempFile::new().unwrap(); let first_frame_path = get_asset_file(&proj_dirs, game, "first_frame_image")?;
temp_file.write_all(&data).unwrap(); let first_frame = ImageReader::new(Cursor::new(std::fs::read(first_frame_path)?))
let temp_path = temp_file.path().to_str().unwrap().to_string();
match Video::new(&url::Url::from_file_path(temp_path).unwrap()) {
Ok(mut video) => {
video.set_looping(true);
LauncherBackground::Video(video)
},
Err(err) => {
panic!("{:#?}", err)
},
}
} else {
let img = ImageReader::new(Cursor::new(&*data))
.with_guessed_format() .with_guessed_format()
.unwrap() .unwrap()
.decode() .decode()
.unwrap(); .unwrap();
LauncherBackground::Image(img)
if !make_video {
return Ok(LauncherBackground::Video(VideoBackground {
first_frame,
video: None
}));
}
match Video::new(&url::Url::from_file_path(background_path).unwrap()) {
Ok(video) => {
let video_object = VideoBackground {
first_frame,
video: Some(video)
};
Ok(LauncherBackground::Video(video_object))
},
Err(err) => {
eprintln!("{:#?}", err);
Ok(LauncherBackground::Video(VideoBackground {
first_frame,
video: None
}))
},
}
} else {
let img = ImageReader::new(Cursor::new(std::fs::read(background_path)?))
.with_guessed_format()
.unwrap()
.decode()
.unwrap();
Ok(LauncherBackground::Image(img))
} }
} }
fn get_background_file(proj_dirs: &ProjectDirs, game: &PossibleGames) -> Result<Vec<u8>, std::io::Error> { fn get_asset_file(proj_dirs: &ProjectDirs, game: &PossibleGames, identifier: &str) -> Result<PathBuf, std::io::Error> {
let game_dir = match game { let game_dir = match game {
PossibleGames::WutheringWaves => proj_dirs.data_dir().join("kuro/wuwa"), PossibleGames::WutheringWaves => proj_dirs.data_dir().join("kuro/wuwa"),
PossibleGames::ZenlessZoneZero => proj_dirs.data_dir().join("hoyoverse/zzz"), PossibleGames::ZenlessZoneZero => proj_dirs.data_dir().join("hoyoverse/zzz"),
@ -62,8 +75,8 @@ fn get_background_file(proj_dirs: &ProjectDirs, game: &PossibleGames) -> Result<
let entry = entry?; let entry = entry?;
let file_name = entry.file_name().into_string().unwrap_or_default(); let file_name = entry.file_name().into_string().unwrap_or_default();
if file_name.starts_with("background_") { if file_name.starts_with(format!("{}_", identifier).as_str()) {
return std::fs::read(entry.path()); return Ok(entry.path());
} }
} }
@ -75,9 +88,10 @@ fn get_background_file(proj_dirs: &ProjectDirs, game: &PossibleGames) -> Result<
pub fn get_game_splash_dynamic_image(game: &PossibleGames) -> Option<DynamicImage> { pub fn get_game_splash_dynamic_image(game: &PossibleGames) -> Option<DynamicImage> {
let proj_dirs = ProjectDirs::from("com", "RabbyDevs", "rr-launcher").unwrap(); let proj_dirs = ProjectDirs::from("com", "RabbyDevs", "rr-launcher").unwrap();
let file_data = get_splash_file(&proj_dirs, game); let file_path = get_asset_file(&proj_dirs, game, "splash");
if let Some(data) = file_data {
let data_cursor = Cursor::new(data); if let Ok(path) = file_path {
let data_cursor = Cursor::new(std::fs::read(path).unwrap());
Some(ImageReader::new(data_cursor) Some(ImageReader::new(data_cursor)
.with_guessed_format() .with_guessed_format()
@ -91,9 +105,9 @@ pub fn get_game_icon_dynamic_image(game: &PossibleGames) -> DynamicImage {
let proj_dirs = ProjectDirs::from("com", "RabbyDevs", "rr-launcher").unwrap(); let proj_dirs = ProjectDirs::from("com", "RabbyDevs", "rr-launcher").unwrap();
let file_data: &[u8] = match game { let file_data: &[u8] = match game {
PossibleGames::WutheringWaves => include_bytes!("../../resources/wutheringwaves-icon.png"), PossibleGames::WutheringWaves => include_bytes!("../../resources/wutheringwaves-icon.png"),
PossibleGames::ZenlessZoneZero => &get_hoyo_game_icon_file(&proj_dirs, game), PossibleGames::ZenlessZoneZero => &std::fs::read(get_asset_file(&proj_dirs, game, "icon").unwrap()).unwrap(),
PossibleGames::HonkaiStarRail => &get_hoyo_game_icon_file(&proj_dirs, game), PossibleGames::HonkaiStarRail => &std::fs::read(get_asset_file(&proj_dirs, game, "icon").unwrap()).unwrap(),
PossibleGames::GenshinImpact => &get_hoyo_game_icon_file(&proj_dirs, game), PossibleGames::GenshinImpact => &std::fs::read(get_asset_file(&proj_dirs, game, "icon").unwrap()).unwrap(),
}; };
let data_cursor = Cursor::new(file_data); let data_cursor = Cursor::new(file_data);
@ -109,52 +123,6 @@ pub fn get_game_icon_dynamic_image(game: &PossibleGames) -> DynamicImage {
.unwrap() .unwrap()
} }
fn get_hoyo_game_icon_file(proj_dirs: &ProjectDirs, game: &PossibleGames) -> Vec<u8> {
let game_abbrevation = match game {
PossibleGames::ZenlessZoneZero => "zzz",
PossibleGames::HonkaiStarRail => "hsr",
PossibleGames::GenshinImpact => "gi",
_ => panic!("Wuwa inputted in hoyo only func")
};
let data_dir = proj_dirs.data_dir().join(format!("hoyoverse/{}", game_abbrevation));
let icon: Option<DirEntry> = {
let mut icon = None;
for path in data_dir.read_dir().unwrap() {
let path = path.unwrap();
if path.file_name().into_string().unwrap().starts_with("icon_") {
icon = Some(path);
}
}
icon
};
std::fs::read(icon.expect("installation went wrong.").path()).unwrap()
}
fn get_splash_file(proj_dirs: &ProjectDirs, game: &PossibleGames) -> Option<Vec<u8>> {
let game_path = match game {
PossibleGames::ZenlessZoneZero => "hoyoverse/zzz",
PossibleGames::HonkaiStarRail => "hoyoverse/hsr",
PossibleGames::GenshinImpact => "hoyoverse/gi",
PossibleGames::WutheringWaves => "kuro/wuwa"
};
let data_dir = proj_dirs.data_dir().join(game_path);
let icon: Option<DirEntry> = {
let mut icon = None;
for path in data_dir.read_dir().unwrap() {
let path = path.unwrap();
if path.file_name().into_string().unwrap().starts_with("splash_") {
icon = Some(path);
}
}
icon
};
icon.map(|icon| std::fs::read(icon.path()).unwrap())
}
fn rad(deg: f32) -> f32 { fn rad(deg: f32) -> f32 {
deg * std::f32::consts::PI / 180.0 deg * std::f32::consts::PI / 180.0
} }