diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/.woodpecker.yml b/.woodpecker.yml new file mode 100644 index 0000000..d783a5a --- /dev/null +++ b/.woodpecker.yml @@ -0,0 +1,23 @@ +pipeline: + build: + when: + event: [push, pull_request] + image: ghcr.io/13hannes11/gtk4-rs-docker:latest-appimage + commands: + - cargo update + - cargo build + - cargo test + - echo "Done building!" + gzip: + when: + event: [push] + image: rust:latest + commands: + - mkdir ../artifact + - mv target/debug ../artifact + - tar -c -z -v -f gtubek-dev.tar.gz ../artifact + #upload: + #when: + #event: [push] + #image: + diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..73bd3e7 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "gtubek" +version = "0.0.1" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +gtk = { version = "0.4.8", package = "gtk4" } +adw = { version = "0.1.1", package = "libadwaita" } +invidious = "0.3.2" +gtk-ui-builder = "0.2.0" +serde_json = "1.0" +gst = { version = "0.18.8", package = "gstreamer" } +anyhow = "1.0.65" +glib = "0.15.12" +gst-plugin-reqwest = "0.8.0" + +[profile.release] +strip = true \ No newline at end of file diff --git a/inv.rs b/inv.rs deleted file mode 100644 index 5c47af1..0000000 --- a/inv.rs +++ /dev/null @@ -1,10 +0,0 @@ -use invidious::reqwest::blocking::Client; -use std::error::Error; - -fn main() -> Result<(), Box> { - let client = Client::new(String::from("https://vid.puffyan.us")); - let search_results = client.search(Some("q=rust programming"))?.items; - let video = client.video("5C_HPTJg5ek", None)?; - - Ok(()) -} \ No newline at end of file diff --git a/main.rs b/main.rs deleted file mode 100644 index 7af0eab..0000000 --- a/main.rs +++ /dev/null @@ -1,52 +0,0 @@ -use adw::Application; -use std::time::Duration; -use gtk::Video; -use gtk::gio; -use std::fs::File; -use std::path::Path; -use gtk::prelude::*; -use gtk::{self, ApplicationWindow, Button, prelude::*}; -use invidious::reqwest::blocking::functions::video; -mod inv; - - -const APP_ID: &str = "org.gtk_rs.MainEventLoop1"; - -fn main() { - // Create a new application - let app = Application::builder().application_id(APP_ID).build(); - - // Connect to "activate" signal of `app` - app.connect_activate(build_ui); - - print!("{}: {}", video.title, video.author); - - // Run the application - app.run(); -} - -fn build_ui(app: &Application) { - // Create a button - let button = Button::builder() - .label("Press me!") - .margin_top(12) - .margin_bottom(12) - .margin_start(12) - .margin_end(12) - .build(); - - - let video = Video::for_file(Some(&gio::File::for_path("/home/midou/Vidéos/miui.mp4"))); - - - // Create a window - let window = ApplicationWindow::builder() - .application(app) - .title("My GTK App") - .child(&button) - .child(&video) - .build(); - - // Present window - window.present(); -} diff --git a/src/inv.rs b/src/inv.rs new file mode 100644 index 0000000..f795182 --- /dev/null +++ b/src/inv.rs @@ -0,0 +1,12 @@ +use invidious::reqwest::blocking::Client; +use std::error::Error; +//use serde::{Deserialize, Serialize}; +//use serde_json::Result; + + +pub fn inv() -> Result> { + let client = Client::new(String::from("https://invidious.projectsegfau.lt")); + let output = &client.video("5C_HPTJg5ek", None)?.format_streams[2].url; + println!("{}", output); + Ok(output.to_string()) +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..06d28ea --- /dev/null +++ b/src/main.rs @@ -0,0 +1,180 @@ +use adw::Application; +use std::time::Duration; +use gtk::Video; +use gtk::gio; +use std::fs::File; +use std::path::Path; +use gtk::prelude::*; +use gtk::{self, ApplicationWindow, Button, prelude::*, ListBox, FlowBox, PolicyType, ScrolledWindow, Box, Orientation, Align::*}; +use gst::prelude::*; +use std::io::Write; +use anyhow::Error; +use crate::inv::*; +mod inv; + + +const APP_ID: &str = "org.gtk_rs.MainEventLoop1"; + +fn main() { + // Create a new application + let app = Application::builder().application_id(APP_ID).build(); + + // Connect to "activate" signal of `app` + app.connect_activate(build_ui); + + //print!("{:?}", output); + // Run the application + match VidPlayer() { + Ok(_) => {} + Err(err) => eprintln!("Failed: {}", err), + } + + + + app.run(); +} + + + +fn build_ui(app: &Application) { + // Create a button + let button = Button::builder() + .label("Press me!") + .icon_name("fingerprint-authentication-symbolic") + .valign(Fill) + .vexpand(true) + .margin_top(12) + .margin_bottom(12) + .margin_start(12) + .margin_end(12) + .build(); + + //button.connect_clicked(move |_| trig_play()); + + + let video = Video::for_file(Some(&gio::File::for_path("/home/midou/Vidéos/miui.mp4"))); + //let output = inv::inv(); + + //let video = Video::for_media_stream(Some(&output)); + + /* + // Set a listBox + let listbox = ListBox::builder() + .child(&video) + .build(); + + */ + + // Set a box + let boxy = Box::new(Orientation::Vertical, 0); + boxy.append(&video); + boxy.append(&button); + + // Then make the window scrollable + let scrolled_window = ScrolledWindow::builder() + // .hscrollbar_policy(PolicyType::Never) // Disable horizontal scrolling + // .hscrollbar_policy() + .min_content_width(360) + .child(&boxy) + .build(); + + // Create a window + let window = ApplicationWindow::builder() + .application(app) + .default_width(1280) + .default_height(720) + .title("My GTK App") + .child(&scrolled_window) + .build(); + + // Present window + window.present(); +} + +//fn trig_play() { +// let trigger = +//} + +use gst::prelude::*; + +fn VidPlayer() -> Result<(), Error> { + // Initialize GStreamer + gst::init()?; + + // Build the pipeline + //let uri = + //"https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm"; + let uri = inv().unwrap(); + println!("{:?}", uri); + let pipeline = gst::parse_launch(&format!("playbin uri={:?}", uri))?; + + // Start playing + let res = pipeline.set_state(gst::State::Playing)?; + let is_live = res == gst::StateChangeSuccess::NoPreroll; + + let main_loop = glib::MainLoop::new(None, false); + let main_loop_clone = main_loop.clone(); + let pipeline_weak = pipeline.downgrade(); + let bus = pipeline.bus().expect("Pipeline has no bus"); + bus.add_watch(move |_, msg| { + let pipeline = match pipeline_weak.upgrade() { + Some(pipeline) => pipeline, + None => return glib::Continue(true), + }; + let main_loop = &main_loop_clone; + match msg.view() { + gst::MessageView::Error(err) => { + println!( + "Error from {:?}: {} ({:?})", + err.src().map(|s| s.path_string()), + err.error(), + err.debug() + ); + let _ = pipeline.set_state(gst::State::Ready); + main_loop.quit(); + } + gst::MessageView::Eos(..) => { + // end-of-stream + let _ = pipeline.set_state(gst::State::Ready); + main_loop.quit(); + } + gst::MessageView::Buffering(buffering) => { + // If the stream is live, we do not care about buffering + if is_live { + return glib::Continue(true); + } + + let percent = buffering.percent(); + print!("Buffering ({}%)\r", percent); + match std::io::stdout().flush() { + Ok(_) => {} + Err(err) => eprintln!("Failed: {}", err), + }; + + // Wait until buffering is complete before start/resume playing + if percent < 100 { + let _ = pipeline.set_state(gst::State::Paused); + } else { + let _ = pipeline.set_state(gst::State::Playing); + } + } + gst::MessageView::ClockLost(_) => { + // Get a new clock + let _ = pipeline.set_state(gst::State::Paused); + let _ = pipeline.set_state(gst::State::Playing); + } + _ => (), + } + glib::Continue(true) + }) + .expect("Failed to add bus watch"); + + main_loop.run(); + + bus.remove_watch()?; + pipeline.set_state(gst::State::Null)?; + + Ok(()) +} + + diff --git a/uai.rs b/src/uai.rs similarity index 100% rename from uai.rs rename to src/uai.rs diff --git a/uai/ui.rs b/src/uai/ui.rs similarity index 100% rename from uai/ui.rs rename to src/uai/ui.rs