generated from PlexSheep/rs-base
Compare commits
No commits in common. "a7441473555123349037f9fcdd8445afbf6423f4" and "b19bc410d5a703a326cc7b5ca4b5c5805454467b" have entirely different histories.
a744147355
...
b19bc410d5
9 changed files with 5 additions and 267 deletions
11
Cargo.toml
11
Cargo.toml
|
@ -12,20 +12,13 @@ repository = "https://git.cscherr.de/PlexSheep/beatbear"
|
||||||
keywords = ["media", "sound", "music", "player", "jellyfin", "downloads"]
|
keywords = ["media", "sound", "music", "player", "jellyfin", "downloads"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["backend-fs", "backend-jellyfin"]
|
|
||||||
backend-fs = []
|
backend-fs = []
|
||||||
backend-jellyfin = []
|
backend-jellyfin = []
|
||||||
|
gui = []
|
||||||
|
tui = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.86"
|
|
||||||
clap = { version = "4.5.16", features = ["derive"] }
|
|
||||||
eframe = { version = "0.28.1", optional = false }
|
|
||||||
egui = { version = "0.28.1", optional = false }
|
|
||||||
human-panic = "2.0.1"
|
|
||||||
image = "0.25.2"
|
|
||||||
libpt = { version = "0.6.0", features = ["cli", "full"] }
|
libpt = { version = "0.6.0", features = ["cli", "full"] }
|
||||||
thiserror = "1.0.63"
|
|
||||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "beatbaer"
|
name = "beatbaer"
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 14 KiB |
Binary file not shown.
Before Width: | Height: | Size: 36 KiB |
|
@ -1 +1 @@
|
||||||
pub trait Backend {}
|
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
|
||||||
pub enum Error {
|
|
||||||
#[error("Error in the player UI")]
|
|
||||||
UiError(#[from] eframe::Error),
|
|
||||||
#[error(transparent)]
|
|
||||||
Other(#[from] anyhow::Error),
|
|
||||||
}
|
|
|
@ -1,4 +1,2 @@
|
||||||
pub mod backend;
|
pub mod backend;
|
||||||
pub mod error;
|
|
||||||
pub mod music;
|
pub mod music;
|
||||||
pub mod player;
|
|
||||||
|
|
32
src/main.rs
32
src/main.rs
|
@ -1,31 +1,3 @@
|
||||||
use beatbaer::error::Error;
|
fn main() {
|
||||||
use beatbaer::player::Player;
|
println!("Hello, world!");
|
||||||
use human_panic::{setup_panic, Metadata};
|
|
||||||
use libpt::log::info;
|
|
||||||
|
|
||||||
fn main() -> Result<(), Error> {
|
|
||||||
setup_panic!(
|
|
||||||
Metadata::new(env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"))
|
|
||||||
.authors(env!("CARGO_PKG_AUTHORS"))
|
|
||||||
.homepage(env!("CARGO_PKG_HOMEPAGE"))
|
|
||||||
.support(format!(
|
|
||||||
"Public issue tracker at: {}\nor alternatively email: {}",
|
|
||||||
env!("CARGO_PKG_REPOSITORY"),
|
|
||||||
"software@cscherr.de"
|
|
||||||
))
|
|
||||||
);
|
|
||||||
let mut player = Player::build()?;
|
|
||||||
info!("starting ui");
|
|
||||||
|
|
||||||
eframe::run_native(
|
|
||||||
beatbaer::player::TITLE,
|
|
||||||
player.gui_options.clone(),
|
|
||||||
Box::new(|cc| {
|
|
||||||
player.init(cc);
|
|
||||||
Ok(Box::new(player))
|
|
||||||
}),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
info!("leaving ui");
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
use clap::Parser;
|
|
||||||
use eframe::CreationContext;
|
|
||||||
use libpt::cli::args::VerbosityLevel;
|
|
||||||
use libpt::log::{debug, info, trace};
|
|
||||||
|
|
||||||
use crate::error::Error;
|
|
||||||
|
|
||||||
pub mod ui;
|
|
||||||
|
|
||||||
pub const TITLE: &str = "Beatbär";
|
|
||||||
|
|
||||||
#[derive(Parser)]
|
|
||||||
pub struct Player {
|
|
||||||
#[command(flatten)]
|
|
||||||
verbosity: VerbosityLevel,
|
|
||||||
|
|
||||||
#[clap(skip)]
|
|
||||||
pub gui_options: eframe::NativeOptions,
|
|
||||||
|
|
||||||
#[clap(skip)]
|
|
||||||
show_info_window: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Player {
|
|
||||||
pub fn build() -> Result<Self, Error> {
|
|
||||||
let mut app = Player::parse();
|
|
||||||
|
|
||||||
let filter = tracing_subscriber::EnvFilter::builder()
|
|
||||||
.with_default_directive(app.verbosity.level().into())
|
|
||||||
.from_env()
|
|
||||||
.expect("could not init logger")
|
|
||||||
.add_directive(
|
|
||||||
format!("{}={}", env!("CARGO_PKG_NAME"), app.verbosity.level())
|
|
||||||
.parse()
|
|
||||||
.expect("could not init logger"),
|
|
||||||
);
|
|
||||||
|
|
||||||
tracing_subscriber::fmt::Subscriber::builder()
|
|
||||||
.with_env_filter(filter)
|
|
||||||
.with_max_level(app.verbosity.level())
|
|
||||||
.init();
|
|
||||||
debug!("logging initialized!");
|
|
||||||
debug!("level: {}", app.verbosity.level());
|
|
||||||
|
|
||||||
app.gui_options = eframe::NativeOptions {
|
|
||||||
viewport: egui::ViewportBuilder::default()
|
|
||||||
.with_inner_size([400.0, 300.0])
|
|
||||||
.with_min_inner_size([300.0, 220.0])
|
|
||||||
.with_icon(Player::load_icon()),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
info!("Player ready to start UI");
|
|
||||||
Ok(app)
|
|
||||||
}
|
|
||||||
pub fn init(&mut self, _cc: &CreationContext) {
|
|
||||||
// we can use the creation context to do some customizing, but idc right now
|
|
||||||
}
|
|
||||||
}
|
|
157
src/player/ui.rs
157
src/player/ui.rs
|
@ -1,157 +0,0 @@
|
||||||
use egui::IconData;
|
|
||||||
use libpt::log::trace;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
const ICON_RAW: &[u8; 36525] = include_bytes!("../../assets/img/icon-512.jpg");
|
|
||||||
|
|
||||||
impl Player {
|
|
||||||
fn top_panel(&mut self, ui: &mut egui::Ui, ctx: &egui::Context) {
|
|
||||||
egui::menu::bar(ui, |ui| {
|
|
||||||
// NOTE: no File->Quit on web pages!
|
|
||||||
let is_web = cfg!(target_arch = "wasm32");
|
|
||||||
if !is_web {
|
|
||||||
ui.menu_button("File", |ui| {
|
|
||||||
if ui.button("Info").clicked() {
|
|
||||||
self.show_info_window = true;
|
|
||||||
}
|
|
||||||
ui.separator();
|
|
||||||
if ui.button("Quit").clicked() {
|
|
||||||
ctx.send_viewport_cmd(egui::ViewportCommand::Close);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
ui.add_space(16.0);
|
|
||||||
}
|
|
||||||
if self.show_info_window {
|
|
||||||
self.info_diag(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
egui::widgets::global_dark_light_mode_buttons(ui);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn info_diag(&mut self, ctx: &egui::Context) {
|
|
||||||
trace!("rendering info dialogue");
|
|
||||||
ctx.show_viewport_immediate(
|
|
||||||
egui::ViewportId::from_hash_of(format!("{TITLE}: Information")),
|
|
||||||
egui::ViewportBuilder::default()
|
|
||||||
.with_title(format!("{TITLE}: Information"))
|
|
||||||
.with_inner_size([500.0, 200.0]),
|
|
||||||
|ctx, class| {
|
|
||||||
assert!(
|
|
||||||
class == egui::ViewportClass::Immediate,
|
|
||||||
"This egui backend doesn't support multiple viewports"
|
|
||||||
);
|
|
||||||
|
|
||||||
egui::CentralPanel::default().show(ctx, |ui| {
|
|
||||||
ui.label(format!("{TITLE} v{}", env!("CARGO_PKG_VERSION")));
|
|
||||||
ui.hyperlink_to("Source Code\n", env!("CARGO_PKG_REPOSITORY"));
|
|
||||||
ui.label(format!("Author: {}", env!("CARGO_PKG_AUTHORS")));
|
|
||||||
ui.label(format!("License: {}", env!("CARGO_PKG_LICENSE")));
|
|
||||||
ui.label(format!(
|
|
||||||
"\n{TITLE} is free software. If you paid for this you were scammed.\n"
|
|
||||||
));
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.spacing_mut().item_spacing.x = 0.0;
|
|
||||||
ui.label("Powered by ");
|
|
||||||
ui.hyperlink_to("egui", "https://github.com/emilk/egui");
|
|
||||||
ui.label(" and ");
|
|
||||||
ui.hyperlink_to(
|
|
||||||
"eframe",
|
|
||||||
"https://github.com/emilk/egui/tree/master/crates/eframe",
|
|
||||||
);
|
|
||||||
ui.label(".");
|
|
||||||
});
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
|
||||||
});
|
|
||||||
if ctx.input(|i| i.viewport().close_requested()) {
|
|
||||||
// Tell parent viewport that we should not show next frame:
|
|
||||||
self.show_info_window = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bottom_label(ui: &mut egui::Ui) {
|
|
||||||
ui.with_layout(egui::Layout::bottom_up(egui::Align::LEFT), |ui| {
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.spacing_mut().item_spacing.x = 0.0;
|
|
||||||
ui.label(format!("{TITLE} v{}", env!("CARGO_PKG_VERSION")));
|
|
||||||
ui.label(" | Powered by ");
|
|
||||||
ui.hyperlink_to("egui", "https://github.com/emilk/egui");
|
|
||||||
ui.label(" and ");
|
|
||||||
ui.hyperlink_to(
|
|
||||||
"eframe",
|
|
||||||
"https://github.com/emilk/egui/tree/master/crates/eframe",
|
|
||||||
);
|
|
||||||
ui.label(".");
|
|
||||||
});
|
|
||||||
egui::warn_if_debug_build(ui);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main_panel(&mut self, ui: &mut egui::Ui, ctx: &egui::Context) {
|
|
||||||
ui.vertical_centered(|ui| {
|
|
||||||
for i in 0..100 {
|
|
||||||
ui.horizontal_wrapped(|ui| {
|
|
||||||
for j in 0..10 {
|
|
||||||
ui.label(format!("foo-{i}-{j}"));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn meta_panel(&mut self, ui: &mut egui::Ui, ctx: &egui::Context) {
|
|
||||||
egui::menu::bar(ui, |ui| {
|
|
||||||
// NOTE: no File->Quit on web pages!
|
|
||||||
ui.menu_button("File", |ui| {
|
|
||||||
if ui.button("Info").clicked() {
|
|
||||||
self.show_info_window = true;
|
|
||||||
}
|
|
||||||
ui.separator();
|
|
||||||
if ui.button("Quit").clicked() {
|
|
||||||
ctx.send_viewport_cmd(egui::ViewportCommand::Close);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
ui.add_space(16.0);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn load_icon() -> IconData {
|
|
||||||
let (icon_rgba, icon_width, icon_height) = {
|
|
||||||
let image = image::load_from_memory(ICON_RAW)
|
|
||||||
.expect("Failed to open icon path")
|
|
||||||
.into_rgba8();
|
|
||||||
let (width, height) = image.dimensions();
|
|
||||||
let rgba = image.into_raw();
|
|
||||||
(rgba, width, height)
|
|
||||||
};
|
|
||||||
|
|
||||||
IconData {
|
|
||||||
rgba: icon_rgba,
|
|
||||||
width: icon_width,
|
|
||||||
height: icon_height,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl eframe::App for Player {
|
|
||||||
/// Called each time the UI needs repainting, which may be many times per second.
|
|
||||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
|
||||||
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
|
|
||||||
self.top_panel(ui, ctx);
|
|
||||||
});
|
|
||||||
egui::TopBottomPanel::top("top_panel2").show(ctx, |ui| {
|
|
||||||
self.meta_panel(ui, ctx);
|
|
||||||
});
|
|
||||||
egui::CentralPanel::default().show(ctx, |ui| {
|
|
||||||
self.main_panel(ui, ctx);
|
|
||||||
});
|
|
||||||
egui::TopBottomPanel::bottom("bot_panel").show(ctx, Self::bottom_label);
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Add table
Reference in a new issue