gui
This commit is contained in:
parent
8b414b5181
commit
340f3572d5
|
@ -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"
|
||||||
|
|
|
@ -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,
|
||||||
|
|
84
src/lib.rs
84
src/lib.rs
|
@ -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(
|
||||||
|
¤t_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(
|
||||||
|
¤t_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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
161
src/main.rs
161
src/main.rs
|
@ -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: {}", ¤t_page.image, &config.cache_folder);
|
|
||||||
cache::download_page_image(&config.cache_folder.replace('~', &ggg::get_home()[..]), ¤t_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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue