initial commit

This commit is contained in:
yggverse 2026-04-08 12:05:46 +03:00
parent bb39b5b504
commit 8dcd80b9fe
6 changed files with 113 additions and 8 deletions

30
Cargo.lock generated
View file

@ -635,6 +635,16 @@ dependencies = [
"pin-project-lite",
]
[[package]]
name = "http_req"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92208b0986f414edaca8ede2c6962c979309346a9e8e19d07d0a7879aae1549e"
dependencies = [
"native-tls",
"unicase",
]
[[package]]
name = "httparse"
version = "1.10.1"
@ -851,6 +861,17 @@ dependencies = [
"serde_core",
]
[[package]]
name = "invidious"
version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa2cff01a915c4780785828f6bec4ad5f8d03f34307d4183ca210830a8a6d88d"
dependencies = [
"http_req",
"serde",
"serde_json",
]
[[package]]
name = "ipnet"
version = "2.12.0"
@ -1980,6 +2001,12 @@ version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
[[package]]
name = "unicase"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142"
[[package]]
name = "unicode-ident"
version = "1.0.24"
@ -2458,12 +2485,13 @@ dependencies = [
[[package]]
name = "ytd"
version = "0.3.2"
version = "0.4.0"
dependencies = [
"anyhow",
"chrono",
"clap",
"env_logger",
"invidious",
"log",
"redb",
"regex",

View file

@ -1,6 +1,6 @@
[package]
name = "ytd"
version = "0.3.2"
version = "0.4.0"
edition = "2024"
license = "MIT"
readme = "README.md"
@ -14,6 +14,7 @@ anyhow = "1.0.102"
chrono = "0.4.44"
clap = { version = "4.6", features = ["derive"] }
env_logger = "0.11.10"
invidious = "0.7.8"
log = "0.4.29"
redb = "4.0.0"
regex = "1.12.3"

View file

@ -21,6 +21,10 @@ sleep = 1
# exec = ""
# stdout_contains = [""]
# Supported values: youtube | invidious
provider = "youtube"
# Channels queue config
[channel.test]

View file

@ -12,6 +12,11 @@ pub struct Command {
#[derive(Deserialize)]
pub struct Config {
/// Supported values:
/// * youtube
/// * invidious
pub provider: String,
/// Channels parsed from the config file
pub channel: HashMap<String, Channel>,
/// Persist processed entries between sessions
pub database: PathBuf,

View file

@ -1,6 +1,7 @@
mod argument;
mod config;
mod database;
mod provider;
use argument::Argument;
use chrono::Local;
@ -8,8 +9,8 @@ use clap::Parser;
use config::Config;
use database::Database;
use log::*;
use provider::Provider;
use regex::Regex;
use rustypipe::client::RustyPipe;
use std::{env::var, process::Command, thread, time::Duration};
#[tokio::main]
@ -34,7 +35,11 @@ async fn main() {
let update = config.update.map(Duration::from_secs);
let sleep = config.sleep.map(Duration::from_secs);
let mut database = Database::new(&config.database).unwrap();
let rp = RustyPipe::new();
let client = if "youtube" == config.provider.to_lowercase() {
Provider::youtube()
} else {
Provider::invidious()
};
let channel_item_id_regex = Regex::new(r"^[A-z0-9_-]{11}$").unwrap();
loop {
@ -45,11 +50,10 @@ async fn main() {
info!("begin {} channels update...", config.channel.len());
for (c, channel) in &config.channel {
debug!("get `{c}` ({})...", channel.id);
match rp.query().channel_videos(&channel.id).await {
Ok(result) => {
let items = result.content.items;
match client.videos(&channel.id).await {
Ok(items) => {
debug!(
"received {:?} items to handle, limit: {:?}...",
"received {} items to handle, limit: {:?}...",
items.len(),
channel.items_limit
);

63
src/provider.rs Normal file
View file

@ -0,0 +1,63 @@
use anyhow::{Result, anyhow};
use invidious::{ClientSync, ClientSyncTrait};
use rustypipe::client::RustyPipe;
pub struct Video {
pub id: String,
pub name: String,
pub is_live: bool,
pub is_short: bool,
pub is_upcoming: bool,
}
pub enum Provider {
Youtube(RustyPipe),
Invidious(ClientSync),
}
impl Provider {
pub fn youtube() -> Self {
Self::Youtube(RustyPipe::new())
}
pub fn invidious() -> Self {
Self::Invidious(ClientSync::default())
}
pub async fn videos(&self, channel_id: &str) -> Result<Vec<Video>> {
let mut videos = Vec::new();
match self {
Provider::Youtube(client) => {
for video in client
.query()
.channel_videos(channel_id)
.await?
.content
.items
{
videos.push(Video {
id: video.id,
name: video.name,
is_live: video.is_live,
is_short: video.is_short,
is_upcoming: video.is_upcoming,
})
}
}
Provider::Invidious(client) => {
for video in client
.channel_videos(channel_id, None)
.map_err(|e| anyhow!("{e:?}"))?
.videos
{
videos.push(Video {
id: video.id,
name: video.title,
is_live: video.live,
is_short: video.length <= 60,
is_upcoming: video.upcoming,
})
}
}
}
Ok(videos)
}
}