This commit is contained in:
SadlyNotSappho 2023-09-12 12:07:24 -07:00
parent 8b414b5181
commit 340f3572d5
4 changed files with 236 additions and 19 deletions

View File

@ -7,7 +7,8 @@ edition = "2021"
[dependencies] [dependencies]
clap = { version = "4.3.3", features = ["derive"] } clap = { version = "4.3.3", features = ["derive"] }
iced = "0.9.0" color-print = "0.3.5"
iced = {version="0.10.0", features=["image", "debug", "tokio"]}
regex = "1.8.4" regex = "1.8.4"
reqwest = {version="0.11.18",features=["blocking"]} reqwest = {version="0.11.18",features=["blocking"]}
scraper = "0.16.0" scraper = "0.16.0"

View File

@ -1,15 +1,16 @@
use std::{fs, path::Path, process}; use std::{fs, path::Path, process};
use color_print::cprintln;
use crate::Page; use crate::Page;
pub async fn download_page_image(cache_path: &str, page: &Page) { pub async fn download_page_image(cache_path: &str, page: &Page) {
let replaced = cache_path.replace('~', &crate::get_home()[..]); // replace ~ with $HOME let replaced = cache_path.replace('~', &crate::get_home()[..]); // replace ~ with $HOME
crate::ensure_exists(&replaced); crate::ensure_exists(&replaced);
if Path::new(&format!("{cache_path}/{}.json", page.date)).exists() { if Path::new(&format!("{replaced}/{}.json", page.date)).exists() {
return; return
} }
cprintln!("<italics>downloading page image<italics>");
let image = match reqwest::get(&page.image).await { let image = match reqwest::get(&page.image).await {
Ok(image) => match image.bytes().await { Ok(image) => match image.bytes().await {
Ok(bytes) => bytes, Ok(bytes) => bytes,

View File

@ -138,7 +138,7 @@ pub struct Config {
pub cache_folder: String, pub cache_folder: String,
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Page { pub struct Page {
pub date: String, pub date: String,
pub next_page: Option<String>, pub next_page: Option<String>,
@ -154,7 +154,7 @@ pub async fn get_page(date: &str, config: &Config) -> Page {
)) ))
.exists() .exists()
{ {
match serde_json::from_str( match serde_json::from_str::<Page>(
&match fs::read_to_string( &match fs::read_to_string(
&format!( &format!(
"{}/{date}.json", "{}/{date}.json",
@ -163,18 +163,20 @@ pub async fn get_page(date: &str, config: &Config) -> Page {
) { ) {
Ok(var) => var, Ok(var) => var,
Err(why) => { Err(why) => {
eprintln!("lib::get_page: Couldn't read cache data: {why:?}"); eprintln!("lib::get_page: Couldn't read cache data for page {date}: {why:?}");
process::exit(1) process::exit(1)
} }
}[..], }[..],
) { ) {
Ok(var) => var, Ok(var) => var,
Err(why) => { Err(why) => {
eprintln!("lib::get_page: Couldn't serialize read cache data: {why:?}"); eprintln!(
"lib::get_page: Couldn't serialize read cache data for page {date}: {why:?}"
);
process::exit(1); process::exit(1);
} }
} };
} };
let page_html = &match match reqwest::get(format!( let page_html = &match match reqwest::get(format!(
"https://girlgeniusonline.com/comic.php?date={date}" "https://girlgeniusonline.com/comic.php?date={date}"
@ -210,13 +212,33 @@ pub async fn get_page(date: &str, config: &Config) -> Page {
let next = Selector::parse("#topnext").unwrap(); let next = Selector::parse("#topnext").unwrap();
let mut next_page = None; let mut next_page = None;
for element in parsed.select(&next) { for element in parsed.select(&next) {
next_page = Some(element.value().attr("href").unwrap().to_string()); next_page = element
.value()
.attr("href")
.map(|p| {
if p.is_empty() {
None
} else {
Some(link_to_datestring(p.to_string()).to_string())
}
})
.expect("??????????"); // i dont understand rust sometimes
} }
let prev = Selector::parse("#topprev").unwrap(); let prev = Selector::parse("#topprev").unwrap();
let mut prev_page = None; let mut prev_page = None;
for element in parsed.select(&prev) { for element in parsed.select(&prev) {
prev_page = Some(element.value().attr("href").unwrap().to_string()); prev_page = element
.value()
.attr("href")
.map(|p| {
if p.is_empty() {
None
} else {
Some(link_to_datestring(p.to_string()).to_string())
}
})
.expect("??????????"); // if you do please yell at me as to why that expect is required
} }
// check if cache/date.json exists // check if cache/date.json exists
@ -228,7 +250,7 @@ pub async fn get_page(date: &str, config: &Config) -> Page {
} }
Page { Page {
date: date.to_string(), date: link_to_datestring(date.to_string()).to_string(),
next_page, next_page,
prev_page, prev_page,
image, image,
@ -245,3 +267,47 @@ pub async fn current_page(config: &Config) -> Page {
page page
} }
} }
pub async fn next_page(config: &Config) -> Option<Page> {
let current_page = get_page(&config.latest_date[..], config).await;
if current_page.next_page.is_some() {
let page = get_page(
&current_page
.next_page
.expect("i shall *expect* (see what i did there) war crimes if this error pops up")
[..],
config,
)
.await;
if page.cached {
Some(page)
} else {
cache::download_page_image(&config.cache_folder[..], &page).await;
Some(page)
}
} else {
None
}
}
pub async fn prev_page(config: &Config) -> Option<Page> {
let current_page = get_page(&config.latest_date[..], config).await;
if current_page.prev_page.is_some() {
let page = get_page(
&current_page
.prev_page
.expect("i shall *expect* (see what i did there) war crimes if this error pops up")
[..],
config,
)
.await;
if page.cached {
Some(page)
} else {
cache::download_page_image(&config.cache_folder[..], &page).await;
Some(page)
}
} else {
None
}
}

View File

@ -1,5 +1,9 @@
use clap::Parser; use clap::Parser;
use ggg::cache;
use ggg::{current_page, get_home, read_config};
use iced::widget::{button, column, container, image, row, text};
use iced::{Application, Command, Element, Length, Theme, Settings};
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
@ -9,7 +13,7 @@ struct Args {
} }
#[tokio::main] #[tokio::main]
async fn main() { async fn main() -> iced::Result {
let args = Args::parse(); let args = Args::parse();
let config_file = args.config_file; let config_file = args.config_file;
@ -20,8 +24,153 @@ async fn main() {
ggg::ensure_exists(&config.cache_folder); ggg::ensure_exists(&config.cache_folder);
ggg::ensure_exists(&format!("{}/images", &config.cache_folder)[..]); ggg::ensure_exists(&format!("{}/images", &config.cache_folder)[..]);
let current_page = ggg::get_page(&config.latest_date, &config).await; Reader::run(Settings::default())
}
println!("image url: {}\ncache folder: {}", &current_page.image, &config.cache_folder);
cache::download_page_image(&config.cache_folder.replace('~', &ggg::get_home()[..]), &current_page).await; enum Reader {
Loading,
Loaded { page: Book },
}
#[derive(Debug, Clone)]
enum Message {
PageFound(Book),
NextPage,
PreviousPage,
}
impl Application for Reader {
type Message = Message;
type Theme = Theme;
type Executor = iced::executor::Default;
type Flags = ();
fn new(_flags: ()) -> (Reader, Command<Message>) {
(
Reader::Loading,
Command::perform(Book::current(), Message::PageFound),
)
}
fn title(&self) -> String {
format!("ggg") // i didn't feel like typing out .to_string(), plus this is easier to go
// back and change
}
fn update(&mut self, message: Message) -> Command<Message> {
match message {
Message::NextPage => match self {
Reader::Loading => Command::none(),
_ => {
*self = Reader::Loading;
Command::perform(Book::next_page(), Message::PageFound)
}
},
Message::PreviousPage => match self {
Reader::Loading => Command::none(),
_ => {
*self = Reader::Loading;
Command::perform(Book::prev_page(), Message::PageFound)
}
},
Message::PageFound(page) => {
*self = Reader::Loaded { page };
Command::none()
}
}
}
fn view(&self) -> Element<Message> {
let content = match self {
Reader::Loading => column![text("Loading...").size(40),].width(Length::Shrink),
Reader::Loaded { page } => column![
page.view(),
row![
// TODO: make these disable/enable if page.next and page.prev are false
button("Next").on_press(Message::NextPage),
button("Prev").on_press(Message::PreviousPage),
]
.spacing(20)
.align_items(iced::Alignment::Center)
]
.spacing(20)
.align_items(iced::Alignment::Center),
};
container(content)
.width(Length::Fill)
.height(Length::Fill)
.center_x()
.center_y()
.into()
}
}
#[derive(Debug, Clone)]
struct Book {
image: image::Handle,
// see above todo
next: bool,
prev: bool,
}
impl Book {
fn view(&self) -> Element<Message> {
row![image::viewer(self.image.clone())]
.spacing(20)
.align_items(iced::Alignment::Center)
.into()
}
async fn current() -> Book {
let args = Args::parse();
let config = read_config(&args.config_file[..]);
let page = current_page(&config).await;
let image = image::Handle::from_path(format!(
"{}/images/{}.jpg",
config.cache_folder.replace('~', &get_home()),
ggg::link_to_datestring(page.date)
));
Book {
image,
next: page.next_page.is_some(),
prev: page.prev_page.is_some(),
}
}
async fn next_page() -> Book {
let args = Args::parse();
let config = read_config(&args.config_file[..]);
let page = ggg::next_page(&config).await.unwrap();
let image = image::Handle::from_path(format!(
"{}/images/{}.jpg",
config.cache_folder.replace('~', &get_home()),
ggg::link_to_datestring(page.date)
));
Book {
image,
next: page.next_page.is_some(),
prev: page.prev_page.is_some(),
}
}
async fn prev_page() -> Book {
let args = Args::parse();
let config = read_config(&args.config_file[..]);
let page = ggg::prev_page(&config).await.unwrap();
let image = image::Handle::from_path(format!(
"{}/images/{}.jpg",
config.cache_folder.replace('~', &get_home()),
ggg::link_to_datestring(page.date)
));
Book {
image,
next: page.next_page.is_some(),
prev: page.prev_page.is_some(),
}
}
} }