use iced_video::{Video, VideoHandle}; pub fn main() -> iced::Result { use tracing_subscriber::prelude::*; tracing_subscriber::registry() .with( tracing_subscriber::fmt::layer() .with_thread_ids(true) .with_file(true), ) .with(tracing_subscriber::EnvFilter::from_default_env()) .init(); iced::application(State::new, update, view) .subscription(|state| { // Foo match &state.video { Some(video) => video.subscription_with(state, keyboard_event), None => keyboard_event(state), } }) .run() } fn keyboard_event(_state: &State) -> iced::Subscription { 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::Noop, } } _ => Message::Noop, }) } #[derive(Debug, Clone)] pub struct State { video: Option>, fullscreen: bool, } impl State { pub fn new() -> (Self, iced::Task) { ( Self { video: None, fullscreen: false, }, iced::Task::done(Message::Load), ) } } #[derive(Debug, Clone)] pub enum Message { Play, Pause, Toggle, Noop, Load, Fullscreen, OnLoad(VideoHandle), OnError(String), NewFrame, Eos, Quit, } pub fn update(state: &mut State, message: Message) -> iced::Task { match message { 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::(id, mode)) } Message::Play => { state .video .as_ref() .unwrap() .source() .play() .expect("Failed to play video"); iced::Task::none() } Message::Pause => { state .video .as_ref() .unwrap() .source() .pause() .expect("Failed to pause video"); iced::Task::none() } Message::Toggle => { state .video .as_ref() .unwrap() .source() .toggle() .expect("Failed to stop video"); iced::Task::none() } Message::Quit => { 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> { 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); iced::widget::Column::new() .push(video_widget) .push( iced::widget::Row::new() .push(iced::widget::Button::new("Play").on_press(Message::Play)) .push(iced::widget::Button::new("Pause").on_press(Message::Pause)) .spacing(5) .padding(10) .align_y(iced::Alignment::Center), ) .align_x(iced::Alignment::Center) .into() }