i5-add-tests (part 3) (#9)
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Reviewed-on: #9 Co-authored-by: Paul Campbell <pcampbell@kemitix.net> Co-committed-by: Paul Campbell <pcampbell@kemitix.net>
This commit is contained in:
parent
9057adddc3
commit
83081a2051
20 changed files with 182 additions and 148 deletions
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -1,5 +1,7 @@
|
||||||
target
|
target
|
||||||
*.mp3
|
*.mp3
|
||||||
*.webm
|
*.webm
|
||||||
/subscriptions.txt
|
subscriptions.txt
|
||||||
/downloaded.txt
|
downloaded.txt
|
||||||
|
coverage
|
||||||
|
*.profraw
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
pipeline:
|
steps:
|
||||||
build:
|
build:
|
||||||
image: rust
|
image: rust:latest
|
||||||
|
pull: true
|
||||||
commands:
|
commands:
|
||||||
- rustup component add rustfmt clippy
|
- rustup component add rustfmt clippy
|
||||||
- cargo --version
|
- cargo --version
|
||||||
|
|
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1014,6 +1014,7 @@ name = "podal"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atom_syndication",
|
"atom_syndication",
|
||||||
|
"bytes",
|
||||||
"clap",
|
"clap",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"scraper",
|
"scraper",
|
||||||
|
|
|
@ -10,6 +10,7 @@ atom_syndication = "0.12.1"
|
||||||
reqwest = { version = "0.11.18", features = ["json", "blocking"] }
|
reqwest = { version = "0.11.18", features = ["json", "blocking"] }
|
||||||
scraper = "0.17.1"
|
scraper = "0.17.1"
|
||||||
clap = "4.3.19"
|
clap = "4.3.19"
|
||||||
|
bytes = "1.4.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = "*"
|
tempfile = "*"
|
||||||
|
|
23
justfile
Normal file
23
justfile
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
coverage-init:
|
||||||
|
cargo install grcov
|
||||||
|
rustup component add llvm-tools
|
||||||
|
|
||||||
|
coverage:
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
rm -rf ./target
|
||||||
|
just clean
|
||||||
|
export RUSTFLAGS="-Zinstrument-coverage" LLVM_PROFILE_FILE="just-%p-%m.profraw"
|
||||||
|
cargo +nightly build
|
||||||
|
cargo +nightly test
|
||||||
|
just generate
|
||||||
|
just server
|
||||||
|
|
||||||
|
generate:
|
||||||
|
grcov . --binary-path ./target/debug/ -s . -t html --branch --ignore-not-existing --ignore "./target/" -o ./coverage/
|
||||||
|
|
||||||
|
server:
|
||||||
|
cd ./coverage && python3 -m http.server 8001
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf ./*.prof* ./coverage/
|
|
@ -1,12 +1,14 @@
|
||||||
use std::{fmt::Display, str::Utf8Error, string::FromUtf8Error};
|
use std::{str::Utf8Error, string::FromUtf8Error};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Error {
|
pub struct Error {
|
||||||
details: String,
|
pub details: String,
|
||||||
}
|
}
|
||||||
impl Display for Error {
|
impl Error {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
pub fn message(details: &str) -> Self {
|
||||||
f.write_str(self.details.to_string().as_str())
|
Self {
|
||||||
|
details: details.to_string(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl From<Utf8Error> for Error {
|
impl From<Utf8Error> for Error {
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
|
use crate::network::NetworkEnv;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
use crate::fetch::FetchGet;
|
pub fn find(site: &str, channel_name: &str, e: &NetworkEnv) -> Result<String> {
|
||||||
|
|
||||||
pub fn find(site: &str, channel_name: &str, e: &FetchGet) -> Result<String> {
|
|
||||||
if let Some(channel_prefix) = channel_name.chars().next() {
|
if let Some(channel_prefix) = channel_name.chars().next() {
|
||||||
if channel_prefix != '@' {
|
if channel_prefix != '@' {
|
||||||
return Err(format!("Channel Name must begin with an '@': {}", channel_name).into());
|
return Err(format!("Channel Name must begin with an '@': {}", channel_name).into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let channel_url = format!("{}{}", site, channel_name);
|
let channel_url = format!("{}{}", site, channel_name);
|
||||||
let response = (e)(&channel_url)?;
|
let response = (e.fetch_as_text)(&channel_url)?;
|
||||||
let rss_url = scraper::Html::parse_document(&response)
|
let rss_url = scraper::Html::parse_document(&response)
|
||||||
.select(&scraper::Selector::parse("link[title='RSS']").unwrap())
|
.select(&scraper::Selector::parse("link[title='RSS']").unwrap())
|
||||||
.next()
|
.next()
|
||||||
|
@ -23,15 +22,20 @@ pub fn find(site: &str, channel_name: &str, e: &FetchGet) -> Result<String> {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::fetch::Response;
|
|
||||||
|
use crate::errors::Error;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
#[test]
|
#[test]
|
||||||
fn finds_rss_url() -> Result<()> {
|
fn finds_rss_url() -> Result<()> {
|
||||||
//given
|
//given
|
||||||
let fetch_get = &(get as FetchGet);
|
let network_env = NetworkEnv {
|
||||||
|
fetch_as_text: dummy_fetch_as_text,
|
||||||
|
fetch_as_bytes: dummy_fetch_as_bytes,
|
||||||
|
download_as_mp3: dummy_download_as_mp3,
|
||||||
|
};
|
||||||
//when
|
//when
|
||||||
let result = find("site", "@channel", fetch_get)?;
|
let result = find("site", "@channel", &network_env)?;
|
||||||
//then
|
//then
|
||||||
assert_eq!(result, "the-rss-url");
|
assert_eq!(result, "the-rss-url");
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -40,15 +44,19 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn error_if_channel_name_is_invalid() -> Result<()> {
|
fn error_if_channel_name_is_invalid() -> Result<()> {
|
||||||
//given
|
//given
|
||||||
let fetch_get = &(get as FetchGet);
|
let network_env = NetworkEnv {
|
||||||
|
fetch_as_text: dummy_fetch_as_text,
|
||||||
|
fetch_as_bytes: dummy_fetch_as_bytes,
|
||||||
|
download_as_mp3: dummy_download_as_mp3,
|
||||||
|
};
|
||||||
//when
|
//when
|
||||||
let result = find("site", "invalid-channel-name", fetch_get);
|
let result = find("site", "invalid-channel-name", &network_env);
|
||||||
//then
|
//then
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(_url: &str) -> Result<Response> {
|
fn dummy_fetch_as_text(_url: &str) -> Result<String> {
|
||||||
Ok(r#"
|
Ok(r#"
|
||||||
<html>
|
<html>
|
||||||
<link title="RSS" href="the-rss-url">
|
<link title="RSS" href="the-rss-url">
|
||||||
|
@ -56,4 +64,10 @@ mod tests {
|
||||||
"#
|
"#
|
||||||
.to_string())
|
.to_string())
|
||||||
}
|
}
|
||||||
|
fn dummy_fetch_as_bytes(_url: &str) -> Result<bytes::Bytes> {
|
||||||
|
Err(Error::message("Not implemented"))
|
||||||
|
}
|
||||||
|
fn dummy_download_as_mp3(_url: &str) -> Result<()> {
|
||||||
|
Err(Error::message("Not implemented"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
use crate::prelude::*;
|
|
||||||
|
|
||||||
use atom_syndication::Feed;
|
|
||||||
|
|
||||||
pub fn reqwest_blocking_get(url: &str) -> Result<Feed> {
|
|
||||||
let content = reqwest::blocking::get(url)?.bytes()?;
|
|
||||||
let channel = Feed::read_from(&content[..])?;
|
|
||||||
Ok(channel)
|
|
||||||
}
|
|
|
@ -1,18 +1,14 @@
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
use crate::fetch::FetchGet;
|
use crate::network::NetworkEnv;
|
||||||
|
use atom_syndication::Feed;
|
||||||
|
|
||||||
mod find;
|
mod find;
|
||||||
mod get;
|
|
||||||
|
|
||||||
use atom_syndication::Feed;
|
|
||||||
pub use find::find;
|
pub use find::find;
|
||||||
pub use get::reqwest_blocking_get;
|
|
||||||
|
|
||||||
pub struct FeedEnv {
|
pub fn get(url: &str, e: &NetworkEnv) -> Result<Feed> {
|
||||||
pub find: FeedFind,
|
let content = (e.fetch_as_bytes)(url)?;
|
||||||
pub get: FeedGet,
|
let channel = Feed::read_from(&content[..])?;
|
||||||
|
Ok(channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type FeedFind = fn(&str, &str, &FetchGet) -> Result<String>;
|
|
||||||
pub type FeedGet = fn(&str) -> Result<Feed>;
|
|
||||||
|
|
34
src/fetch.rs
34
src/fetch.rs
|
@ -1,34 +0,0 @@
|
||||||
use crate::prelude::*;
|
|
||||||
|
|
||||||
use atom_syndication::Link;
|
|
||||||
use std::process::Command;
|
|
||||||
|
|
||||||
pub struct FetchEnv {
|
|
||||||
pub download: FetchDownload,
|
|
||||||
pub get: FetchGet,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type FetchDownload = fn(&Link) -> Result<()>;
|
|
||||||
pub type FetchGet = fn(&str) -> Result<Response>;
|
|
||||||
|
|
||||||
pub fn download(link: &Link) -> Result<()> {
|
|
||||||
let cmd = "yt-dlp";
|
|
||||||
// println!("{} --extract-audio --audio-format mp3 {}", cmd, &link.href);
|
|
||||||
let output = Command::new(cmd)
|
|
||||||
.arg("--extract-audio")
|
|
||||||
.arg("--audio-format")
|
|
||||||
.arg("mp3")
|
|
||||||
.arg(&link.href)
|
|
||||||
.output()?;
|
|
||||||
if !output.stderr.is_empty() {
|
|
||||||
eprintln!("Error: {}", String::from_utf8(output.stderr)?);
|
|
||||||
println!("{}", String::from_utf8(output.stdout)?);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Response = String;
|
|
||||||
|
|
||||||
pub fn get(url: &str) -> Result<Response> {
|
|
||||||
Ok(reqwest::blocking::get(url)?.text()?)
|
|
||||||
}
|
|
26
src/file/env.rs
Normal file
26
src/file/env.rs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
use std::fs::{File, OpenOptions};
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
pub struct FileEnv {
|
||||||
|
pub open: FileOpen,
|
||||||
|
pub append_line: FileAppendLine,
|
||||||
|
}
|
||||||
|
impl Default for FileEnv {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
open: |path| File::open(path),
|
||||||
|
append_line: |file_name, line| {
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.append(true)
|
||||||
|
.create(true)
|
||||||
|
.open(file_name)
|
||||||
|
.unwrap();
|
||||||
|
writeln!(file, "{}", line)?;
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub type FileOpen = fn(path: &str) -> std::io::Result<File>;
|
||||||
|
pub type FileAppendLine = fn(paht: &str, line: &str) -> std::io::Result<()>;
|
4
src/file/mod.rs
Normal file
4
src/file/mod.rs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
mod env;
|
||||||
|
pub mod read;
|
||||||
|
|
||||||
|
pub use env::FileEnv;
|
|
@ -1,10 +1,10 @@
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
use std::fs::File;
|
use crate::file::FileEnv;
|
||||||
use std::io::{BufRead, BufReader};
|
use std::io::{BufRead, BufReader};
|
||||||
|
|
||||||
pub fn lines_from(file_name: &str) -> Result<Vec<String>> {
|
pub fn lines_from(file_name: &str, e: &FileEnv) -> Result<Vec<String>> {
|
||||||
let file = File::open(file_name)?;
|
let file = (e.open)(file_name)?;
|
||||||
let reader = BufReader::new(file);
|
let reader = BufReader::new(file);
|
||||||
let mut lines = vec![];
|
let mut lines = vec![];
|
||||||
for line in reader.lines().flatten() {
|
for line in reader.lines().flatten() {
|
||||||
|
@ -27,11 +27,12 @@ mod tests {
|
||||||
//given
|
//given
|
||||||
let (dir, file_name) = create_text_file(
|
let (dir, file_name) = create_text_file(
|
||||||
"subscriptions.txt",
|
"subscriptions.txt",
|
||||||
include_bytes!("../test/data/subscriptions.txt"),
|
include_bytes!("../../test/data/subscriptions.txt"),
|
||||||
)?;
|
)?;
|
||||||
|
let file_env = FileEnv::default();
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let result = lines_from(&file_name)?;
|
let result = lines_from(&file_name, &file_env)?;
|
||||||
|
|
||||||
//then
|
//then
|
||||||
drop(dir);
|
drop(dir);
|
||||||
|
@ -44,11 +45,12 @@ mod tests {
|
||||||
//given
|
//given
|
||||||
let (dir, file_name) = create_text_file(
|
let (dir, file_name) = create_text_file(
|
||||||
"subscriptions.txt",
|
"subscriptions.txt",
|
||||||
include_bytes!("../test/data/subscriptions-blank-line.txt"),
|
include_bytes!("../../test/data/subscriptions-blank-line.txt"),
|
||||||
)?;
|
)?;
|
||||||
|
let file_env = FileEnv::default();
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let result = lines_from(&file_name)?;
|
let result = lines_from(&file_name, &file_env)?;
|
||||||
|
|
||||||
//then
|
//then
|
||||||
drop(dir);
|
drop(dir);
|
||||||
|
@ -61,11 +63,12 @@ mod tests {
|
||||||
//given
|
//given
|
||||||
let (dir, file_name) = create_text_file(
|
let (dir, file_name) = create_text_file(
|
||||||
"subscriptions.txt",
|
"subscriptions.txt",
|
||||||
include_bytes!("../test/data/subscriptions-comment.txt"),
|
include_bytes!("../../test/data/subscriptions-comment.txt"),
|
||||||
)?;
|
)?;
|
||||||
|
let file_env = FileEnv::default();
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let result = lines_from(&file_name)?;
|
let result = lines_from(&file_name, &file_env)?;
|
||||||
|
|
||||||
//then
|
//then
|
||||||
drop(dir);
|
drop(dir);
|
|
@ -1,18 +1,10 @@
|
||||||
|
use crate::file::FileEnv;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
use atom_syndication::Link;
|
use super::Link;
|
||||||
use std::fs::OpenOptions;
|
|
||||||
use std::io::prelude::*;
|
|
||||||
|
|
||||||
pub fn add(link: &Link, file_name: &str) -> Result<()> {
|
pub fn add(link: &Link, file_name: &str, e: &FileEnv) -> Result<()> {
|
||||||
let mut file = OpenOptions::new()
|
(e.append_line)(file_name, &link.href)?;
|
||||||
.write(true)
|
|
||||||
.append(true)
|
|
||||||
.create(true)
|
|
||||||
.open(file_name)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
writeln!(file, "{}", link.href)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +34,7 @@ mod tests {
|
||||||
length: None,
|
length: None,
|
||||||
};
|
};
|
||||||
//when
|
//when
|
||||||
add(&link, &file_name)?;
|
add(&link, &file_name, &FileEnv::default())?;
|
||||||
|
|
||||||
//then
|
//then
|
||||||
let content: Vec<String> = read_text_file(&file_name)?;
|
let content: Vec<String> = read_text_file(&file_name)?;
|
||||||
|
@ -71,7 +63,7 @@ mod tests {
|
||||||
length: None,
|
length: None,
|
||||||
};
|
};
|
||||||
//when
|
//when
|
||||||
add(&link, &file_name)?;
|
add(&link, &file_name, &FileEnv::default())?;
|
||||||
|
|
||||||
//then
|
//then
|
||||||
let content: Vec<String> = read_text_file(&file_name)?;
|
let content: Vec<String> = read_text_file(&file_name)?;
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use crate::prelude::*;
|
use crate::{file::FileEnv, prelude::*};
|
||||||
|
|
||||||
use atom_syndication::Link;
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::{BufRead, BufReader};
|
use std::io::{BufRead, BufReader};
|
||||||
|
|
||||||
pub fn find(link: &Link, file_name: &str) -> Result<bool> {
|
use super::Link;
|
||||||
if let Ok(file) = File::open(file_name) {
|
|
||||||
|
pub fn find(link: &Link, file_name: &str, e: &FileEnv) -> Result<bool> {
|
||||||
|
if let Ok(file) = (e.open)(file_name) {
|
||||||
let reader = BufReader::new(file);
|
let reader = BufReader::new(file);
|
||||||
for line in reader.lines() {
|
for line in reader.lines() {
|
||||||
if line? == link.href {
|
if line? == link.href {
|
||||||
|
@ -34,14 +34,13 @@ mod test {
|
||||||
title: None,
|
title: None,
|
||||||
length: None,
|
length: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let result = find(&link, &file_name)?;
|
let result = find(&link, &file_name, &FileEnv::default())?;
|
||||||
|
|
||||||
//then
|
//then
|
||||||
drop(dir);
|
drop(dir);
|
||||||
|
|
||||||
assert_eq!(result, true);
|
assert!(result);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -61,12 +60,12 @@ mod test {
|
||||||
};
|
};
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let result = find(&link, &file_name)?;
|
let result = find(&link, &file_name, &FileEnv::default())?;
|
||||||
|
|
||||||
//then
|
//then
|
||||||
drop(dir);
|
drop(dir);
|
||||||
|
|
||||||
assert_eq!(result, false);
|
assert!(!result);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -88,12 +87,12 @@ mod test {
|
||||||
};
|
};
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let result = find(&link, &file_name)?;
|
let result = find(&link, &file_name, &FileEnv::default())?;
|
||||||
|
|
||||||
//then
|
//then
|
||||||
drop(dir);
|
drop(dir);
|
||||||
|
|
||||||
assert_eq!(result, false);
|
assert!(!result);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,7 @@
|
||||||
use crate::prelude::*;
|
|
||||||
|
|
||||||
mod add;
|
mod add;
|
||||||
mod find;
|
mod find;
|
||||||
|
|
||||||
pub use add::add;
|
pub use add::add;
|
||||||
pub use find::find;
|
pub use find::find;
|
||||||
|
|
||||||
type Link = atom_syndication::Link;
|
pub type Link = atom_syndication::Link;
|
||||||
|
|
||||||
pub struct HistoryEnv {
|
|
||||||
pub find: HistoryFind,
|
|
||||||
pub add: HistoryAdd,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type HistoryFind = fn(&Link, &str) -> Result<bool>;
|
|
||||||
pub type HistoryAdd = fn(&Link, &str) -> Result<()>;
|
|
||||||
|
|
29
src/lib.rs
29
src/lib.rs
|
@ -1,34 +1,33 @@
|
||||||
|
use prelude::*;
|
||||||
|
|
||||||
mod errors;
|
mod errors;
|
||||||
pub mod feed;
|
pub mod feed;
|
||||||
pub mod fetch;
|
pub mod file;
|
||||||
pub mod history;
|
pub mod history;
|
||||||
|
pub mod network;
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
mod subscriptions;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_utils;
|
mod test_utils;
|
||||||
|
|
||||||
use feed::FeedEnv;
|
use file::FileEnv;
|
||||||
use fetch::FetchEnv;
|
use network::NetworkEnv;
|
||||||
use history::HistoryEnv;
|
|
||||||
use prelude::*;
|
|
||||||
|
|
||||||
pub struct Env {
|
pub struct Env {
|
||||||
pub feed: FeedEnv,
|
pub network: NetworkEnv,
|
||||||
pub history: HistoryEnv,
|
pub file: FileEnv,
|
||||||
pub fetch: FetchEnv,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(subscriptions: &str, history: &str, site: &str, e: Env) -> Result<()> {
|
pub fn run(subscriptions: &str, history: &str, site: &str, e: Env) -> Result<()> {
|
||||||
for channel_name in subscriptions::lines_from(subscriptions)? {
|
for channel_name in file::read::lines_from(subscriptions, &e.file)? {
|
||||||
println!("Channel: {}", channel_name);
|
println!("Channel: {}", channel_name);
|
||||||
let feed_url = (e.feed.find)(site, &channel_name, &e.fetch.get)?;
|
let feed_url = feed::find(site, &channel_name, &e.network)?;
|
||||||
for entry in (e.feed.get)(&feed_url)?.entries() {
|
for entry in feed::get(&feed_url, &e.network)?.entries() {
|
||||||
if let Some(link) = entry.links().get(0).cloned() {
|
if let Some(link) = entry.links().get(0).cloned() {
|
||||||
if !(e.history.find)(&link, history)? {
|
if !history::find(&link, history, &e.file)? {
|
||||||
println!("Downloading {}: {}", &channel_name, entry.title().as_str());
|
println!("Downloading {}: {}", &channel_name, entry.title().as_str());
|
||||||
(e.fetch.download)(&link)?;
|
(e.network.download_as_mp3)(&link.href)?;
|
||||||
(e.history.add)(&link, history)?;
|
history::add(&link, history, &e.file)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
19
src/main.rs
19
src/main.rs
|
@ -1,7 +1,7 @@
|
||||||
|
use podal::file::FileEnv;
|
||||||
|
use podal::network::NetworkEnv;
|
||||||
use podal::prelude::*;
|
use podal::prelude::*;
|
||||||
|
|
||||||
use podal::{feed::FeedEnv, fetch::FetchEnv, history::HistoryEnv};
|
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
println!("Podal");
|
println!("Podal");
|
||||||
let subscriptions = "subscriptions.txt";
|
let subscriptions = "subscriptions.txt";
|
||||||
|
@ -13,19 +13,8 @@ fn main() -> Result<()> {
|
||||||
history,
|
history,
|
||||||
site,
|
site,
|
||||||
podal::Env {
|
podal::Env {
|
||||||
feed: FeedEnv {
|
network: NetworkEnv::default(),
|
||||||
find: podal::feed::find,
|
file: FileEnv::default(),
|
||||||
get: podal::feed::reqwest_blocking_get,
|
|
||||||
},
|
|
||||||
|
|
||||||
history: HistoryEnv {
|
|
||||||
find: podal::history::find,
|
|
||||||
add: podal::history::add,
|
|
||||||
},
|
|
||||||
fetch: FetchEnv {
|
|
||||||
download: podal::fetch::download,
|
|
||||||
get: podal::fetch::get,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|
32
src/network/env.rs
Normal file
32
src/network/env.rs
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
pub struct NetworkEnv {
|
||||||
|
pub fetch_as_text: fn(url: &str) -> Result<String>,
|
||||||
|
pub fetch_as_bytes: fn(url: &str) -> Result<bytes::Bytes>,
|
||||||
|
pub download_as_mp3: fn(url: &str) -> Result<()>,
|
||||||
|
}
|
||||||
|
impl Default for NetworkEnv {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
fetch_as_text: |url| Ok(reqwest::blocking::get(url)?.text()?),
|
||||||
|
fetch_as_bytes: |url| Ok(reqwest::blocking::get(url)?.bytes()?),
|
||||||
|
download_as_mp3: |url| {
|
||||||
|
let cmd = "yt-dlp";
|
||||||
|
// println!("{} --extract-audio --audio-format mp3 {}", cmd, &link.href);
|
||||||
|
let output = Command::new(cmd)
|
||||||
|
.arg("--extract-audio")
|
||||||
|
.arg("--audio-format")
|
||||||
|
.arg("mp3")
|
||||||
|
.arg(url)
|
||||||
|
.output()?;
|
||||||
|
if !output.stderr.is_empty() {
|
||||||
|
eprintln!("Error: {}", String::from_utf8(output.stderr)?);
|
||||||
|
println!("{}", String::from_utf8(output.stdout)?);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3
src/network/mod.rs
Normal file
3
src/network/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
mod env;
|
||||||
|
|
||||||
|
pub use env::NetworkEnv;
|
Loading…
Add table
Reference in a new issue