feat: Many more improvements to video player now with a subscription
This commit is contained in:
@@ -11,87 +11,151 @@ pub fn main() -> iced::Result {
|
||||
.with(tracing_subscriber::EnvFilter::from_default_env())
|
||||
.init();
|
||||
iced::application(State::new, update, view)
|
||||
.subscription(keyboard_event)
|
||||
.subscription(|state| match &state.video {
|
||||
Some(video) => video.subscription_with(state, keyboard_event),
|
||||
None => keyboard_event(state),
|
||||
})
|
||||
.run()
|
||||
}
|
||||
|
||||
fn keyboard_event(state: &State) -> iced::Subscription<Message> {
|
||||
fn keyboard_event(_state: &State) -> iced::Subscription<Message> {
|
||||
use iced::keyboard::{Key, key::Named};
|
||||
iced::keyboard::listen().map(move |event| match event {
|
||||
iced::keyboard::Event::KeyPressed { key, .. } => {
|
||||
let key = key.as_ref();
|
||||
match key {
|
||||
Key::Named(Named::Escape) | Key::Character("q") => Message::Quit,
|
||||
Key::Character("f") => Message::Fullscreen,
|
||||
Key::Named(Named::Space) => Message::Toggle,
|
||||
_ => Message::Load,
|
||||
_ => Message::Noop,
|
||||
}
|
||||
// if key == &space {
|
||||
// // Toggle play/pause
|
||||
// let is_playing = state
|
||||
// .video
|
||||
// .source()
|
||||
// .is_playing()
|
||||
// .expect("Failed to get playing state");
|
||||
// if is_playing {
|
||||
// Message::Pause
|
||||
// } else {
|
||||
// Message::Play
|
||||
// }
|
||||
// }
|
||||
}
|
||||
_ => Message::Load,
|
||||
_ => Message::Noop,
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct State {
|
||||
video: VideoHandle,
|
||||
video: Option<VideoHandle<Message>>,
|
||||
fullscreen: bool,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn new() -> Self {
|
||||
let video = VideoHandle::new("https://jellyfin.tsuba.darksailor.dev/Items/6010382cf25273e624d305907010d773/Download?api_key=036c140222464878862231ef66a2bc9c")
|
||||
.expect("Failed to create video handle");
|
||||
Self { video }
|
||||
pub fn new() -> (Self, iced::Task<Message>) {
|
||||
(
|
||||
Self {
|
||||
video: None,
|
||||
fullscreen: false,
|
||||
},
|
||||
iced::Task::done(Message::Load),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Message {
|
||||
Play,
|
||||
Pause,
|
||||
Toggle,
|
||||
Noop,
|
||||
Load,
|
||||
Fullscreen,
|
||||
OnLoad(VideoHandle<Message>),
|
||||
OnError(String),
|
||||
NewFrame,
|
||||
Eos,
|
||||
Quit,
|
||||
}
|
||||
|
||||
pub fn update(state: &mut State, message: Message) -> iced::Task<Message> {
|
||||
match message {
|
||||
Message::Load => {
|
||||
// does stuff
|
||||
Message::NewFrame => {
|
||||
iced::Task::none()
|
||||
}
|
||||
Message::Eos => {
|
||||
iced::Task::done(Message::Pause)
|
||||
}
|
||||
Message::Load => {
|
||||
iced::Task::perform(
|
||||
VideoHandle::load(
|
||||
"https://jellyfin.tsuba.darksailor.dev/Items/6010382cf25273e624d305907010d773/Download?api_key=036c140222464878862231ef66a2bc9c",
|
||||
),
|
||||
|result| match result {
|
||||
Ok(video) => Message::OnLoad(video),
|
||||
Err(err) => Message::OnError(format!("Error loading video: {:?}", err)),
|
||||
},
|
||||
).chain(iced::Task::done(Message::Play))
|
||||
}
|
||||
Message::OnError(err) => {
|
||||
eprintln!("Error: {}", err);
|
||||
iced::Task::none()
|
||||
}
|
||||
Message::OnLoad(video) => {
|
||||
state.video = Some(video.on_new_frame(Message::NewFrame).on_end_of_stream(Message::Eos));
|
||||
iced::Task::none()
|
||||
}
|
||||
Message::Fullscreen => {
|
||||
state.fullscreen = !state.fullscreen;
|
||||
let fullscreen = state.fullscreen;
|
||||
let mode = if fullscreen {
|
||||
iced::window::Mode::Fullscreen
|
||||
} else {
|
||||
iced::window::Mode::Windowed
|
||||
};
|
||||
iced::window::oldest().and_then(move |id| iced::window::set_mode::<Message>(id, mode))
|
||||
}
|
||||
Message::Play => {
|
||||
state.video.source().play().expect("Failed to play video");
|
||||
state
|
||||
.video
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.source()
|
||||
.play()
|
||||
.expect("Failed to play video");
|
||||
iced::Task::none()
|
||||
}
|
||||
Message::Pause => {
|
||||
state.video.source().pause().expect("Failed to pause video");
|
||||
state
|
||||
.video
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.source()
|
||||
.pause()
|
||||
.expect("Failed to pause video");
|
||||
iced::Task::none()
|
||||
}
|
||||
Message::Toggle => {
|
||||
state.video.source().toggle().expect("Failed to stop video");
|
||||
state
|
||||
.video
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.source()
|
||||
.toggle()
|
||||
.expect("Failed to stop video");
|
||||
iced::Task::none()
|
||||
}
|
||||
Message::Quit => {
|
||||
state.video.source().stop().expect("Failed to stop video");
|
||||
state
|
||||
.video
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.source()
|
||||
.stop()
|
||||
.expect("Failed to stop video");
|
||||
std::process::exit(0);
|
||||
}
|
||||
Message::Noop => iced::Task::none(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn view<'a>(state: &'a State) -> iced::Element<'a, Message> {
|
||||
let video_widget = Video::new(&state.video)
|
||||
if let None = &state.video {
|
||||
return iced::widget::Column::new()
|
||||
.push(iced::widget::Text::new("Press any key to load video"))
|
||||
.align_x(iced::Alignment::Center)
|
||||
.into();
|
||||
}
|
||||
let video_widget = Video::new(&state.video.as_ref().unwrap())
|
||||
.width(iced::Length::Fill)
|
||||
.height(iced::Length::Fill)
|
||||
.content_fit(iced::ContentFit::Contain);
|
||||
|
||||
Reference in New Issue
Block a user