|
@@ -10,6 +10,7 @@ use std::{
|
|
|
};
|
|
|
use std::{thread, time::Duration};
|
|
|
|
|
|
+mod config;
|
|
|
mod fetch;
|
|
|
mod parse;
|
|
|
|
|
@@ -26,6 +27,10 @@ struct Cli {
|
|
|
#[arg(short, long, default_value = "ESV")]
|
|
|
version: String,
|
|
|
|
|
|
+ /// User Agent
|
|
|
+ #[arg(short, long, action=clap::ArgAction::SetTrue)]
|
|
|
+ agent_update: bool,
|
|
|
+
|
|
|
#[command(subcommand)]
|
|
|
command: Option<Commands>,
|
|
|
}
|
|
@@ -58,14 +63,19 @@ enum Commands {
|
|
|
Test {},
|
|
|
}
|
|
|
|
|
|
+const CONFIG_FILE : &str = "app.config";
|
|
|
+
|
|
|
+/*
|
|
|
static APP_USER_AGENT: &str =
|
|
|
- "Mozilla/5.0 (X11; Linux x86_64; rv:134.0) Gecko/20100101 Firefox/134.0";
|
|
|
+ "Mozilla/5.0 (X11; Linux x86_64; rv:135.0) Gecko/20100101 Firefox/135.0";
|
|
|
+*/
|
|
|
|
|
|
// Not needed, I process relative URLs correctly now.
|
|
|
// static BASE_URL: &str = "https://www.bible.com";
|
|
|
|
|
|
static VOD_URL: &str = "https://www.bible.com/verse-of-the-day";
|
|
|
|
|
|
+/*
|
|
|
static VERSION_URLS: LazyLock<HashMap<&str, &str>> = LazyLock::new(|| {
|
|
|
HashMap::from([
|
|
|
("ESV", "https://www.bible.com/bible/59/GEN.1.ESV"),
|
|
@@ -76,6 +86,7 @@ static VERSION_URLS: LazyLock<HashMap<&str, &str>> = LazyLock::new(|| {
|
|
|
("YLT98", "https://www.bible.com/bible/821/GEN.1.YLT98"),
|
|
|
])
|
|
|
});
|
|
|
+*/
|
|
|
|
|
|
static BOOKS: LazyLock<Vec<&str>> = LazyLock::new(|| {
|
|
|
Vec::from([
|
|
@@ -148,10 +159,35 @@ fn find_files(base_dir: &str, version: &str) -> Vec<String> {
|
|
|
// https://www.bible.com/verse-of-the-day
|
|
|
|
|
|
fn main() -> Result<()> {
|
|
|
+ let mut config = config::read_config(CONFIG_FILE)?;
|
|
|
+
|
|
|
let cli = Cli::parse();
|
|
|
// println!("Work Dir: {:?}", cli.work);
|
|
|
// println!("Bible: {:?}", cli.bible);
|
|
|
|
|
|
+ if !config.versions.contains_key(cli.version.as_str()) {
|
|
|
+ println!("Sorry, I don't know about Bible Version [{}].", cli.version);
|
|
|
+ println!("I do know about the following:");
|
|
|
+
|
|
|
+ // Keys in arbitrary order.
|
|
|
+ for (name, _) in config.versions.iter() {
|
|
|
+ println!(" {}", name);
|
|
|
+ }
|
|
|
+ return Ok(());
|
|
|
+ }
|
|
|
+
|
|
|
+ if cli.agent_update {
|
|
|
+ if let Ok(new_agent) = fetch::agent_update(&config.user_agent) {
|
|
|
+ config.user_agent = new_agent;
|
|
|
+ println!("User agent now {}", &config.user_agent);
|
|
|
+ config::write_config(CONFIG_FILE, &config)?;
|
|
|
+ return Ok(());
|
|
|
+ } else {
|
|
|
+ println!("User agent OK.");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
if !VERSION_URLS.contains_key(cli.version.as_str()) {
|
|
|
println!("Sorry, I don't know about Bible Version [{}].", cli.version);
|
|
|
println!("I do know about the following:");
|
|
@@ -162,14 +198,15 @@ fn main() -> Result<()> {
|
|
|
}
|
|
|
return Ok(());
|
|
|
}
|
|
|
+ */
|
|
|
|
|
|
match &cli.command {
|
|
|
Some(Commands::Fetch { delay }) => {
|
|
|
let client = reqwest::blocking::Client::builder()
|
|
|
- .user_agent(APP_USER_AGENT)
|
|
|
+ .user_agent(&config.user_agent)
|
|
|
.build()?;
|
|
|
// .unwrap();
|
|
|
- let mut url = VERSION_URLS[cli.version.as_str()].to_string();
|
|
|
+ let mut url = config.versions[cli.version.as_str()].to_string();
|
|
|
println!("Fetch! [{}] with delay {} secs.", cli.version, delay);
|
|
|
let mut more = true;
|
|
|
let mut cache_hit_once = true;
|
|
@@ -184,20 +221,22 @@ fn main() -> Result<()> {
|
|
|
url.as_str(),
|
|
|
)?;
|
|
|
|
|
|
- let next_chapter = parse::find_next_chapter(&result.html, &url);
|
|
|
+ let next_chapter = parse::find_next_chapter(&result.html);
|
|
|
|
|
|
if let Ok(next_url) = next_chapter {
|
|
|
// Ok! We have something
|
|
|
// more = true;
|
|
|
|
|
|
- /*
|
|
|
+ /*
|
|
|
if next_url.starts_with("/") {
|
|
|
url = String::from(BASE_URL) + &next_url;
|
|
|
} else {
|
|
|
url = next_url.to_string();
|
|
|
}
|
|
|
*/
|
|
|
- url = next_url;
|
|
|
+ if let Ok(abs_url) = fetch::relative_to_absolute(&next_url, &url) {
|
|
|
+ url = abs_url;
|
|
|
+ }
|
|
|
} else {
|
|
|
// We didn't find the Next Chapter link, so stop.
|
|
|
more = false;
|
|
@@ -329,7 +368,7 @@ fn main() -> Result<()> {
|
|
|
|
|
|
Some(Commands::Verse { fetch: _ }) => {
|
|
|
let client = reqwest::blocking::Client::builder()
|
|
|
- .user_agent(APP_USER_AGENT)
|
|
|
+ .user_agent(&config.user_agent)
|
|
|
.build()?;
|
|
|
|
|
|
println!("Verse of the day.");
|
|
@@ -349,33 +388,48 @@ fn main() -> Result<()> {
|
|
|
println!("Verse: {}", v.verse);
|
|
|
println!("Ref: {}", v.reference);
|
|
|
println!("------");
|
|
|
- };
|
|
|
+ }
|
|
|
}
|
|
|
Some(Commands::Test {}) => {
|
|
|
println!("Testing...");
|
|
|
+ let client = reqwest::blocking::Client::builder()
|
|
|
+ .user_agent(&config.user_agent)
|
|
|
+ .build()?;
|
|
|
+
|
|
|
+ // They are using react. There's a token request, which allows them to fetch the daily reading...
|
|
|
+ let odb = fetch::fetch(&client, "https://www.odbm.org/");
|
|
|
+ // See the .har file for more details.
|
|
|
+
|
|
|
+ if let Ok(html) = odb {
|
|
|
+ println!("{}", html);
|
|
|
+ } else {
|
|
|
+ println!("Fetch error: {:?}", odb.unwrap_err());
|
|
|
+ }
|
|
|
+
|
|
|
+ if false {
|
|
|
+ let path = Path::new(&cli.work).join("GEN.1.NIV");
|
|
|
+ let buffer = std::fs::read_to_string(path).unwrap();
|
|
|
+ let document = scraper::Html::parse_document(&buffer);
|
|
|
+
|
|
|
+ let span_data_usfm = scraper::Selector::parse("span[data-usfm]").unwrap();
|
|
|
+ let _span_class = scraper::Selector::parse("span[class]").unwrap();
|
|
|
+ let span_selector = scraper::Selector::parse("span").unwrap();
|
|
|
|
|
|
- let path = Path::new(&cli.work).join("GEN.1.NIV");
|
|
|
- let buffer = std::fs::read_to_string(path).unwrap();
|
|
|
- let document = scraper::Html::parse_document(&buffer);
|
|
|
-
|
|
|
- let span_data_usfm = scraper::Selector::parse("span[data-usfm]").unwrap();
|
|
|
- let _span_class = scraper::Selector::parse("span[class]").unwrap();
|
|
|
- let span_selector = scraper::Selector::parse("span").unwrap();
|
|
|
-
|
|
|
- for span in document.select(&span_data_usfm) {
|
|
|
- if let Some(data) = span.attr("data-usfm") {
|
|
|
- println!("data-usfm {}:", data);
|
|
|
- let mut lines = Vec::<&str>::new();
|
|
|
- for data_span in span.select(&span_selector) {
|
|
|
- if let Some(data_class) = data_span.attr("class") {
|
|
|
- if data_class.contains("content") {
|
|
|
- let mut text = data_span.text().collect::<Vec<_>>();
|
|
|
- println!("{} {:?}", data, text);
|
|
|
- lines.append(&mut text);
|
|
|
+ for span in document.select(&span_data_usfm) {
|
|
|
+ if let Some(data) = span.attr("data-usfm") {
|
|
|
+ println!("data-usfm {}:", data);
|
|
|
+ let mut lines = Vec::<&str>::new();
|
|
|
+ for data_span in span.select(&span_selector) {
|
|
|
+ if let Some(data_class) = data_span.attr("class") {
|
|
|
+ if data_class.contains("content") {
|
|
|
+ let mut text = data_span.text().collect::<Vec<_>>();
|
|
|
+ println!("{} {:?}", data, text);
|
|
|
+ lines.append(&mut text);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
+ println!("data {} lines {:?}", data, lines);
|
|
|
}
|
|
|
- println!("data {} lines {:?}", data, lines);
|
|
|
}
|
|
|
}
|
|
|
/*
|