diff --git a/Cargo.lock b/Cargo.lock index f3acca9..ff4cbc9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -150,9 +150,9 @@ checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" [[package]] name = "cc" -version = "1.1.18" +version = "1.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476" +checksum = "2d74707dde2ba56f86ae90effb3b43ddd369504387e718014de010cec7959800" dependencies = [ "shlex", ] @@ -677,9 +677,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "33ea5043e58958ee56f3e15a90aee535795cd7dfd319846288d93c5b57d85cbe" [[package]] name = "openssl" @@ -1198,7 +1198,7 @@ dependencies = [ [[package]] name = "tootube" -version = "0.9.1" +version = "0.10.0" dependencies = [ "async-stream", "clap", diff --git a/Cargo.toml b/Cargo.toml index 31d93e6..6c86d54 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "tootube" authors = ["VC "] -version = "0.9.1" +version = "0.10.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/README.md b/README.md index c00345c..6d56558 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,11 @@ client_secret="" refresh_token="" [youtube] +notify_subscribers_on_shorts=false # will you notify subscribers for shorts? +# optional +# allows you to notify subscribers when transferring shorts, defaults to false + +[youtube.oauth2] refresh_token="" # leave empty for now client_id="" client_secret="" diff --git a/src/config.rs b/src/config.rs index a6cb770..acf9640 100644 --- a/src/config.rs +++ b/src/config.rs @@ -40,6 +40,13 @@ pub struct PeertubeConfigOauth2 { #[derive(Debug, Deserialize, Serialize)] pub struct YoutubeConfig { + #[serde(default)] + pub notify_subscribers_on_shorts: bool, + pub oauth2: YoutubeConfigOauth2, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct YoutubeConfigOauth2 { pub refresh_token: String, pub client_id: String, pub client_secret: String, @@ -79,3 +86,33 @@ impl Config { Ok(()) } } + +#[cfg(test)] +mod tests { + use super::*; + use std::fs::{remove_file, write}; + + #[test] + fn test_minimal_conf() { + let file_name = "/tmp/test_minimal_conf.toml"; + let data = r#" +[peertube] +base_url = 'https://p.nintendojo.fr' + +[youtube.oauth2] +refresh_token = 'rt_N/A' +client_id = 'ci_N/A' +client_secret = 'cs_N/A' +"#; + write(file_name, data).unwrap(); + let config = Config::new(file_name); + + assert_eq!(config.youtube.notify_subscribers_on_shorts, false); + assert_eq!(config.tootube.progress_chars, "#>-"); + assert_eq!(config.youtube.oauth2.refresh_token, "rt_N/A"); + assert_eq!(config.youtube.oauth2.client_id, "ci_N/A"); + assert_eq!(config.youtube.oauth2.client_secret, "cs_N/A"); + + remove_file(file_name).unwrap(); + } +} diff --git a/src/lib.rs b/src/lib.rs index 9da0400..0b74111 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -66,15 +66,24 @@ pub async fn run(config: &Config, pl: Vec, pt_video_id: Option<&str>) -> debug!("PT download URL: {}", &dl_url); let youtube = YouTube::new( - &config.youtube.client_id, - &config.youtube.client_secret, - &config.youtube.refresh_token, + &config.youtube.oauth2.client_id, + &config.youtube.oauth2.client_secret, + &config.youtube.oauth2.refresh_token, ) .await .unwrap_or_else(|e| panic!("Cannot instantiate YouTube struct: {}", e)); + // Do not notify when notify_subscribers_on_shorts = False and latest_id is a short + debug!( + "Will user get notified? {}", + !(!config.youtube.notify_subscribers_on_shorts & latest_vid.is_short()) + ); + let resumable_upload_url = youtube - .create_resumable_upload(&latest_vid) + .create_resumable_upload( + &latest_vid, + !(!config.youtube.notify_subscribers_on_shorts & latest_vid.is_short()), + ) .await .unwrap_or_else(|e| panic!("Cannot retrieve the upload’s resumable id: {e}")); debug!("YT upload URL: {}", &resumable_upload_url); diff --git a/src/main.rs b/src/main.rs index fe4c60e..79407d5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -69,10 +69,10 @@ fn main() { let mut config = Config::new(sub_m.get_one::("config").unwrap()); if sub_m.get_flag("youtube") { - let yt_refresh_token = register_youtube(&config.youtube) + let yt_refresh_token = register_youtube(&config.youtube.oauth2) .unwrap_or_else(|e| panic!("Cannot register to YouTube API: {}", e)); - config.youtube.refresh_token = yt_refresh_token; + config.youtube.oauth2.refresh_token = yt_refresh_token; } if sub_m.get_flag("peertube") { diff --git a/src/peertube.rs b/src/peertube.rs index 6672e8f..d436bc6 100644 --- a/src/peertube.rs +++ b/src/peertube.rs @@ -19,6 +19,9 @@ pub struct PeerTubeVideo { pub name: String, pub uuid: String, pub description: String, + pub duration: u64, + #[serde(rename = "aspectRatio")] + pub aspect_ratio: Option, #[serde(rename = "previewPath")] pub preview_path: String, #[serde(rename = "streamingPlaylists")] @@ -27,6 +30,15 @@ pub struct PeerTubeVideo { pub channel: PeerTubeVideoChannel, } +impl PeerTubeVideo { + pub fn is_short(&self) -> bool { + if self.duration < 60 && self.aspect_ratio.is_some_and(|x| x == 0.5625) { + return true; + } + false + } +} + #[derive(Debug, Deserialize)] pub struct PeerTubeVideoChannel { pub id: u8, diff --git a/src/youtube.rs b/src/youtube.rs index f9a3619..da2af05 100644 --- a/src/youtube.rs +++ b/src/youtube.rs @@ -1,5 +1,5 @@ use crate::{ - config::{TootubeConfig, YoutubeConfig}, + config::{TootubeConfig, YoutubeConfigOauth2}, error::TootubeError, peertube::PeerTubeVideo, }; @@ -162,7 +162,7 @@ struct YoutubePlaylistListResponseItemSnippet { /// This function makes the registration process a little bit easier /// It returns the expected refresh_token so that it can be written back to the file #[tokio::main] -pub async fn register(config: &YoutubeConfig) -> Result> { +pub async fn register(config: &YoutubeConfigOauth2) -> Result> { println!("Click on the link below to authorize {} to upload to YouTube and deal with your playlists:", env!("CARGO_PKG_NAME")); println!("https://accounts.google.com/o/oauth2/v2/auth?client_id={}&redirect_uri=urn:ietf:wg:oauth:2.0:oob&scope=https://www.googleapis.com/auth/youtube%20https://www.googleapis.com/auth/youtube.upload&response_type=code", config.client_id); println!("Paste the returned authorization code:"); @@ -340,6 +340,7 @@ impl YouTube { pub async fn create_resumable_upload( &self, vid: &PeerTubeVideo, + notify: bool, ) -> Result> { if vid.name.chars().count() > 100 { warn!( @@ -365,7 +366,9 @@ impl YouTube { }; debug!("YT upload params: {:?}", &upload_params); - let res = self.client.post("https://www.googleapis.com/upload/youtube/v3/videos?uploadType=resumable&part=snippet%2Cstatus") + let notify_subscriber = notify.then_some(()).map_or("False", |_| "True"); + + let res = self.client.post(format!("https://www.googleapis.com/upload/youtube/v3/videos?uploadType=resumable&part=snippet%2Cstatus¬ifySubscribers={}", notify_subscriber)) .json(&upload_params) .send().await?;