use anyhow result in most cases & some more tracing improvements

This commit is contained in:
OMGeeky
2023-06-03 19:10:52 +02:00
parent e32b8a35bd
commit 6a4a3a8ad7
3 changed files with 80 additions and 47 deletions

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "google_youtube" name = "google_youtube"
version = "0.1.2" version = "0.1.3"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -18,7 +18,7 @@ serde_json = "1.0"
async-trait = "0.1.60" async-trait = "0.1.60"
strfmt = "0.2.2" strfmt = "0.2.2"
anyhow = "1.0"
log = "0.4" log = "0.4"
simplelog = "0.12.1" simplelog = "0.12.1"
#[patch.crates-io.yup-oauth2] #[patch.crates-io.yup-oauth2]

View File

@@ -1,7 +1,7 @@
use anyhow::{anyhow, Context};
use crate::prelude::*;
use std::default::Default; use std::default::Default;
use std::error::Error; use std::error::Error;
use std::fmt::{Debug, Formatter};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use exponential_backoff::youtube::generic_check_backoff_youtube; use exponential_backoff::youtube::generic_check_backoff_youtube;
@@ -20,17 +20,27 @@ use google_youtube3::{
hyper::{Body, Response}, hyper::{Body, Response},
hyper_rustls::HttpsConnector, hyper_rustls::HttpsConnector,
}; };
#[cfg(feature = "tracing")]
use tracing::instrument;
use youtube::YouTube; use youtube::YouTube;
use youtube::{hyper, hyper_rustls::HttpsConnectorBuilder}; use youtube::{hyper, hyper_rustls::HttpsConnectorBuilder};
use crate::prelude::*;
mod auth; mod auth;
pub mod scopes;
pub mod prelude; pub mod prelude;
pub mod scopes;
// mod config; // mod config;
pub struct YoutubeClient { pub struct YoutubeClient {
pub client: YouTube<HttpsConnector<HttpConnector>>, pub client: YouTube<HttpsConnector<HttpConnector>>,
} }
impl Debug for YoutubeClient {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("YoutubeClient").finish()
}
}
#[derive(Debug)]
pub enum PrivacyStatus { pub enum PrivacyStatus {
Public, Public,
Unlisted, Unlisted,
@@ -46,11 +56,12 @@ impl PrivacyStatus {
} }
} }
impl YoutubeClient { impl YoutubeClient {
pub async fn new<S: Into<String>>( #[cfg_attr(feature = "tracing", instrument)]
path_to_application_secret: Option<S>, pub async fn new(
scopes: Vec<S>, path_to_application_secret: Option<impl Into<String> + Debug>,
user: Option<S>, scopes: Vec<impl Into<String> + Debug>,
) -> Result<Self, Box<dyn Error>> { user: Option<impl Into<String> + Debug>,
) -> anyhow::Result<Self> {
let scopes = scopes let scopes = scopes
.into_iter() .into_iter()
.map(|s| s.into()) .map(|s| s.into())
@@ -64,23 +75,29 @@ impl YoutubeClient {
.build(), .build(),
); );
let path_to_application_secret = match path_to_application_secret { let path_to_application_secret = path_to_application_secret
None => "auth/service_account2.json".to_string(), .map(|x| x.into())
Some(s) => s.into(), .unwrap_or_else(|| {
}; warn!("the path to the application secret was not provided. Using default!");
"auth/service_account2.json".to_string()
let auth = auth::get_authenticator(path_to_application_secret, &scopes, user).await?; });
trace!(
"getting authenticator from path: {}",
path_to_application_secret
);
let auth = auth::get_authenticator(path_to_application_secret, &scopes, user)
.await
.map_err(|e| anyhow!("error: {}", e))
.context("could not get authenticator")?;
trace!("creating youtube client");
let client: YouTube<HttpsConnector<HttpConnector>> = YouTube::new(hyper_client, auth); let client: YouTube<HttpsConnector<HttpConnector>> = YouTube::new(hyper_client, auth);
let res = Self { client }; let res = Self { client };
Ok(res) Ok(res)
} }
pub async fn find_playlist_by_name( #[cfg_attr(feature = "tracing", instrument)]
&self, pub async fn find_playlist_by_name(&self, name: &str) -> Result<Option<Playlist>> {
name: &str,
) -> Result<Option<Playlist>, Box<dyn Error>> {
let part = vec!["snippet".to_string()]; let part = vec!["snippet".to_string()];
struct PlaylistParams { struct PlaylistParams {
@@ -100,7 +117,10 @@ impl YoutubeClient {
} }
let para = PlaylistParams { part, mine: true }; let para = PlaylistParams { part, mine: true };
let (_res, playlists): (Response<Body>, PlaylistListResponse) = let (_res, playlists): (Response<Body>, PlaylistListResponse) =
generic_check_backoff_youtube(&self.client, &para, list_playlist).await??; generic_check_backoff_youtube(&self.client, &para, list_playlist)
.await
.map_err(|e| anyhow!("backoff error: {}", e))?
.context("list_playlist returned an error")?;
if let Some(items) = playlists.items { if let Some(items) = playlists.items {
for element in items { for element in items {
@@ -116,10 +136,8 @@ impl YoutubeClient {
Ok(None) Ok(None)
} }
pub async fn find_playlist_or_create_by_name( #[cfg_attr(feature = "tracing", instrument)]
&self, pub async fn find_playlist_or_create_by_name(&self, name: &str) -> Result<Playlist> {
name: &str,
) -> Result<Playlist, Box<dyn Error>> {
let playlist = self.find_playlist_by_name(name).await?; let playlist = self.find_playlist_by_name(name).await?;
if let Some(playlist) = playlist { if let Some(playlist) = playlist {
return Ok(playlist); return Ok(playlist);
@@ -128,11 +146,8 @@ impl YoutubeClient {
Ok(playlist) Ok(playlist)
} }
pub async fn add_video_to_playlist( #[cfg_attr(feature = "tracing", instrument)]
&self, pub async fn add_video_to_playlist(&self, video: &Video, playlist: &Playlist) -> Result<()> {
video: &Video,
playlist: &Playlist,
) -> Result<(), Box<dyn Error>> {
let playlist_item = PlaylistItem { let playlist_item = PlaylistItem {
snippet: Some(PlaylistItemSnippet { snippet: Some(PlaylistItemSnippet {
playlist_id: Some(playlist.id.clone().unwrap()), playlist_id: Some(playlist.id.clone().unwrap()),
@@ -160,22 +175,25 @@ impl YoutubeClient {
let (res, _) = let (res, _) =
generic_check_backoff_youtube(&self.client, &playlist_item, insert_playlist_item) generic_check_backoff_youtube(&self.client, &playlist_item, insert_playlist_item)
.await??; .await
.map_err(|e| anyhow!("backoff error: {}", e))?
.context("insert playlist item returned an error")?;
if res.status().is_success() { if res.status().is_success() {
Ok(()) Ok(())
} else { } else {
Err(format!("got status: {}", res.status().as_u16()).into()) Err(anyhow!("got status: {}", res.status().as_u16()))
} }
} }
pub async fn upload_video<S: Into<String>, V: Into<Vec<String>>>( #[cfg_attr(feature = "tracing", instrument)]
pub async fn upload_video(
&self, &self,
path: impl AsRef<Path>, path: impl AsRef<Path> + Debug,
title: S, title: impl Into<String> + Debug,
description: S, description: impl Into<String> + Debug,
tags: V, tags: impl Into<Vec<String>> + Debug,
privacy_status: PrivacyStatus, privacy_status: PrivacyStatus,
) -> Result<Video, Box<dyn Error>> { ) -> Result<Video> {
info!("test 123"); info!("test 123");
let video = Video { let video = Video {
snippet: Some(VideoSnippet { snippet: Some(VideoSnippet {
@@ -212,7 +230,10 @@ impl YoutubeClient {
para: &UploadParameters, para: &UploadParameters,
) -> Result<(Response<Body>, Video), google_youtube3::Error> { ) -> Result<(Response<Body>, Video), google_youtube3::Error> {
info!("Opening file: {:?}", para.path); info!("Opening file: {:?}", para.path);
let stream = std::fs::File::open(&para.path)?; let stream = std::fs::File::open(&para.path).map_err(|e| {
error!("could not open file: {} error: {}", &para.path.display(), e);
e
})?;
info!("Uploading file: {:?}", para.path); info!("Uploading file: {:?}", para.path);
let insert_call = client.videos().insert(para.video.clone()); let insert_call = client.videos().insert(para.video.clone());
@@ -220,14 +241,19 @@ impl YoutubeClient {
let upload_call = insert_call.upload_resumable(stream, "video/mp4".parse().unwrap()); let upload_call = insert_call.upload_resumable(stream, "video/mp4".parse().unwrap());
// .upload(stream, "video/mp4".parse().unwrap()); // .upload(stream, "video/mp4".parse().unwrap());
info!("Upload request"); info!("Upload request");
let res = upload_call.await; let res = upload_call.await.map_err(|e| {
error!("upload call did not work: {}", e);
e
});
info!("Upload request done"); info!("Upload request done");
res res
} }
info!("Starting upload..."); info!("Starting upload...");
let (response, video) = let (response, video) = generic_check_backoff_youtube(&self.client, &params, upload_fn)
generic_check_backoff_youtube(&self.client, &params, upload_fn).await??; .await
.map_err(|e| anyhow!("backoff error: {}", e))?
.context("upload_fn returned an error")?;
// let (response, video) = exponential_backoff::youtube::check_backoff_youtube_upload( // let (response, video) = exponential_backoff::youtube::check_backoff_youtube_upload(
// &self.client, // &self.client,
@@ -245,7 +271,7 @@ impl YoutubeClient {
info!("Status: {}", response.status()); info!("Status: {}", response.status());
info!("Body: {:?}", response); info!("Body: {:?}", response);
info!("Video: {:?}", video); info!("Video: {:?}", video);
Err(format!("got status: {}", response.status().as_u16()).into()) Err(anyhow!("got status: {}", response.status().as_u16()))
} }
// return Ok(video); // return Ok(video);
@@ -264,7 +290,8 @@ impl YoutubeClient {
// } // }
// } // }
} }
async fn create_playlist(&self, name: &str) -> Result<Playlist, Box<dyn Error>> { #[cfg_attr(feature = "tracing", instrument)]
async fn create_playlist(&self, name: &str) -> Result<Playlist> {
let playlist = Playlist { let playlist = Playlist {
snippet: Some(PlaylistSnippet { snippet: Some(PlaylistSnippet {
title: Some(name.to_string()), title: Some(name.to_string()),
@@ -281,16 +308,20 @@ impl YoutubeClient {
} }
let (res, playlist) = let (res, playlist) =
generic_check_backoff_youtube(&self.client, &playlist, create_playlist).await??; generic_check_backoff_youtube(&self.client, &playlist, create_playlist)
.await
.map_err(|e| anyhow!("backoff error: {}", e))?
.map_err(|e| anyhow!("create playlist returned an error: {}", e))?;
if res.status().is_success() { if res.status().is_success() {
Ok(playlist) Ok(playlist)
} else { } else {
Err(format!("got status: {}", res.status().as_u16()).into()) Err(anyhow!("got status: {}", res.status().as_u16()))
} }
} }
} }
#[cfg_attr(feature = "tracing", instrument)]
pub async fn sample() -> Result<(), Box<dyn Error>> { pub async fn sample() -> Result<(), Box<dyn Error>> {
info!("Hello from the youtube lib!"); info!("Hello from the youtube lib!");
Ok(()) Ok(())

View File

@@ -2,3 +2,5 @@
pub use log::{debug, error, info, trace, warn}; pub use log::{debug, error, info, trace, warn};
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
pub use tracing::{debug, error, info, trace, warn}; pub use tracing::{debug, error, info, trace, warn};
pub use anyhow::Result;