mirror of
https://github.com/OMGeeky/google_youtube.git
synced 2026-02-23 15:49:49 +01:00
use anyhow result in most cases & some more tracing improvements
This commit is contained in:
@@ -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]
|
||||||
|
|||||||
121
src/lib.rs
121
src/lib.rs
@@ -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, ¶, list_playlist).await??;
|
generic_check_backoff_youtube(&self.client, ¶, 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(¶.path)?;
|
let stream = std::fs::File::open(¶.path).map_err(|e| {
|
||||||
|
error!("could not open file: {} error: {}", ¶.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, ¶ms, upload_fn)
|
||||||
generic_check_backoff_youtube(&self.client, ¶ms, 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(())
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user