youtube_rs/
lib.rs

1#![warn(clippy::all)]
2use cpython::*;
3use crate::thumbnail::ThumbnailArgs;
4use crate::liking::LikingArgs;
5pub use crate::video::*;
6use std::{fs::File, io::Read, path::Path};
7/// Video Rating
8/// ```
9/// use youtube_rs::liking::*;
10/// use youtube_rs::YTClient;
11///
12/// fn main() {
13///     let client = YTClient::from_secret_path("src/secret.json").unwrap();
14///     let rating = LikingArgs {
15///         // ID of the video you are going to like
16///         id:"sg4TxfwSeYs",
17///         // Type of rating you are going to give
18///         rating:VideoInteration::Like
19///     };
20///     client.rate_video_request(rating).unwrap();
21/// }
22/// ```
23pub mod liking;
24/// Thumbnail Editing
25pub mod thumbnail;
26/// Video Uploading
27/// ```
28/// // Put your own youtube secret file here
29/// let client = YTClient::from_secret_path("./secret.json").unwrap();
30/// let options = VideoData {
31///     title: "test video",
32///     desc: "cool description",
33///     keywords: Some("test,cool"),
34///     category:video::CategoryID::SciTech as u32,
35///     privacy_status: video::PrivacyStatus::Private,
36///     file: "./test.mp4",
37///     for_kids:false
38/// };
39/// Creates the settings for the video
40/// let opt = client.create_upload_options(options).unwrap();
41/// client.upload_request(opt).expect("Could not upload");
42/// ```
43pub mod video;
44
45static PYLIB:&str = include_str!("../pythonlib/lib.py");
46
47/// The client that is used to interface with the google youtube api.
48/// ```
49/// /* When inputing the secret make sure it is the oauth2 token and make sure it is a desktop
50/// application type */
51/// YTClient::new_from_path("./secret.json")
52/// ```
53pub struct YTClient {
54    gil:GILGuard,
55    module:PyObject,
56    client:PyObject
57}
58/// The YTClient is required for calling and using the youtube api in this library.
59impl YTClient {
60    /// Creates the client using thhe google youtube api secret json file. You can get the secret
61    /// json file from the google developers page. If you want to create the client using the path
62    /// the secret json file use the from_path(...) method instead.
63    pub fn new_from_secret(data:&str) -> Self {
64        let gil = Python::acquire_gil();
65        let py = gil.python();
66        // Included in the python standard library
67        let types = py.import("types").unwrap();
68        let module = types.call(py, "ModuleType",("lib",),None).unwrap();
69        let local = PyDict::new(py);
70        local.set_item(py, "code", PYLIB).unwrap();
71        local.set_item(py, "m", module.clone_ref(py)).unwrap();
72        py.run("exec(code,m.__dict__)", Some(&local), None).unwrap();
73        let client = module.call_method(py, "client_from_str", (data,), None).unwrap();
74        Self { gil, module, client }
75    }
76    /// DEPRICATED. This method is deprecated because it required the lib files to be present in
77    /// user rust program. Call new_from_secret(...) to create a new youtube client from a str.
78    #[deprecated]
79    pub fn from_secret(data:&str) -> Self {
80        let gil = Python::acquire_gil();
81        let py = gil.python();
82        // This will be used to import the lib module using import lib
83        // Okay to unwrap because they are all valid functions
84        let importlib = py.import("importlib.util").unwrap();
85        let sys = py.import("sys").unwrap();
86        // location of the library
87        let spec = importlib.call(py, "spec_from_file_location", ("lib","./pythonlib/lib.py",), None).unwrap();
88        let module = importlib.call(py, "module_from_spec", (spec.clone_ref(py),), None).unwrap();
89        let modules = sys.get(py, "modules").unwrap();
90        modules.set_item(py, "lib", module.clone_ref(py)).unwrap();
91        let loader = spec.getattr(py, "loader").unwrap();
92        loader.call_method(py, "exec_module", (module.clone_ref(py),), None).unwrap();
93
94        // Begin to create the client
95        let client = module.call_method(py, "client_from_str", (data,), None).unwrap();
96        Self { gil,module,client }
97    }
98    /// Creates the client using the path to the secret json file as input.
99    pub fn from_secret_path(path:&str) -> std::io::Result<Self> {
100        let dir = std::env::current_dir().unwrap();
101        println!("{dir:?}");
102        let mut file = File::open(path)?;
103        let mut buf = String::new();
104        file.read_to_string(&mut buf)?;
105        let client = Self::new_from_secret(&buf);
106        Ok(client)
107    }
108    /// Returns a struct that can be used to upload a video. Will return an error is the file given
109    /// doesn't exist.
110    pub fn create_upload_options(&self,video_data:VideoData) -> Result<UploadOptions,String> {
111        let py = self.gil.python();
112        if !Path::new(video_data.file).exists() { return Err(String::from("File does not exist")) }
113        let opt = self.module.call_method( py, "Options", (
114                video_data.title,
115                 video_data.desc,
116                 video_data.keywords.unwrap_or(""),
117                 &video_data.category.to_string(),
118                 video_data.privacy_status.to_str(),
119                 video_data.file,
120                 video_data.for_kids
121             ), None).unwrap();
122        Ok(UploadOptions { options: opt })
123    }
124    pub fn rate_video_request(&self,rating_data:LikingArgs) -> Result<(),impl std::fmt::Debug> {
125        let py = self.gil.python();
126        let interaction = self.module.call_method(py, "InteractionArgs", (rating_data.id,rating_data.rating.to_str()), None)?;
127        self.module.call_method(py, "interact_with_video", (self.client.clone_ref(py),interaction), None)?;
128        Ok::<(),PyErr>(())
129    }
130    /// Sends a request to upload a new video.
131    pub fn upload_request(&self,opt:UploadOptions) -> Result<(),impl std::fmt::Debug> {
132        let py = self.gil.python();
133        self.module.call_method(py, "upload_req", (self.client.clone_ref(py),opt.options), None)?;
134        Ok::<(),PyErr>(())
135    }
136    /// Sends a request to change the thumbnail of one of your videos.
137    pub fn set_thumbnail(&self,args:ThumbnailArgs) -> Result<(),impl std::fmt::Debug>{
138        let py = self.gil.python();
139        self.module.call_method(py, "set_thumbnail", (args.id,args.file), None)?;
140        Ok::<(),PyErr>(())
141    }
142}
143
144
145#[test]
146fn test_client() {
147    let client = YTClient::from_secret_path("src/secret.json").unwrap();
148    let video_data = VideoData {
149        title: "cool",
150        desc: "cool video",
151        keywords: Some("cool,video"),
152        category:video::CategoryID::SciTech as u32,
153        privacy_status:video::PrivacyStatus::Private,
154        file:"src/funny.mp4",
155        for_kids:false
156    };
157    let opt = client.create_upload_options(video_data).unwrap();
158    client.upload_request(opt).unwrap();
159}