Init repo
This commit is contained in:
commit
ca82cd5dbb
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/target
|
2020
Cargo.lock
generated
Normal file
2020
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
10
Cargo.toml
Normal file
10
Cargo.toml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "ytatomlink"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1.0.95"
|
||||||
|
clap = { version = "4.5.23", features = ["derive"] }
|
||||||
|
reqwest = { version = "0.12.12", features = ["blocking"] }
|
||||||
|
soup = "0.5.1"
|
14
README.md
Normal file
14
README.md
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# ytatomlink
|
||||||
|
|
||||||
|
Get the Atom link to a YouTube channel URL.
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage: ytatomlink <URL>
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
<URL> YouTube URL
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-h, --help Print help
|
||||||
|
-V, --version Print version
|
||||||
|
```
|
61
src/main.rs
Normal file
61
src/main.rs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
use anyhow::{
|
||||||
|
bail,
|
||||||
|
Context,
|
||||||
|
Result,
|
||||||
|
};
|
||||||
|
use clap::{
|
||||||
|
Parser,
|
||||||
|
};
|
||||||
|
use reqwest::{
|
||||||
|
Url,
|
||||||
|
};
|
||||||
|
use soup::prelude::{
|
||||||
|
Soup,
|
||||||
|
QueryBuilderExt,
|
||||||
|
NodeExt,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Parser)]
|
||||||
|
#[command(version, about, long_about = None, arg_required_else_help = true)]
|
||||||
|
struct Cli {
|
||||||
|
/// YouTube URL
|
||||||
|
url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
let cli = Cli::parse();
|
||||||
|
|
||||||
|
let url = Url::parse(&cli.url)
|
||||||
|
.context("Does not seem to be a URL")?;
|
||||||
|
|
||||||
|
let content = reqwest::blocking::get(url)
|
||||||
|
.context("Could not retrieve URL contents")?
|
||||||
|
.text()?;
|
||||||
|
|
||||||
|
let soup = Soup::new(&content);
|
||||||
|
let canonical_url = Url::parse(
|
||||||
|
&soup.tag("link").find_all()
|
||||||
|
.filter(|link| link.get("rel") == Some(String::from("canonical")))
|
||||||
|
.next()
|
||||||
|
.context("No canonical link provided")?
|
||||||
|
.get("href")
|
||||||
|
.context("Canonical link does not provide a URL")?
|
||||||
|
)
|
||||||
|
.context("Canonical URL is invalid")?;
|
||||||
|
|
||||||
|
if !&canonical_url.to_string().starts_with("https://www.youtube.com/channel/") {
|
||||||
|
bail!("Canonical URL does not refer to a YouTube channel");
|
||||||
|
}
|
||||||
|
|
||||||
|
let canonical_url_segments = canonical_url.path_segments()
|
||||||
|
.context("Failed to get URL segments")?
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let channel_id = canonical_url_segments.last()
|
||||||
|
.context("Failed to retrieve last URL segment")?;
|
||||||
|
|
||||||
|
let atom_feed_url = format!("https://www.youtube.com/feeds/videos.xml?channel_id={}", &channel_id);
|
||||||
|
|
||||||
|
println!("{}", &atom_feed_url);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user