From 62641f526f849d737558d36b128b3c49420d2cf8 Mon Sep 17 00:00:00 2001 From: VC Date: Mon, 16 Oct 2023 21:22:47 +0200 Subject: [PATCH 1/4] refactor: add Ord/PartialOrd/PartialEq for PeerTubeVideoStreamingPlaylistsFiles struct --- src/peertube.rs | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/peertube.rs b/src/peertube.rs index d4e025a..d872adf 100644 --- a/src/peertube.rs +++ b/src/peertube.rs @@ -1,5 +1,5 @@ use serde::Deserialize; -use std::{boxed::Box, error::Error}; +use std::{boxed::Box, cmp::Ordering, error::Error}; #[derive(Debug, Deserialize)] pub struct PeerTubeVideos { @@ -22,7 +22,7 @@ pub struct PeerTubeVideoStreamingPlaylists { pub files: Vec, } -#[derive(Debug, Deserialize)] +#[derive(Eq, Debug, Deserialize)] pub struct PeerTubeVideoStreamingPlaylistsFiles { pub id: u64, pub resolution: PeerTubeVideoStreamingPlaylistsFilesResolution, @@ -30,7 +30,25 @@ pub struct PeerTubeVideoStreamingPlaylistsFiles { pub file_download_url: String, } -#[derive(Debug, Deserialize)] +impl Ord for PeerTubeVideoStreamingPlaylistsFiles { + fn cmp(&self, other: &Self) -> Ordering { + self.resolution.id.cmp(&other.resolution.id) + } +} + +impl PartialOrd for PeerTubeVideoStreamingPlaylistsFiles { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl PartialEq for PeerTubeVideoStreamingPlaylistsFiles { + fn eq(&self, other: &Self) -> bool { + self.resolution.id == other.resolution.id + } +} + +#[derive(Eq, Debug, Deserialize, PartialEq)] pub struct PeerTubeVideoStreamingPlaylistsFilesResolution { pub id: u16, } @@ -49,17 +67,7 @@ pub async fn get_latest_video(u: &str) -> Result> /// This returns the direct download URL for with the maximum resolution pub fn get_max_resolution_dl(p: &[PeerTubeVideoStreamingPlaylists]) -> String { - let mut res = 0; - let mut dl_url = String::new(); - - for i in p[0].files.iter() { - if i.resolution.id > res { - res = i.resolution.id; - dl_url = i.file_download_url.clone(); - } - } - - dl_url + p[0].files.iter().max().unwrap().file_download_url.clone() } /// This gets all the crispy details about one particular video From ae3fa74d9bf3cefe9b630ccdf426322c94324c1f Mon Sep 17 00:00:00 2001 From: VC Date: Mon, 16 Oct 2023 21:23:15 +0200 Subject: [PATCH 2/4] chore: bump version --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dd14064..6603631 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1081,7 +1081,7 @@ dependencies = [ [[package]] name = "tootube" -version = "0.4.0" +version = "0.4.1" dependencies = [ "bytes", "clap", diff --git a/Cargo.toml b/Cargo.toml index af32a81..89d591d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "tootube" authors = ["VC "] -version = "0.4.0" +version = "0.4.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html From 3f482ff258c5f2248106e521fcc2f1a0800bacf5 Mon Sep 17 00:00:00 2001 From: VC Date: Mon, 16 Oct 2023 21:23:29 +0200 Subject: [PATCH 3/4] feat: add relevant debug bits --- src/lib.rs | 4 ++++ src/youtube.rs | 7 +++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 567d065..3f7ee6b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ use bytes::Bytes; use futures_core::stream::Stream; +use log::debug; use std::error::Error; mod error; @@ -31,6 +32,7 @@ pub async fn run(config: Config, pl: Vec) { }); let dl_url = get_max_resolution_dl(latest_vid.streaming_playlists.as_ref().unwrap()); + debug!("PT download URL: {}", &dl_url); let pt_stream = get_dl_video_stream(&dl_url) .await @@ -39,10 +41,12 @@ pub async fn run(config: Config, pl: Vec) { let resumable_upload_url = create_resumable_upload(&config.youtube, &latest_vid) .await .unwrap_or_else(|e| panic!("Cannot retrieve the upload’s resumable id: {e}")); + debug!("YT upload URL: {}", &resumable_upload_url); let yt_video_id = now_kiss(pt_stream, &resumable_upload_url, &config.youtube) .await .unwrap_or_else(|e| panic!("Cannot resume upload!: {e}")); + debug!("YT video ID: {}", &yt_video_id); if !pl.is_empty() { add_video_to_playlists(&config.youtube, &yt_video_id, &pl) diff --git a/src/youtube.rs b/src/youtube.rs index 5e5c873..a36493c 100644 --- a/src/youtube.rs +++ b/src/youtube.rs @@ -1,6 +1,7 @@ use crate::{config::YoutubeConfig, error::TootubeError, peertube::PeerTubeVideo}; use bytes::Bytes; use futures_core::stream::Stream; +use log::debug; use reqwest::{multipart::Form, Body, Client}; use serde::{Deserialize, Serialize}; use std::{error::Error, io::stdin}; @@ -207,6 +208,7 @@ async fn refresh_token(config: &YoutubeConfig) -> Result let access_token: AccessTokenResponse = res.json().await?; + debug!("YT Access Token: {}", &access_token.access_token); Ok(access_token.access_token) }) .await @@ -250,6 +252,7 @@ async fn get_playlist_ids( } } + debug!("Playlists IDs: {:?}", &playlists); Ok(playlists) } @@ -273,8 +276,8 @@ pub async fn add_video_to_playlists( ..Default::default() }, }; - let client = Client::new(); + let client = Client::new(); let res = client .post("https://youtube.googleapis.com/youtube/v3/playlistItems?part=snippet") .header("Authorization", format!("Bearer {}", access_token)) @@ -316,6 +319,7 @@ pub async fn create_resumable_upload( } }, }; + debug!("YT upload params: {:?}", &upload_params); let client = Client::new(); let res = client.post("https://www.googleapis.com/upload/youtube/v3/videos?uploadType=resumable&part=snippet%2Cstatus") @@ -349,7 +353,6 @@ pub async fn now_kiss<'a>( // Create client let client = Client::new(); - let res = client .put(r_url) .header("Authorization", format!("Bearer {}", access_token)) From 3d53ad7bbd9469eaca554327f86665a33380e13a Mon Sep 17 00:00:00 2001 From: VC Date: Mon, 16 Oct 2023 21:36:41 +0200 Subject: [PATCH 4/4] doc: update documentation --- README.md | 55 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index bfbe526..6cb3ced 100644 --- a/README.md +++ b/README.md @@ -6,50 +6,57 @@ This program takes the very last video published on a PeerTube instance and push This is an early prototype not really suited for production purposes for now: * it still relies way too much on pre-determined value to upload the video to YouTube -* it cannot determine the playlists it needs to put them in * it cannot determine the recording date (believe me, I tried!) -* there are still a lot of static values that I would rather not have static (like the cache directory…) -* it is 100% sync, meaning it’s clearly not optimal for now and probably won’t be for the next releases -So consider this a work in progress that will slowly get better with time +So consider this a work in progress that will slowly get better with time. # What does it do exactly? -* it downloads the latest PeerTube video from an instance -* stores it in cache directory -* uploads it to YouTube +* it retrieves the latest PeerTube video download URL from an instance +* it creates a resumable upload into the target YouTube account +* it downloads/uploads the latest PeerTube video to YouTube without using a cache (stream-to-stream) # What doesn’t it do exactly? -* it cannot register the original key (see below) -* it relies on a local cache despite the fact that it might well be possible to just download from PT/upload to YT at the same time (I don’t see why not in fact) * it doesn’t retrieve ALL the original PeerTube video properties like licences, languages, categories, etc… again: early prototype -# Obtain Authorization Token from Google +# Howtos +## General usage -That the complicated part: -* create an OAuth2.0 application with authorization for Youtube DATA Api v3 Upload +Once you fill your `tootube.toml` config file (see below), you can run `tootube` like this: + +```bash +tootube --config tootube.toml --playlist playlist_1 "Things You Might Like" +``` + +You can turn on debug using env var `RUST_LOG`. + +## Obtain Authorization Token from Google + +The complicated part: +* create an OAuth2.0 application with authorization for Youtube DATA Api v3 Upload and Youtube DATA Api v3 (generally referenced as `../auth/youtube.upload` and `../auth/youtube`) * create a OAuth2.0 client with Desktop client You’ll need: * the `client_id` from your OAuth2.0 client * the `client_secret` from you OAuth2.0 client -Then enter in: +Create your `tootube.toml` config file: -``` -https://accounts.google.com/o/oauth2/v2/auth?client_id=XXX.apps.googleusercontent.com&redirect_uri=urn:ietf:wg:oauth:2.0:oob&scope=https://www.googleapis.com/auth/youtube.upload&response_type=code +```toml +[peertube] +base_url="https://p.nintendojo.fr" + +[youtube] +refresh_token="" # leave empty for now +client_id="" +client_secret="" ``` -And accept that your YouTube account might be modified. You’ll get a code then; enter this `curl` post: +Then run: -``` -curl -s \ ---request POST \ ---data "code=[THE_CODE]&client_id=XXX.apps.googleusercontent.com&client_secret=[THE_CLIENT_SECRET]&redirect_uri=urn:ietf:wg:oauth:2.0:oob&grant_type=authorization_code" \ -https://accounts.google.com/o/oauth2/token +```bash +tootube register --config ``` -You’ll get a Token. The only important part is the `refresh_token` - -In your `tootube.toml` config file, put the `refresh_token`. +You’ll be then prompted with all the necessary information to register `tootube`. You’ll end with a `refresh_token` that you can now paste inside your tootube configuration.