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