From beedcf8ce9db37f835710bd65bc6d5bcd7b3d4bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Lidwin?= Date: Tue, 23 Jul 2024 14:44:41 +0200 Subject: [PATCH 1/5] play videos on the background added ffmpeg as a dependency. Videos are loaded from the URL at the moment, but nothing stops us from downloading them. There is a lot we can still improve here... --- maxima-lib/src/core/service_layer.rs | 2 +- maxima-ui/Cargo.toml | 1 + maxima-ui/src/bridge/game_images.rs | 74 +++++++++- maxima-ui/src/bridge_processor.rs | 6 +- maxima-ui/src/bridge_thread.rs | 2 +- maxima-ui/src/fs/image_loader.rs | 10 +- maxima-ui/src/main.rs | 47 +++++- maxima-ui/src/renderers/media_player.rs | 184 ++++++++++++++++++++++++ maxima-ui/src/renderers/mod.rs | 1 + maxima-ui/src/ui_image.rs | 6 +- maxima-ui/src/views/downloads_view.rs | 2 +- maxima-ui/src/views/game_view.rs | 2 +- 12 files changed, 315 insertions(+), 22 deletions(-) create mode 100644 maxima-ui/src/renderers/media_player.rs diff --git a/maxima-lib/src/core/service_layer.rs b/maxima-lib/src/core/service_layer.rs index 08f6287..c7cadd6 100644 --- a/maxima-lib/src/core/service_layer.rs +++ b/maxima-lib/src/core/service_layer.rs @@ -640,7 +640,7 @@ service_layer_type!(Asset, { }); service_layer_type!(GameHub, { - background_video: ServiceAsset, + background_video: Option, hero_background: ServiceImageRendition, }); diff --git a/maxima-ui/Cargo.toml b/maxima-ui/Cargo.toml index 6140141..dce8cab 100644 --- a/maxima-ui/Cargo.toml +++ b/maxima-ui/Cargo.toml @@ -26,6 +26,7 @@ strum_macros = "0.25.3" sys-locale = "0.3.1" humansize = { version = "2.0.0", features = ["no_alloc"] } fuzzy-matcher = "*" +ffmpeg-next = "7.0.2" [target.'cfg(windows)'.dependencies] is_elevated = "0.1.2" diff --git a/maxima-ui/src/bridge/game_images.rs b/maxima-ui/src/bridge/game_images.rs index 205750e..1cb864a 100644 --- a/maxima-ui/src/bridge/game_images.rs +++ b/maxima-ui/src/bridge/game_images.rs @@ -3,7 +3,8 @@ use egui::Context; use log::{debug, error}; use maxima::{ core::{ - service_layer::{ServiceGame, ServiceGameImagesRequestBuilder, SERVICE_REQUEST_GAMEIMAGES}, + service_layer::{ServiceGame, ServiceGameHubCollection, + ServiceGameImagesRequestBuilder, ServiceHeroBackgroundImageRequestBuilder, SERVICE_REQUEST_GAMEIMAGES, SERVICE_REQUEST_GETHEROBACKGROUNDIMAGE}, LockedMaxima, }, util::native::maxima_dir, @@ -19,7 +20,33 @@ use crate::{ GameUIImages, }; -async fn get_preferred_hero_image(images: &Option) -> Option { +fn get_preferred_bg_hero(heroes: &Option) -> Option { + if heroes.is_none() { + return None + } + let bg = heroes.as_ref().unwrap().items().get(0); + + if bg.is_none() { + return None; + } + let bg = bg.as_ref().unwrap().hero_background(); + + if let Some(img) = bg.aspect_10x3_image() { + return Some(img.path().clone()); + } + + if let Some(img) = bg.aspect_2x1_image() { + return Some(img.path().clone()); + } + + if let Some(img) = bg.aspect_16x9_image() { + return Some(img.path().clone()); + } + + None +} + +fn get_preferred_hero(images: &Option) -> Option { if images.is_none() { return None; } @@ -38,7 +65,6 @@ async fn get_preferred_hero_image(images: &Option) -> Option Result<()> { debug!("got request to load game images for {:?}", slug); + let background_hero = maxima_dir() + .unwrap() + .join("cache/ui/images/") + .join(&slug) + .join("background.jpg"); let game_hero = maxima_dir() .unwrap() .join("cache/ui/images/") @@ -79,6 +110,7 @@ pub async fn game_images_request( .join("logo.png"); let has_hero = fs::metadata(&game_hero).is_ok(); let has_logo = fs::metadata(&game_logo).is_ok(); + let has_background = fs::metadata(&background_hero).is_ok(); let images: Option = // TODO: make it a result if !has_hero || !has_logo { //game hasn't been cached yet let maxima = maxima_arc.lock().await; @@ -90,11 +122,26 @@ pub async fn game_images_request( .locale(maxima.locale().short_str().to_owned()) .build()?).await? } else { None }; + let hero_images: Option = { + let maxima = maxima_arc.lock().await; + maxima.service_layer().request(SERVICE_REQUEST_GETHEROBACKGROUNDIMAGE, + ServiceHeroBackgroundImageRequestBuilder::default() + .game_slug(slug.clone()) + .locale(maxima.locale().short_str().to_owned()) + .build()?).await? + }; + + let bg_hero_url: Option = if has_background{ + debug!("Using cached bg hero image for {:?}", slug); + None + } else { + get_preferred_bg_hero(&hero_images) + }; let hero_url: Option = if has_hero { debug!("Using cached hero image for {:?}", slug); None } else { - get_preferred_hero_image(&images).await + get_preferred_hero(&images) }; let logo_url: Option = if has_logo { debug!("Using cached logo for {:?}", slug); @@ -119,9 +166,22 @@ pub async fn game_images_request( if has_logo { None } else { logo_url }, ctx.clone(), ); + + let bg = UIImage::load( + slug.clone(), + GameImageType::Background, + if has_background { None } else { bg_hero_url }, + ctx.clone() + ); + + let hero_vid = match hero_images { + Some(imgs) => if let Some(vid) = imgs.items().get(0).unwrap().background_video() { + vid.url().clone() + } else { None }, + None => None, + }; - let hero = hero.await; - let logo = logo.await; + let (hero, logo, bg) = tokio::join!(hero, logo, bg); if hero.is_ok() { let res = MaximaLibResponse::GameUIImagesResponse(InteractThreadGameUIImagesResponse { @@ -133,6 +193,8 @@ pub async fn game_images_request( None }, hero: hero.expect("no hero").into(), + hero_bg: bg.expect("no bg").into(), + hero_video: hero_vid }), }); debug!("sending {}'s GameUIImages back to UI", &slug); diff --git a/maxima-ui/src/bridge_processor.rs b/maxima-ui/src/bridge_processor.rs index 3efe6ae..6ea570d 100644 --- a/maxima-ui/src/bridge_processor.rs +++ b/maxima-ui/src/bridge_processor.rs @@ -84,10 +84,8 @@ pub fn frontend_processor(app: &mut MaximaEguiApp, ctx: &egui::Context) { } debug!("setting images for {:?}", game.slug); - game.images = GameUIImagesWrapper::Available(GameUIImages { - hero: response.hero.to_owned(), - logo: response.logo.to_owned(), - }); + game.images = GameUIImagesWrapper::Available(response); + break } } bridge_thread::MaximaLibResponse::UserAvatarResponse(res) => { diff --git a/maxima-ui/src/bridge_thread.rs b/maxima-ui/src/bridge_thread.rs index b63112a..cbb56b4 100644 --- a/maxima-ui/src/bridge_thread.rs +++ b/maxima-ui/src/bridge_thread.rs @@ -9,7 +9,7 @@ use std::{ }, time::{Duration, SystemTime} }; -use maxima::{content::manager::{ContentManager, QueuedGameBuilder}, core::{dip::{DiPManifest, DIP_RELATIVE_PATH}, service_layer::ServicePlayer, LockedMaxima, Maxima, MaximaOptionsBuilder}, util::registry::{check_registry_validity, set_up_registry}}; +use maxima::{content::manager::{ContentManager, QueuedGameBuilder}, core::{dip::{DiPManifest, DIP_RELATIVE_PATH}, service_layer::{ServicePlayer, ServiceGameHubCollection}, LockedMaxima, Maxima, MaximaOptionsBuilder}, util::registry::{check_registry_validity, set_up_registry}}; use crate::{ bridge::{ game_details::game_details_request, diff --git a/maxima-ui/src/fs/image_loader.rs b/maxima-ui/src/fs/image_loader.rs index 58d1424..af33fe0 100644 --- a/maxima-ui/src/fs/image_loader.rs +++ b/maxima-ui/src/fs/image_loader.rs @@ -3,18 +3,18 @@ use egui::ColorImage; use egui_extras::RetainedImage; use image::{io::Reader as ImageReader, DynamicImage}; use log::{debug, error}; -use std::io::Read; +use tokio::io::AsyncReadExt; pub struct ImageLoader {} impl ImageLoader { - pub fn load_from_fs(path: &str) -> Result { + pub async fn load_from_fs(path: &str) -> Result { debug!("Loading image {:?}", path); let img = ImageReader::open(path); if img.is_err() { error!("Failed to open \"{}\"!", path); // TODO: fix this - return Self::load_from_fs("./res/placeholder.png"); // probably a really shitty idea but i don't want to embed the png, or make a system to return pointers to the texture, suffer. + return Box::pin(Self::load_from_fs("./res/placeholder.png")).await; // probably a really shitty idea but i don't want to embed the png, or make a system to return pointers to the texture, suffer. } let img_decoded = img?.with_guessed_format(); @@ -23,9 +23,9 @@ impl ImageLoader { // this is incredibly fucking stupid // i should've never done this, i should've found a proper method to detect things // but here we are. if it works, it works, and i sure as hell don't want to fix it. - let mut f = std::fs::File::open(path)?; + let mut f = tokio::fs::File::open(path).await?; let mut buffer = String::new(); - f.read_to_string(&mut buffer)?; + f.read_to_string(&mut buffer).await?; let yeah = RetainedImage::from_svg_str(format!("{:?}_Retained_Decoded", path), &buffer); if yeah.is_err() { bail!("Failed to read SVG from \"{}\"!", path); diff --git a/maxima-ui/src/main.rs b/maxima-ui/src/main.rs index d73a6ca..e2679f3 100644 --- a/maxima-ui/src/main.rs +++ b/maxima-ui/src/main.rs @@ -1,4 +1,5 @@ #![feature(slice_pattern)] +extern crate ffmpeg_next as ffmpeg; use anyhow::bail; use clap::{arg, command, Parser}; @@ -7,6 +8,7 @@ use egui::style::{ScrollStyle, Spacing}; use egui::Style; use log::{error, warn}; use maxima::core::library::OwnedOffer; +use renderers::media_player::{Player, State}; use views::downloads_view::{downloads_view, QueuedDownload}; use views::undefinied_view::coming_soon_view; use std::collections::HashMap; @@ -83,6 +85,7 @@ struct Args { #[tokio::main] async fn main() { init_logger(); + ffmpeg::init().unwrap(); let mut args = Args::parse(); if !cfg!(debug_assertions) { @@ -174,6 +177,8 @@ pub struct GameInstalledModsInfo {} pub struct GameUIImages { /// YOOOOO hero: Arc, + hero_bg: Arc, + hero_video: Option, /// The stylized logo of the game, some games don't have this! logo: Option>, } @@ -308,6 +313,8 @@ pub struct MaximaEguiApp { game_view_bg_renderer: Option, /// Renderer for the app's background app_bg_renderer: Option, + /// Renderer for the app's background videos + app_bg_media_player: Option, /// Translations locale: TranslationManager, /// If a core thread has crashed and made the UI unstable @@ -473,6 +480,7 @@ impl MaximaEguiApp { modal: None, game_view_bg_renderer: GameViewBgRenderer::new(cc), app_bg_renderer: AppBgRenderer::new(cc), + app_bg_media_player: Some(Player::new(&cc.egui_ctx)), locale: TranslationManager::new() .expect("Could not load translation file"), critical_bg_thread_crashed: false, @@ -649,6 +657,7 @@ impl eframe::App for MaximaEguiApp { let has_game_img = self.backend_state == BackendStallState::BingChilling && self.games.len() > 0; let gaming = self.page_view == PageType::Games && has_game_img; let how_game: f32 = ctx.animate_bool(egui::Id::new("MainAppBackgroundGamePageFadeBool"), gaming); + if has_game_img { if self.game_sel.is_empty() && self.games.len() > 0 { @@ -656,17 +665,53 @@ impl eframe::App for MaximaEguiApp { self.game_sel = key.clone() } } + match &self.games[&self.game_sel].images { GameUIImagesWrapper::Unloaded | GameUIImagesWrapper::Loading => { render.draw(ui, fullrect, fullrect.size(), TextureId::Managed(1), 0.0); } GameUIImagesWrapper::Available(images) => { - render.draw(ui, fullrect, images.hero.size, images.hero.renderable, how_game); + match &mut self.app_bg_media_player { + Some(player) => { + if let Some(video) = &images.hero_video { + match player.state() { + State::Paused => { + if gaming { + player.unpause(); + } + render.draw(ui, fullrect, player.size(), player.texture(), how_game); + } + State::Playing => { + if !gaming { + player.pause(); + } + if player.ready_to_show() { + render.draw(ui, fullrect, player.size(), player.texture(), how_game); + } else { + render.draw(ui, fullrect, fullrect.size(), TextureId::Managed(1), 0.0); + } + }, + State::EndOfFile | State::Stopped => { + render.draw(ui, fullrect, images.hero_bg.size, images.hero_bg.renderable, how_game); + } + } + player.start(&video); + } + else { + player.stop(); + render.draw(ui, fullrect, images.hero_bg.size, images.hero_bg.renderable, how_game); + } + }, + None => render.draw(ui, fullrect, images.hero_bg.size, images.hero_bg.renderable, how_game) + + } } } + } else { render.draw(ui, fullrect, fullrect.size(), TextureId::Managed(1), 0.0); } + } let app_rect = ui.available_rect_before_wrap().clone(); match self.backend_state { diff --git a/maxima-ui/src/renderers/media_player.rs b/maxima-ui/src/renderers/media_player.rs new file mode 100644 index 0000000..bed9a45 --- /dev/null +++ b/maxima-ui/src/renderers/media_player.rs @@ -0,0 +1,184 @@ +use eframe::glow; +use anyhow::Result; +use egui::{Color32, ColorImage, TextureHandle, TextureId, Vec2}; +use ffmpeg::media::Type; +use ffmpeg::util::frame::Video; +use ffmpeg::software::scaling::{Context, Flags}; +use ffmpeg::format::{input, context::Input}; +use log::error; +use std::thread; +use std::sync::Arc; +use egui::mutex::Mutex; + + +#[derive(Debug, Clone, Copy)] +pub enum State { + Stopped, + Paused, + Playing, + EndOfFile +} + +pub struct Player { + current_location: String, + texture: TextureHandle, + ctx: egui::Context, + play_thread: Option>>, + state: Arc>, + thread_stop: Arc>, + ready_to_show: Arc>, + video_res: Arc>, +} + +impl Player { + pub fn new(ctx: &egui::Context) -> Self { + let texture_handle = ctx.load_texture("video", ColorImage::example(), Default::default()); + + Self { + current_location: String::new(), + ctx: ctx.clone(), + texture: texture_handle, + play_thread: None, + state: Arc::new(Mutex::new(State::Stopped)), + thread_stop: Arc::new(Mutex::new(false)), + ready_to_show: Arc::new(Mutex::new(false)), + video_res: Arc::new(Mutex::new(Vec2::new(0.0, 0.0))) + } + } + + pub fn texture(&self) -> TextureId { + self.texture.id() + } + + pub fn size(&self) -> Vec2 { + (*self.video_res.lock()).clone() + } + + pub fn ready_to_show(&self) -> bool { + *self.ready_to_show.lock() + } + + pub fn state(&self) -> State { + *self.state.lock() + } + + pub fn start(&mut self, location: &str) { + if location != self.current_location { + self.stop(); + } + if self.play_thread.is_some() { + return + } + + let location = location.to_owned(); + self.current_location = location.clone(); + + *self.thread_stop.lock() = false; + *self.state.lock() = State::Playing; + let ctx = self.ctx.clone(); + let thread_stop = self.thread_stop.clone(); + let state = self.state.clone(); + let mut texture = self.texture.clone(); + let ready_to_show = self.ready_to_show.clone(); + let video_res = self.video_res.clone(); + self.play_thread = Some(thread::spawn(move || { + let mut input = input(&location)?; + let stream = input.streams().best(Type::Video).ok_or(ffmpeg::Error::StreamNotFound)?; + let video_index = stream.index(); + let ctx_decoder = ffmpeg::codec::context::Context::from_parameters(stream.parameters())?; + let mut decoder = ctx_decoder.decoder().video()?; + let frame_rate = stream.avg_frame_rate().numerator() as f64 / stream.avg_frame_rate().denominator() as f64; + let wait_duration = std::time::Duration::from_millis((1000.0 / frame_rate) as u64); + *video_res.lock() = Vec2::new(decoder.width() as f32, decoder.height() as f32); + let mut scaler = Context::get(decoder.format(), decoder.width(), decoder.height(), ffmpeg::format::Pixel::RGB24, decoder.width(), decoder.height(), Flags::BILINEAR).unwrap(); + loop { + if matches!(*state.lock(), State::Paused) { + std::thread::sleep(std::time::Duration::from_millis(200)); + continue; + } + let mut frame = Video::empty(); + match decoder.receive_frame(&mut frame) { + Ok(_) => { + let mut rgb_frame = Video::empty(); + match scaler.run(&frame, &mut rgb_frame) { + Ok(_) => { + let image = video_frame_to_image(rgb_frame); + texture.set(image, Default::default()); + *ready_to_show.lock() = true; + ctx.request_repaint() + }, + Err(err) => error!("scaler error {:?}", err) + } + } + Err(err) => { + if matches!(err, ffmpeg::Error::Eof) { + *state.lock() = State::EndOfFile; + *ready_to_show.lock() = false; + ctx.request_repaint(); + break + } + if let ffmpeg::Error::Other { errno } = err { + if errno == ffmpeg::error::EAGAIN { + if let Some((stream, packet)) = input.packets().next() { + if stream.index() == video_index { + decoder.send_packet(&packet); + } + } + else { + decoder.send_eof(); + } + continue + } + } + error!("player error {:?}", err) + }, + } + thread::sleep(wait_duration); + if *thread_stop.lock() { + break + } + } + Ok(()) + })); + } + + pub fn stop(&mut self) { + *self.state.lock() = State::Stopped; + self.ctx.request_repaint(); + if let Some(th) = self.play_thread.take() { + *self.thread_stop.lock() = true; + let _ = th.join(); + } + self.current_location = String::new(); + } + pub fn pause(&mut self) { + *self.state.lock() = State::Paused; + } + pub fn unpause(&mut self) { + if self.play_thread.is_some() { + *self.state.lock() = State::Playing; + } + } +} + + +fn video_frame_to_image(frame: Video) -> ColorImage { + let size = [frame.width() as usize, frame.height() as usize]; + let data = frame.data(0); + let stride = frame.stride(0); + let pixel_size_bytes = 3; + let byte_width: usize = pixel_size_bytes * frame.width() as usize; + let height: usize = frame.height() as usize; + let mut pixels = vec![]; + for line in 0..height { + let begin = line * stride; + let end = begin + byte_width; + let data_line = &data[begin..end]; + pixels.extend( + data_line + .chunks_exact(pixel_size_bytes) + .map(|p| Color32::from_rgb(p[0], p[1], p[2])), + ) + } + ColorImage { size, pixels } +} diff --git a/maxima-ui/src/renderers/mod.rs b/maxima-ui/src/renderers/mod.rs index 35696f6..8dc2ae1 100644 --- a/maxima-ui/src/renderers/mod.rs +++ b/maxima-ui/src/renderers/mod.rs @@ -1,2 +1,3 @@ pub mod app_bg_renderer; pub mod game_view_bg_renderer; +pub mod media_player; diff --git a/maxima-ui/src/ui_image.rs b/maxima-ui/src/ui_image.rs index c9b909d..d9ecd70 100644 --- a/maxima-ui/src/ui_image.rs +++ b/maxima-ui/src/ui_image.rs @@ -24,6 +24,7 @@ pub struct UIImage { #[derive(Clone, PartialEq)] pub enum GameImageType { + Background, Hero, Logo, } @@ -58,6 +59,7 @@ impl UIImage { ) -> Result { let cache_folder = maxima_dir().unwrap().join("cache/ui/images").join(&slug); let file_name = match diff { + GameImageType::Background => cache_folder.join("background.jpg"), GameImageType::Hero => cache_folder.join("hero.jpg"), GameImageType::Logo => cache_folder.join("logo.png"), }; @@ -81,7 +83,7 @@ impl UIImage { download_image(url.unwrap(), &file_name).await?; } - let fs_load = ImageLoader::load_from_fs(&file_name.to_str().unwrap()); + let fs_load = ImageLoader::load_from_fs(&file_name.to_str().unwrap()).await; if fs_load.is_ok() { let img = fs_load?; Ok(UIImage { @@ -125,7 +127,7 @@ impl UIImage { png_cache }; - let fs_load = ImageLoader::load_from_fs(&file_name.to_str().unwrap()); + let fs_load = ImageLoader::load_from_fs(&file_name.to_str().unwrap()).await; if fs_load.is_ok() { let img = fs_load?; return Ok(UIImage { diff --git a/maxima-ui/src/views/downloads_view.rs b/maxima-ui/src/views/downloads_view.rs index 5885322..d28c51e 100644 --- a/maxima-ui/src/views/downloads_view.rs +++ b/maxima-ui/src/views/downloads_view.rs @@ -135,4 +135,4 @@ pub fn downloads_view(app : &mut MaximaEguiApp, ui: &mut Ui) { render_queued(app, ui, &game, false); } } -} \ No newline at end of file +} diff --git a/maxima-ui/src/views/game_view.rs b/maxima-ui/src/views/game_view.rs index 060c2ac..2154822 100644 --- a/maxima-ui/src/views/game_view.rs +++ b/maxima-ui/src/views/game_view.rs @@ -577,4 +577,4 @@ pub fn games_view(app : &mut MaximaEguiApp, ui: &mut Ui) { fn bezier_ease(t: f32) -> f32 { t * t * (3.0 - 2.0 * t) -} \ No newline at end of file +} From 519005f96d10a7ca48504ea904dee2dc21b4441d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Lidwin?= Date: Tue, 23 Jul 2024 15:11:41 +0200 Subject: [PATCH 2/5] build: static link of ffmpeg --- maxima-ui/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maxima-ui/Cargo.toml b/maxima-ui/Cargo.toml index dce8cab..5ec9e1b 100644 --- a/maxima-ui/Cargo.toml +++ b/maxima-ui/Cargo.toml @@ -26,7 +26,7 @@ strum_macros = "0.25.3" sys-locale = "0.3.1" humansize = { version = "2.0.0", features = ["no_alloc"] } fuzzy-matcher = "*" -ffmpeg-next = "7.0.2" +ffmpeg-next = { version = "7.0.2", features = ["build"] } [target.'cfg(windows)'.dependencies] is_elevated = "0.1.2" From 2d58dbc0b0e4e1902ccbed65c660dc647c89efbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Lidwin?= Date: Tue, 23 Jul 2024 17:11:37 +0200 Subject: [PATCH 3/5] Revert "build: static link of ffmpeg" This reverts commit 519005f96d10a7ca48504ea904dee2dc21b4441d. --- maxima-ui/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maxima-ui/Cargo.toml b/maxima-ui/Cargo.toml index 5ec9e1b..dce8cab 100644 --- a/maxima-ui/Cargo.toml +++ b/maxima-ui/Cargo.toml @@ -26,7 +26,7 @@ strum_macros = "0.25.3" sys-locale = "0.3.1" humansize = { version = "2.0.0", features = ["no_alloc"] } fuzzy-matcher = "*" -ffmpeg-next = { version = "7.0.2", features = ["build"] } +ffmpeg-next = "7.0.2" [target.'cfg(windows)'.dependencies] is_elevated = "0.1.2" From f5ea5c12077544185450bd87789e7fa44b8d4a44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Lidwin?= Date: Sat, 7 Sep 2024 14:10:52 +0200 Subject: [PATCH 4/5] improv: disable videos if ffmpeg fails --- maxima-ui/src/main.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/maxima-ui/src/main.rs b/maxima-ui/src/main.rs index 6ba4e95..a3d2ff8 100644 --- a/maxima-ui/src/main.rs +++ b/maxima-ui/src/main.rs @@ -84,7 +84,12 @@ struct Args { #[tokio::main] async fn main() { init_logger(); - ffmpeg::init().unwrap(); + let ffmpeg_success = if let Err(err) = ffmpeg::init() { + log::error!("Unable to initialize ffmpeg {err:?}"); + false + } else { + true + }; let mut args = Args::parse(); if !cfg!(debug_assertions) { @@ -124,7 +129,7 @@ async fn main() { "Maxima", native_options, Box::new(move |cc| { - let app = MaximaEguiApp::new(cc, args); + let app = MaximaEguiApp::new(cc, args, ffmpeg_success); // Run initialization code that needs access to the UI here, but DO NOT run any long-runtime functions here, // as it's before the UI is shown if args.no_login { @@ -343,7 +348,7 @@ const F9B233: Color32 = Color32::from_rgb(249, 178, 51); const WIDGET_HOVER: Color32 = Color32::from_rgb(255, 188, 61); impl MaximaEguiApp { - fn new(cc: &eframe::CreationContext<'_>, args: Args) -> Self { + fn new(cc: &eframe::CreationContext<'_>, args: Args, ffmpeg_success: bool) -> Self { let style: Style = Style { spacing: Spacing { scroll: ScrollStyle { @@ -464,7 +469,7 @@ impl MaximaEguiApp { game_view_bg_renderer: GameViewBgRenderer::new(cc), app_bg_renderer: AppBgRenderer::new(cc), img_cache, - app_bg_media_player: if settings.videos { + app_bg_media_player: if settings.videos && ffmpeg_success { Some(Player::new(&cc.egui_ctx)) } else { None }, locale: TranslationManager::new(&settings.language), From e229fa7f32ef3f41a8221b929fb5cfbb1fbd3fd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Lidwin?= Date: Sat, 7 Sep 2024 14:11:15 +0200 Subject: [PATCH 5/5] dev: add rust-toolchain to automatically pick nightly channel --- rust-toolchain.toml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 rust-toolchain.toml diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..5d56faf --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly"