modified: Cargo.lock
modified: Cargo.toml modified: src/main.rs new file: src/objects.rs
This commit is contained in:
parent
312dd9bc90
commit
d0422a7ad9
1571
Cargo.lock
generated
1571
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -4,3 +4,7 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
reqwest = { version = "0.12.12", features = ["json"] }
|
||||||
|
tokio = { version = "1", features = ["full"] }
|
||||||
|
serde = { version = "1.0.217", features = ["derive"] }
|
||||||
|
serde_json = "1.0.138"
|
73
src/main.rs
73
src/main.rs
@ -1,3 +1,74 @@
|
|||||||
fn main() {
|
use std::error::Error;
|
||||||
|
|
||||||
|
use objects::YoutubeChannel;
|
||||||
|
|
||||||
|
mod objects;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
println!("Hello, world!!");
|
println!("Hello, world!!");
|
||||||
|
let rid = get_channel("UCpDEe6JKiI5o8luXpk6UuiA".to_string()).await.unwrap();
|
||||||
|
get_playlists("UCpDEe6JKiI5o8luXpk6UuiA".to_string(), rid).await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_playlists(channel_id: String, request_id: String) -> Result<(), Box<dyn Error>> {
|
||||||
|
let client = reqwest::Client::new();
|
||||||
|
|
||||||
|
let d= serde_json::json!({
|
||||||
|
"context": {
|
||||||
|
"client": {
|
||||||
|
"clientName": "WEB",
|
||||||
|
"clientVersion": "2.20201210.01.00",
|
||||||
|
"originalUrl": "https://www.youtube.com/",
|
||||||
|
"platform": "DESKTOP",
|
||||||
|
"clientFormFactor": "UNKNOWN_FORM_FACTOR",
|
||||||
|
"newVisitorCookie": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"browseId": channel_id,
|
||||||
|
"params": request_id
|
||||||
|
});
|
||||||
|
|
||||||
|
let resp = client.post("https://www.youtube.com/youtubei/v1/browse?prettyPrint=false")
|
||||||
|
.body(d.to_string())
|
||||||
|
.send()
|
||||||
|
.await?
|
||||||
|
.text()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
println!("{}", resp);
|
||||||
|
|
||||||
|
let resp: YoutubeChannel = serde_json::from_str(&resp).unwrap();
|
||||||
|
|
||||||
|
println!("{:?}", resp.get_playlists());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_channel(channel_id: String) -> Result<String, Box<dyn Error>>{
|
||||||
|
let client = reqwest::Client::new();
|
||||||
|
|
||||||
|
let resp = client.post("https://www.youtube.com/youtubei/v1/browse?prettyPrint=false")
|
||||||
|
.body(r#"{
|
||||||
|
"context": {
|
||||||
|
"client": {
|
||||||
|
"clientName": "WEB",
|
||||||
|
"clientVersion": "2.20201210.01.00",
|
||||||
|
"originalUrl": "https://www.youtube.com/",
|
||||||
|
"platform": "DESKTOP",
|
||||||
|
"clientFormFactor": "UNKNOWN_FORM_FACTOR",
|
||||||
|
"newVisitorCookie": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"browseId": "UCpDEe6JKiI5o8luXpk6UuiA",
|
||||||
|
"params": "EgZ2aWRlb3M%3D"
|
||||||
|
}"#)
|
||||||
|
.send()
|
||||||
|
.await?
|
||||||
|
.text()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let resp: YoutubeChannel = serde_json::from_str(&resp).unwrap();
|
||||||
|
|
||||||
|
Ok(resp.get_playlists_request_str().unwrap())
|
||||||
}
|
}
|
157
src/objects.rs
Normal file
157
src/objects.rs
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct YoutubeChannel {
|
||||||
|
pub contents: ChannelContents,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ChannelContents {
|
||||||
|
pub twoColumnBrowseResultsRenderer: TwoColumns,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct TwoColumns {
|
||||||
|
pub tabs: Vec<ChannelTab>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ChannelTab {
|
||||||
|
pub tabRenderer: Option<TabRenderer>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct TabRenderer {
|
||||||
|
pub title: String,
|
||||||
|
pub endpoint: TabEndpoint,
|
||||||
|
pub content: Option<TabContent>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct SectionListRenderer{
|
||||||
|
pub contents: Option<Vec<Content>>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct Content {
|
||||||
|
pub itemSectionRenderer: Option<ItemSectionRenderer>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ItemSectionRenderer {
|
||||||
|
pub contents: Vec<ItemSectionContent>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ItemSectionContent {
|
||||||
|
pub gridRenderer: GridRenderer
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct GridRenderer {
|
||||||
|
pub items: Vec<GridItem>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct GridItem {
|
||||||
|
pub lockupViewModel: LookUpViewModel
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct LookUpViewModel {
|
||||||
|
pub metadata: ModelMetadata
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ModelMetadata {
|
||||||
|
pub lockupMetadataViewModel: LockupMetadataViewModel
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct LockupMetadataViewModel {
|
||||||
|
pub title: PlaylistTitle,
|
||||||
|
pub metadata: PlaylistMetadata,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct PlaylistMetadata {
|
||||||
|
pub contentMetadataViewModel: ContentMetadataViewModel
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ContentMetadataViewModel {
|
||||||
|
pub metadataRows: Vec<MetadataRow>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct MetadataRow {
|
||||||
|
pub metadataParts: Vec<PlaylistTitle>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct PlaylistTitle {
|
||||||
|
pub content: Option<String>,
|
||||||
|
pub commandRuns: Option<Vec<CommandRun>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct CommandRun {
|
||||||
|
onTap: OnTap
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct OnTap {
|
||||||
|
innertubeCommand: InnertubeCommand
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct InnertubeCommand {
|
||||||
|
pub browseEndpoint: BrowseEndpoint,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct TabContent {
|
||||||
|
pub sectionListRenderer: SectionListRenderer
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct TabEndpoint {
|
||||||
|
pub browseEndpoint: BrowseEndpoint,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct BrowseEndpoint {
|
||||||
|
pub browseId: String,
|
||||||
|
pub params: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl YoutubeChannel {
|
||||||
|
pub fn get_playlists_request_str(&self) -> Option<String> {
|
||||||
|
for tab in &self.contents.twoColumnBrowseResultsRenderer.tabs {
|
||||||
|
if let Some(t) = &tab.tabRenderer {
|
||||||
|
if t.title == String::from("Playlists") {
|
||||||
|
return Some(t.endpoint.browseEndpoint.params.as_ref().unwrap().clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_playlists(&self) -> Vec<(String, String)> {
|
||||||
|
let mut arr = Vec::new();
|
||||||
|
for tab in &self.contents.twoColumnBrowseResultsRenderer.tabs {
|
||||||
|
if let Some(t) = &tab.tabRenderer {
|
||||||
|
if t.title == String::from("Playlists") {
|
||||||
|
for item in &t.content.as_ref().unwrap().sectionListRenderer.contents.as_ref().unwrap()[0].itemSectionRenderer.as_ref().unwrap().contents[0].gridRenderer.items {
|
||||||
|
let title = item.lockupViewModel.metadata.lockupMetadataViewModel.title.content.clone().unwrap_or("!".to_string());
|
||||||
|
println!("{:#?}", item.lockupViewModel.metadata.lockupMetadataViewModel.metadata.contentMetadataViewModel.metadataRows[1].metadataParts[0]);
|
||||||
|
let url = item.lockupViewModel.metadata.lockupMetadataViewModel.metadata.contentMetadataViewModel.metadataRows[1].metadataParts[0].commandRuns.as_ref().unwrap()[0].onTap.innertubeCommand.browseEndpoint.browseId.clone();
|
||||||
|
arr.push((title, url));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
arr
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user