From 75634c4a644bfb8e21049e4f54841a62834b596a Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 01:48:42 -0500 Subject: [PATCH 01/84] Add template for mangairo --- src/rust/en.mangairo/.cargo/config | 2 + src/rust/en.mangairo/Cargo.toml | 19 ++++++++++ src/rust/en.mangairo/build.ps1 | 27 ++++++++++++++ src/rust/en.mangairo/build.sh | 6 +++ src/rust/en.mangairo/res/Icon.png | Bin 0 -> 70 bytes src/rust/en.mangairo/res/filters.json | 49 +++++++++++++++++++++++++ src/rust/en.mangairo/res/settings.json | 29 +++++++++++++++ src/rust/en.mangairo/res/source.json | 16 ++++++++ src/rust/en.mangairo/src/lib.rs | 42 +++++++++++++++++++++ 9 files changed, 190 insertions(+) create mode 100644 src/rust/en.mangairo/.cargo/config create mode 100644 src/rust/en.mangairo/Cargo.toml create mode 100644 src/rust/en.mangairo/build.ps1 create mode 100644 src/rust/en.mangairo/build.sh create mode 100644 src/rust/en.mangairo/res/Icon.png create mode 100644 src/rust/en.mangairo/res/filters.json create mode 100644 src/rust/en.mangairo/res/settings.json create mode 100644 src/rust/en.mangairo/res/source.json create mode 100644 src/rust/en.mangairo/src/lib.rs diff --git a/src/rust/en.mangairo/.cargo/config b/src/rust/en.mangairo/.cargo/config new file mode 100644 index 000000000..f4e8c002f --- /dev/null +++ b/src/rust/en.mangairo/.cargo/config @@ -0,0 +1,2 @@ +[build] +target = "wasm32-unknown-unknown" diff --git a/src/rust/en.mangairo/Cargo.toml b/src/rust/en.mangairo/Cargo.toml new file mode 100644 index 000000000..a18b61da3 --- /dev/null +++ b/src/rust/en.mangairo/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "mangairo" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" +opt-level = "s" +strip = true +lto = true + +[dependencies] +aidoku = { git = "https://github.com/Aidoku/aidoku-rs" } diff --git a/src/rust/en.mangairo/build.ps1 b/src/rust/en.mangairo/build.ps1 new file mode 100644 index 000000000..803b75bc5 --- /dev/null +++ b/src/rust/en.mangairo/build.ps1 @@ -0,0 +1,27 @@ +function Package-Source { + param ( + [Parameter(Mandatory = $true, Position = 0)] + [String[]]$Name, + + [switch]$Build + ) + $Name | ForEach-Object { + $source = $_ + if ($Build) { + Write-Output "building $source" + cargo +nightly build --release + } + + Write-Output "packaging $source" + New-Item -ItemType Directory -Path target/wasm32-unknown-unknown/release/Payload -Force | Out-Null + Copy-Item res/* target/wasm32-unknown-unknown/release/Payload -ErrorAction SilentlyContinue + Copy-Item sources/$source/res/* target/wasm32-unknown-unknown/release/Payload -ErrorAction SilentlyContinue + Set-Location target/wasm32-unknown-unknown/release + Copy-Item "$source.wasm" Payload/main.wasm + Compress-Archive -Force -DestinationPath "../../../$source.aix" -Path Payload + Remove-Item -Recurse -Force Payload/ + Set-Location ../../.. + } +} + +Package-Source mangairo -Build diff --git a/src/rust/en.mangairo/build.sh b/src/rust/en.mangairo/build.sh new file mode 100644 index 000000000..53ff3a99d --- /dev/null +++ b/src/rust/en.mangairo/build.sh @@ -0,0 +1,6 @@ +cargo +nightly build --release +mkdir -p target/wasm32-unknown-unknown/release/Payload +cp res/* target/wasm32-unknown-unknown/release/Payload +cp target/wasm32-unknown-unknown/release/*.wasm target/wasm32-unknown-unknown/release/Payload/main.wasm +cd target/wasm32-unknown-unknown/release ; zip -r package.aix Payload +mv package.aix ../../../package.aix diff --git a/src/rust/en.mangairo/res/Icon.png b/src/rust/en.mangairo/res/Icon.png new file mode 100644 index 0000000000000000000000000000000000000000..52c591798eb01fe73a43a2e863e34ec04731e9a2 GIT binary patch literal 70 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx1|;Q0k92}1TpU9xZYBRY|6#uz16wmw{gkyA RUIQf, _: i32) -> Result { + todo!() +} + +#[get_manga_listing] +fn get_manga_listing(_: Listing, _: i32) -> Result { + todo!() +} + +#[get_manga_details] +fn get_manga_details(_: String) -> Result { + todo!() +} + +#[get_chapter_list] +fn get_chapter_list(_: String) -> Result> { + todo!() +} + +#[get_page_list] +fn get_page_list(_: String) -> Result> { + todo!() +} + +#[modify_image_request] +fn modify_image_request(_: Request) { + todo!() +} + +#[handle_url] +fn handle_url(_: String) -> Result { + todo!() +} From 523157f83527c0181af570603a9314ae91f07434 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 02:10:25 -0500 Subject: [PATCH 02/84] Create Cargo.lock --- src/rust/en.mangairo/Cargo.lock | 91 +++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 src/rust/en.mangairo/Cargo.lock diff --git a/src/rust/en.mangairo/Cargo.lock b/src/rust/en.mangairo/Cargo.lock new file mode 100644 index 000000000..d6b4d1334 --- /dev/null +++ b/src/rust/en.mangairo/Cargo.lock @@ -0,0 +1,91 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aidoku" +version = "0.2.0" +source = "git+https://github.com/Aidoku/aidoku-rs#004bddabade7b24c58cf925b08f90dd093b00c9d" +dependencies = [ + "aidoku_imports", + "aidoku_macros", + "aidoku_proc_macros", + "dlmalloc", +] + +[[package]] +name = "aidoku_imports" +version = "0.2.0" +source = "git+https://github.com/Aidoku/aidoku-rs#004bddabade7b24c58cf925b08f90dd093b00c9d" + +[[package]] +name = "aidoku_macros" +version = "0.1.0" +source = "git+https://github.com/Aidoku/aidoku-rs#004bddabade7b24c58cf925b08f90dd093b00c9d" + +[[package]] +name = "aidoku_proc_macros" +version = "0.2.0" +source = "git+https://github.com/Aidoku/aidoku-rs#004bddabade7b24c58cf925b08f90dd093b00c9d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dlmalloc" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "203540e710bfadb90e5e29930baf5d10270cec1f43ab34f46f78b147b2de715a" +dependencies = [ + "libc", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "mangairo" +version = "0.1.0" +dependencies = [ + "aidoku", +] + +[[package]] +name = "proc-macro2" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" From 7166d91d7544d8b151fc8caffbb3e7698271e5f0 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 02:10:44 -0500 Subject: [PATCH 03/84] Update lib.rs --- src/rust/en.mangairo/src/lib.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/rust/en.mangairo/src/lib.rs b/src/rust/en.mangairo/src/lib.rs index d57cb3be1..7d0b9213e 100644 --- a/src/rust/en.mangairo/src/lib.rs +++ b/src/rust/en.mangairo/src/lib.rs @@ -3,9 +3,14 @@ use aidoku::{ error::Result, prelude::*, std::{String, Vec}, - Chapter, Filter, Listing, Manga, MangaPageResult, Page, DeepLink + Chapter, DeepLink, Filter, Listing, Manga, MangaPageResult, Page, }; +#[initialize] +fn initialize() { + todo!() +} + #[get_manga_list] fn get_manga_list(_: Vec, _: i32) -> Result { todo!() @@ -27,7 +32,7 @@ fn get_chapter_list(_: String) -> Result> { } #[get_page_list] -fn get_page_list(_: String) -> Result> { +fn get_page_list(_chapter_id: String, _manga_id: String) -> Result> { todo!() } From 95d438a032f347bced751e296618f9164726ac79 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 02:10:56 -0500 Subject: [PATCH 04/84] Update mangairo source info --- src/rust/en.mangairo/res/source.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/rust/en.mangairo/res/source.json b/src/rust/en.mangairo/res/source.json index f528a6784..4bce31d11 100644 --- a/src/rust/en.mangairo/res/source.json +++ b/src/rust/en.mangairo/res/source.json @@ -4,13 +4,15 @@ "lang": "en", "name": "MangaIro", "version": 1, - "url": "https://w.mangairo.com/home", + "url": "https://w.mangairo.com/", "nsfw": 1 }, "languages": [ ], "listings": [ - + { "name": "Hot" }, + { "name": "Latest" }, + { "name": "Completed" } ] } From f75425297d2f691905301b9463a919c2a5b63a35 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 02:50:23 -0500 Subject: [PATCH 05/84] Update function argument names --- src/rust/en.mangairo/src/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rust/en.mangairo/src/lib.rs b/src/rust/en.mangairo/src/lib.rs index 7d0b9213e..580acd529 100644 --- a/src/rust/en.mangairo/src/lib.rs +++ b/src/rust/en.mangairo/src/lib.rs @@ -12,22 +12,22 @@ fn initialize() { } #[get_manga_list] -fn get_manga_list(_: Vec, _: i32) -> Result { +fn get_manga_list(_filters: Vec, _page: i32) -> Result { todo!() } #[get_manga_listing] -fn get_manga_listing(_: Listing, _: i32) -> Result { +fn get_manga_listing(_listing: Listing, _page: i32) -> Result { todo!() } #[get_manga_details] -fn get_manga_details(_: String) -> Result { +fn get_manga_details(_id: String) -> Result { todo!() } #[get_chapter_list] -fn get_chapter_list(_: String) -> Result> { +fn get_chapter_list(_id: String) -> Result> { todo!() } @@ -37,11 +37,11 @@ fn get_page_list(_chapter_id: String, _manga_id: String) -> Result> { } #[modify_image_request] -fn modify_image_request(_: Request) { +fn modify_image_request(_request: Request) { todo!() } #[handle_url] -fn handle_url(_: String) -> Result { +fn handle_url(_url: String) -> Result { todo!() } From fbc9db438d8abe06f04fee01f0762c94cfe39c37 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 03:22:29 -0500 Subject: [PATCH 06/84] Add boilerplate parser for mangaairo --- src/rust/en.mangairo/src/lib.rs | 82 ++++++++++++++++++++++-------- src/rust/en.mangairo/src/parser.rs | 82 ++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+), 20 deletions(-) create mode 100644 src/rust/en.mangairo/src/parser.rs diff --git a/src/rust/en.mangairo/src/lib.rs b/src/rust/en.mangairo/src/lib.rs index 580acd529..5dcbd5ed3 100644 --- a/src/rust/en.mangairo/src/lib.rs +++ b/src/rust/en.mangairo/src/lib.rs @@ -6,42 +6,84 @@ use aidoku::{ Chapter, DeepLink, Filter, Listing, Manga, MangaPageResult, Page, }; -#[initialize] -fn initialize() { - todo!() -} +mod parser; +use parser::{BASE_URL, USER_AGENT}; #[get_manga_list] -fn get_manga_list(_filters: Vec, _page: i32) -> Result { - todo!() -} +fn get_manga_list(filters: Vec, page: i32) -> Result { + let mut result: Vec = Vec::new(); -#[get_manga_listing] -fn get_manga_listing(_listing: Listing, _page: i32) -> Result { - todo!() + let mut url = String::new(); + parser::get_filtered_url(filters, page, &mut url); + let html = Request::new(url.as_str(), HttpMethod::Get).html()?; + + if url.contains("search") { + parser::parse_search(html, &mut result); + } else { + parser::parse_recents(html, &mut result); + } + + if result.len() >= 50 { + Ok(MangaPageResult { + manga: result, + has_more: true, + }) + } else { + Ok(MangaPageResult { + manga: result, + has_more: false, + }) + } } #[get_manga_details] -fn get_manga_details(_id: String) -> Result { - todo!() +fn get_manga_details(manga_id: String) -> Result { + let url = format!("https://w.mangairo.com/{}", &manga_id); + let html = Request::new(url.as_str(), HttpMethod::Get).html()?; + parser::parse_manga(html, manga_id) +} + +#[get_manga_listing] +fn get_manga_listing(listing: Listing, page: i32) -> Result { + let url = match listing.name.as_str() { + "Latest" => format!("{BASE_URL}/manga-list/type-latest/ctg-all/state-all/page-{page}"), + "Hot" => format!("{BASE_URL}/manga-list/type-topview/ctg-all/state-all/page-{page}"), + // TODO: Make "Completed" a filter + "Completed" => { + format!("{BASE_URL}/manga-list/type-latest/ctg-all/state-completed/page-{page}") + } + _ => String::from(BASE_URL), + }; + parser::parse_manga_listing(url) } #[get_chapter_list] -fn get_chapter_list(_id: String) -> Result> { - todo!() +fn get_chapter_list(manga_id: String) -> Result> { + let url = format!("https://w.mangairo.com/{}", &manga_id); + let html = Request::new(url.as_str(), HttpMethod::Get).html()?; + parser::get_chapter_list(html) } #[get_page_list] -fn get_page_list(_chapter_id: String, _manga_id: String) -> Result> { - todo!() +fn get_page_list(chapter_id: String, manga_id: String) -> Result> { + let url = format!("https://w.mangairo.com/{}/{}", &manga_id, &chapter_id); + let html = Request::new(url.as_str(), HttpMethod::Get).html()?; + parser::get_page_list(html) } #[modify_image_request] -fn modify_image_request(_request: Request) { - todo!() +fn modify_image_request(request: Request) { + request + .header("Referer", BASE_URL) + .header("User-Agent", USER_AGENT); } #[handle_url] -fn handle_url(_url: String) -> Result { - todo!() +fn handle_url(url: String) -> Result { + let parsed_manga_id = parser::parse_incoming_url(url); + + Ok(DeepLink { + manga: Some(get_manga_details(parsed_manga_id)?), + chapter: None, + }) } diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs new file mode 100644 index 000000000..36f0157bf --- /dev/null +++ b/src/rust/en.mangairo/src/parser.rs @@ -0,0 +1,82 @@ +use aidoku::{ + error::Result, prelude::*, std::html::Node, std::String, std::Vec, Chapter, Filter, FilterType, + Manga, MangaContentRating, MangaStatus, MangaViewer, Page, +}; + +pub const BASE_URL: &str = "https://w.mangairo.com"; +pub const USER_AGENT: &str = "Mozilla/5.0 (Macintosh; Intel Mac OS X 13_3_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36"; + +pub fn parse_recents(html: Node, result: &mut Vec) { + todo!() +} + +pub fn parse_search(html: Node, result: &mut Vec) { + todo!() +} + +pub fn parse_manga(obj: Node, id: String) -> Result { + todo!() +} + +pub fn get_chapter_list(obj: Node) -> Result> { + todo!() +} + +pub fn parse_manga_listing(url: String) -> Result { + todo!() +} + +pub fn get_page_list(obj: Node) -> Result> { + todo!() +} + +pub fn get_filtered_url(filters: Vec, page: i32, url: &mut String) { + todo!() +} + +pub fn parse_incoming_url(url: String) -> String { + todo!() +} + +// HELPER FUNCTIONS + +pub fn i32_to_string(mut integer: i32) -> String { + if integer == 0 { + return String::from("0"); + } + let mut string = String::with_capacity(11); + let pos = if integer < 0 { + string.insert(0, '-'); + 1 + } else { + 0 + }; + while integer != 0 { + let mut digit = integer % 10; + if pos == 1 { + digit *= -1; + } + string.insert(pos, char::from_u32((digit as u32) + ('0' as u32)).unwrap()); + integer /= 10; + } + string +} + +pub fn urlencode(string: String) -> String { + let mut result: Vec = Vec::with_capacity(string.len() * 3); + let hex = "0123456789abcdef".as_bytes(); + let bytes = string.as_bytes(); + + for byte in bytes { + let curr = *byte; + if curr.is_ascii_lowercase() || curr.is_ascii_uppercase() || curr.is_ascii_digit() { + result.push(curr); + } else { + result.push(b'%'); + result.push(hex[curr as usize >> 4]); + result.push(hex[curr as usize & 15]); + } + } + + String::from_utf8(result).unwrap_or_default() +} From 7937af852b49ddb382342bd0d4fcc01d42d0a1f6 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 15:06:47 -0500 Subject: [PATCH 07/84] Fix imports for mangairo --- src/rust/en.mangairo/src/lib.rs | 5 ++++- src/rust/en.mangairo/src/parser.rs | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/rust/en.mangairo/src/lib.rs b/src/rust/en.mangairo/src/lib.rs index 5dcbd5ed3..9943836f9 100644 --- a/src/rust/en.mangairo/src/lib.rs +++ b/src/rust/en.mangairo/src/lib.rs @@ -2,7 +2,10 @@ use aidoku::{ error::Result, prelude::*, - std::{String, Vec}, + std::{ + net::{HttpMethod, Request}, + String, Vec, + }, Chapter, DeepLink, Filter, Listing, Manga, MangaPageResult, Page, }; diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index 36f0157bf..bc18edb78 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -1,6 +1,6 @@ use aidoku::{ error::Result, prelude::*, std::html::Node, std::String, std::Vec, Chapter, Filter, FilterType, - Manga, MangaContentRating, MangaStatus, MangaViewer, Page, + Manga, MangaContentRating, MangaStatus, MangaViewer, Page, MangaPageResult, }; pub const BASE_URL: &str = "https://w.mangairo.com"; From 71a1d06d4467f0aaa7a00ffa1b19e590f8055150 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 16:22:57 -0500 Subject: [PATCH 08/84] Implement mangairo filters --- src/rust/en.mangairo/res/filters.json | 88 ++++++++++++------- src/rust/en.mangairo/src/lib.rs | 2 +- src/rust/en.mangairo/src/parser.rs | 119 +++++++++++++++++++++++++- 3 files changed, 175 insertions(+), 34 deletions(-) diff --git a/src/rust/en.mangairo/res/filters.json b/src/rust/en.mangairo/res/filters.json index e1276f060..e0090d0a9 100644 --- a/src/rust/en.mangairo/res/filters.json +++ b/src/rust/en.mangairo/res/filters.json @@ -6,44 +6,70 @@ "type": "author" }, { - "type": "group", - "name": "A group filter", - "filters": [ - { - "type": "check", - "name": "A checkbox", - "default": true - } - ] - }, - { - "type": "group", - "name": "Another group filter", - "filters": [ - { - "type": "genre", - "name": "A genre", - "canExclude": true - } + "type": "select", + "name": "Sort", + "options": [ + "Latest", + "Newest", + "Hot" ] }, { "type": "select", - "name": "A select filter", + "name": "Status", "options": [ - + "All", + "Ongoing", + "Completed" ] }, { - "type": "sort", - "name": "A sorter", - "canAscend": true, + "type": "select", + "name": "Genre", "options": [ - - ], - "default": { - "index": 0, - "ascending": false - } + "All", + "Action", + "Adult", + "Adventure", + "Comedy", + "Cooking", + "Doujinshi", + "Drama", + "Ecchi", + "Erotica", + "Fantasy", + "Gender bender", + "Harem", + "Historical", + "Horror", + "Isekai", + "Josei", + "Manhua", + "Manhwa", + "Martial arts", + "Mature", + "Mecha", + "Medical", + "Mystery", + "One shot", + "Pornographic", + "Phychological", + "Romance", + "School life", + "Sci fi", + "Seinen", + "Shoujo", + "Shoujo ai", + "Shounen", + "Shounen ai", + "Slice of Life", + "Smut", + "Sports", + "Supernatural", + "Tragedy", + "Webtoons", + "Yaoi", + "Yuri" + ] } -] +] \ No newline at end of file diff --git a/src/rust/en.mangairo/src/lib.rs b/src/rust/en.mangairo/src/lib.rs index 9943836f9..4dbdb8681 100644 --- a/src/rust/en.mangairo/src/lib.rs +++ b/src/rust/en.mangairo/src/lib.rs @@ -50,8 +50,8 @@ fn get_manga_details(manga_id: String) -> Result { fn get_manga_listing(listing: Listing, page: i32) -> Result { let url = match listing.name.as_str() { "Latest" => format!("{BASE_URL}/manga-list/type-latest/ctg-all/state-all/page-{page}"), + "New Releases" => format!("{BASE_URL}/manga-list/type-newest/ctg-7/state-all/page-{page}"), "Hot" => format!("{BASE_URL}/manga-list/type-topview/ctg-all/state-all/page-{page}"), - // TODO: Make "Completed" a filter "Completed" => { format!("{BASE_URL}/manga-list/type-latest/ctg-all/state-completed/page-{page}") } diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index bc18edb78..6ad24ee30 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -1,6 +1,6 @@ use aidoku::{ error::Result, prelude::*, std::html::Node, std::String, std::Vec, Chapter, Filter, FilterType, - Manga, MangaContentRating, MangaStatus, MangaViewer, Page, MangaPageResult, + Manga, MangaContentRating, MangaPageResult, MangaStatus, MangaViewer, Page, }; pub const BASE_URL: &str = "https://w.mangairo.com"; @@ -31,7 +31,122 @@ pub fn get_page_list(obj: Node) -> Result> { } pub fn get_filtered_url(filters: Vec, page: i32, url: &mut String) { - todo!() + let mut is_searching = false; + let mut search_string = String::new(); + url.push_str("https://w.mangairo.com"); + + let title_filter: Filter = filters + .iter() + .find(|&x| x.kind == FilterType::Title) + .unwrap() + .clone(); + let author_filter: Filter = filters + .iter() + .find(|&x| x.kind == FilterType::Author) + .unwrap() + .clone(); + let status_filter: Filter = filters + .iter() + .find(|&x| x.kind == FilterType::Select && x.name == "Status") + .unwrap() + .clone(); + let sort_filter: Filter = filters + .iter() + .find(|&x| x.kind == FilterType::Select && x.name == "Sort") + .unwrap() + .clone(); + let genre_filter: Filter = filters + .iter() + .find(|&x| x.kind == FilterType::Select && x.name == "Genre") + .unwrap() + .clone(); + + if let Ok(filter_value) = title_filter.value.as_string() { + // filter_value.read().to_lowercase(); + search_string.push_str(urlencode(filter_value.read().to_lowercase()).as_str()); + is_searching = true; + } + + if let Ok(filter_value) = author_filter.value.as_string() { + // filter_value.read().to_lowercase(); + search_string.push_str(urlencode(filter_value.read().to_lowercase()).as_str()); + is_searching = true; + } + + if is_searching { + url.push_str("/list/search/"); + url.push_str(&search_string); + url.push_str("?page="); + url.push_str(&i32_to_string(page)); + } else { + url.push_str("/manga-list/type-"); + match sort_filter.value.as_int().unwrap_or(0) { + 0 => url.push_str("latest"), + 1 => url.push_str("newest"), + 2 => url.push_str("topview"), + _ => url.push_str("latest"), + } + // Genre + url.push_str("/ctg-"); + match genre_filter.value.as_int().unwrap_or(0) { + 0 => url.push_str("all"), // "All", + 1 => url.push_str("2"), // "Action", + 2 => url.push_str("3"), // "Adult", + 3 => url.push_str("4"), // "Adventure", + 4 => url.push_str("6"), // "Comedy", + 5 => url.push_str("7"), // "Cooking", + 6 => url.push_str("9"), // "Doujinshi", + 7 => url.push_str("10"), // "Drama", + 8 => url.push_str("11"), // "Ecchi", + 9 => url.push_str("48"), // "Erotica", + 10 => url.push_str("12"), // "Fantasy", + 11 => url.push_str("13"), // "Gender bender", + 12 => url.push_str("14"), // "Harem", + 13 => url.push_str("15"), // "Historical", + 14 => url.push_str("16"), // "Horror", + 15 => url.push_str("45"), // "Isekai", + 16 => url.push_str("17"), // "Josei", + 17 => url.push_str("44"), // "Manhua", + 18 => url.push_str("43"), // "Manhwa", + 19 => url.push_str("19"), // "Martial arts", + 20 => url.push_str("20"), // "Mature", + 21 => url.push_str("21"), // "Mecha", + 22 => url.push_str("22"), // "Medical", + 23 => url.push_str("24"), // "Mystery", + 24 => url.push_str("25"), // "One shot", + 25 => url.push_str("47"), // "Pornographic", + 26 => url.push_str("26"), // "Phychological", + 27 => url.push_str("27"), // "Romance", + 28 => url.push_str("28"), // "School life", + 29 => url.push_str("29"), // "Sci fi", + 30 => url.push_str("30"), // "Seinen", + 31 => url.push_str("31"), // "Shoujo", + 32 => url.push_str("32"), // "Shoujo ai", + 33 => url.push_str("33"), // "Shounen", + 34 => url.push_str("34"), // "Shounen ai", + 35 => url.push_str("35"), // "Slice of Life", + 36 => url.push_str("36"), // "Smut", + 37 => url.push_str("37"), // "Sports", + 38 => url.push_str("38"), // "Supernatural", + 39 => url.push_str("39"), // "Tragedy", + 40 => url.push_str("40"), // "Webtoons", + 41 => url.push_str("41"), // "Yaoi", + 42 => url.push_str("42"), // "Yuri" + _ => url.push_str("all"), + } + + // State + url.push_str("/state-"); + match status_filter.value.as_int().unwrap_or(0) { + 0 => url.push_str("all"), + 1 => url.push_str("ongoing"), + 2 => url.push_str("completed"), + _ => url.push_str("all"), + } + + url.push_str("/page-"); + url.push_str(&i32_to_string(page)); + } } pub fn parse_incoming_url(url: String) -> String { From f4114d1e1ae56f22499e386f67e3e7547bdb475c Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 16:27:48 -0500 Subject: [PATCH 09/84] Update parser.rs --- src/rust/en.mangairo/src/parser.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index 6ad24ee30..b07321be1 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -80,7 +80,7 @@ pub fn get_filtered_url(filters: Vec, page: i32, url: &mut String) { url.push_str(&i32_to_string(page)); } else { url.push_str("/manga-list/type-"); - match sort_filter.value.as_int().unwrap_or(0) { + match sort_filter.value.as_int().unwrap_or(-1) { 0 => url.push_str("latest"), 1 => url.push_str("newest"), 2 => url.push_str("topview"), @@ -88,7 +88,7 @@ pub fn get_filtered_url(filters: Vec, page: i32, url: &mut String) { } // Genre url.push_str("/ctg-"); - match genre_filter.value.as_int().unwrap_or(0) { + match genre_filter.value.as_int().unwrap_or(-1) { 0 => url.push_str("all"), // "All", 1 => url.push_str("2"), // "Action", 2 => url.push_str("3"), // "Adult", From cc105ed622ec81de82c5103945f038f05e87ac7d Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 17:05:49 -0500 Subject: [PATCH 10/84] Handle deep links --- src/rust/en.mangairo/Cargo.lock | 45 ++++++++++++++++++++++++++++++ src/rust/en.mangairo/Cargo.toml | 1 + src/rust/en.mangairo/src/lib.rs | 21 ++++++++++---- src/rust/en.mangairo/src/parser.rs | 32 +++++++++++++++++++-- 4 files changed, 91 insertions(+), 8 deletions(-) diff --git a/src/rust/en.mangairo/Cargo.lock b/src/rust/en.mangairo/Cargo.lock index d6b4d1334..3ee2c5746 100644 --- a/src/rust/en.mangairo/Cargo.lock +++ b/src/rust/en.mangairo/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aho-corasick" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c378d78423fdad8089616f827526ee33c19f2fddbd5de1629152c9593ba4783" +dependencies = [ + "memchr", +] + [[package]] name = "aidoku" version = "0.2.0" @@ -53,8 +62,15 @@ name = "mangairo" version = "0.1.0" dependencies = [ "aidoku", + "regex", ] +[[package]] +name = "memchr" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" + [[package]] name = "proc-macro2" version = "1.0.66" @@ -73,6 +89,35 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "regex" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" + [[package]] name = "syn" version = "1.0.109" diff --git a/src/rust/en.mangairo/Cargo.toml b/src/rust/en.mangairo/Cargo.toml index a18b61da3..a8c4d3352 100644 --- a/src/rust/en.mangairo/Cargo.toml +++ b/src/rust/en.mangairo/Cargo.toml @@ -17,3 +17,4 @@ lto = true [dependencies] aidoku = { git = "https://github.com/Aidoku/aidoku-rs" } +regex = "1" diff --git a/src/rust/en.mangairo/src/lib.rs b/src/rust/en.mangairo/src/lib.rs index 4dbdb8681..38d86e062 100644 --- a/src/rust/en.mangairo/src/lib.rs +++ b/src/rust/en.mangairo/src/lib.rs @@ -1,6 +1,6 @@ #![no_std] use aidoku::{ - error::Result, + error::{AidokuError, Result}, prelude::*, std::{ net::{HttpMethod, Request}, @@ -23,7 +23,7 @@ fn get_manga_list(filters: Vec, page: i32) -> Result { if url.contains("search") { parser::parse_search(html, &mut result); } else { - parser::parse_recents(html, &mut result); + parser::parse_manga_list(html, &mut result); } if result.len() >= 50 { @@ -83,10 +83,21 @@ fn modify_image_request(request: Request) { #[handle_url] fn handle_url(url: String) -> Result { - let parsed_manga_id = parser::parse_incoming_url(url); + let parsed_manga_id = parser::parse_incoming_url_manga_id(url.clone()); + let parsed_chapter_id = parser::parse_incoming_url_chapter_id(url); + if parsed_manga_id.is_none() { + panic!("unhandled url"); + } Ok(DeepLink { - manga: Some(get_manga_details(parsed_manga_id)?), - chapter: None, + manga: Some(get_manga_details(parsed_manga_id.unwrap())?), + chapter: if parsed_chapter_id.is_some() { + Some(Chapter { + id: parsed_chapter_id.unwrap(), + ..Default::default() + }) + } else { + None + }, }) } diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index b07321be1..93c1181e4 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -1,12 +1,15 @@ +extern crate regex; + use aidoku::{ error::Result, prelude::*, std::html::Node, std::String, std::Vec, Chapter, Filter, FilterType, Manga, MangaContentRating, MangaPageResult, MangaStatus, MangaViewer, Page, }; +use regex::Regex; pub const BASE_URL: &str = "https://w.mangairo.com"; pub const USER_AGENT: &str = "Mozilla/5.0 (Macintosh; Intel Mac OS X 13_3_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36"; -pub fn parse_recents(html: Node, result: &mut Vec) { +pub fn parse_manga_list(html: Node, result: &mut Vec) { todo!() } @@ -149,8 +152,31 @@ pub fn get_filtered_url(filters: Vec, page: i32, url: &mut String) { } } -pub fn parse_incoming_url(url: String) -> String { - todo!() +pub fn parse_incoming_url_manga_id(url: String) -> Option { + // https://w.mangairo.com/story-pn279847 + // https://w.mangairo.com/story-pn279847/chapter-52 + let manga_id_pattern = r"https?://w\.mangairo\.com/([^/]+)/?.*"; + let regex = Regex::new(manga_id_pattern).unwrap(); + + if let Some(captures) = regex.captures(url.as_str()) { + if let Some(manga_id) = captures.get(1) { + return manga_id.as_str(); + } + } + return Option::None; +} + +pub fn parse_incoming_url_chapter_id(url: String) -> Option { + // https://w.mangairo.com/story-pn279847/chapter-52 + let manga_id_pattern = r"https?://w\.mangairo\.com/([^/]+)/([^/]+)/?.*"; + let regex = Regex::new(manga_id_pattern).unwrap(); + + if let Some(captures) = regex.captures(url.as_str()) { + if let Some(chapter_id) = captures.get(2) { + return chapter_id.as_str(); + } + } + return Option::None; } // HELPER FUNCTIONS From fd2480ac3a06a9fe0242b5c469ef242632cde344 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 17:08:07 -0500 Subject: [PATCH 11/84] Update build.sh --- src/rust/en.mangairo/build.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 src/rust/en.mangairo/build.sh diff --git a/src/rust/en.mangairo/build.sh b/src/rust/en.mangairo/build.sh old mode 100644 new mode 100755 From f0ef730bc93c0ee961db6677df0014aab97678e3 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 17:15:01 -0500 Subject: [PATCH 12/84] Handle url without regex --- src/rust/en.mangairo/Cargo.lock | 45 ------------------------------ src/rust/en.mangairo/Cargo.toml | 1 - src/rust/en.mangairo/src/parser.rs | 36 +++++++++++------------- 3 files changed, 17 insertions(+), 65 deletions(-) diff --git a/src/rust/en.mangairo/Cargo.lock b/src/rust/en.mangairo/Cargo.lock index 3ee2c5746..d6b4d1334 100644 --- a/src/rust/en.mangairo/Cargo.lock +++ b/src/rust/en.mangairo/Cargo.lock @@ -2,15 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "aho-corasick" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c378d78423fdad8089616f827526ee33c19f2fddbd5de1629152c9593ba4783" -dependencies = [ - "memchr", -] - [[package]] name = "aidoku" version = "0.2.0" @@ -62,15 +53,8 @@ name = "mangairo" version = "0.1.0" dependencies = [ "aidoku", - "regex", ] -[[package]] -name = "memchr" -version = "2.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" - [[package]] name = "proc-macro2" version = "1.0.66" @@ -89,35 +73,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "regex" -version = "1.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" - [[package]] name = "syn" version = "1.0.109" diff --git a/src/rust/en.mangairo/Cargo.toml b/src/rust/en.mangairo/Cargo.toml index a8c4d3352..a18b61da3 100644 --- a/src/rust/en.mangairo/Cargo.toml +++ b/src/rust/en.mangairo/Cargo.toml @@ -17,4 +17,3 @@ lto = true [dependencies] aidoku = { git = "https://github.com/Aidoku/aidoku-rs" } -regex = "1" diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index 93c1181e4..582225443 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -1,10 +1,7 @@ -extern crate regex; - use aidoku::{ error::Result, prelude::*, std::html::Node, std::String, std::Vec, Chapter, Filter, FilterType, Manga, MangaContentRating, MangaPageResult, MangaStatus, MangaViewer, Page, }; -use regex::Regex; pub const BASE_URL: &str = "https://w.mangairo.com"; pub const USER_AGENT: &str = "Mozilla/5.0 (Macintosh; Intel Mac OS X 13_3_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36"; @@ -155,28 +152,29 @@ pub fn get_filtered_url(filters: Vec, page: i32, url: &mut String) { pub fn parse_incoming_url_manga_id(url: String) -> Option { // https://w.mangairo.com/story-pn279847 // https://w.mangairo.com/story-pn279847/chapter-52 - let manga_id_pattern = r"https?://w\.mangairo\.com/([^/]+)/?.*"; - let regex = Regex::new(manga_id_pattern).unwrap(); + let parts: Vec<&str> = url.split('/').collect(); + if parts.len() >= 3 && parts[2] == "w.mangairo.com" { + let manga_id = parts[3]; - if let Some(captures) = regex.captures(url.as_str()) { - if let Some(manga_id) = captures.get(1) { - return manga_id.as_str(); - } + return Some(format!("{}", manga_id)); + } else { + return None; } - return Option::None; } pub fn parse_incoming_url_chapter_id(url: String) -> Option { // https://w.mangairo.com/story-pn279847/chapter-52 - let manga_id_pattern = r"https?://w\.mangairo\.com/([^/]+)/([^/]+)/?.*"; - let regex = Regex::new(manga_id_pattern).unwrap(); - - if let Some(captures) = regex.captures(url.as_str()) { - if let Some(chapter_id) = captures.get(2) { - return chapter_id.as_str(); - } - } - return Option::None; + let parts: Vec<&str> = url.split('/').collect(); + if parts.len() >= 4 && parts[2] == "w.mangairo.com" { + let chapter_id = parts.get(4); + if chapter_id.is_none() { + return None; + } + + return Some(format!("{}", chapter_id.unwrap())); + } else { + return None; + } } // HELPER FUNCTIONS From 84ea32ba97efe34508c27beffb1fb13c8c83e47c Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 17:17:42 -0500 Subject: [PATCH 13/84] Return err instead of panic --- src/rust/en.mangairo/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rust/en.mangairo/src/lib.rs b/src/rust/en.mangairo/src/lib.rs index 38d86e062..993c34bfc 100644 --- a/src/rust/en.mangairo/src/lib.rs +++ b/src/rust/en.mangairo/src/lib.rs @@ -87,7 +87,9 @@ fn handle_url(url: String) -> Result { let parsed_chapter_id = parser::parse_incoming_url_chapter_id(url); if parsed_manga_id.is_none() { - panic!("unhandled url"); + return Err(aidoku::error::AidokuError { + reason: aidoku::error::AidokuErrorKind::Unimplemented, + }) } Ok(DeepLink { manga: Some(get_manga_details(parsed_manga_id.unwrap())?), From 53cc3dd3afa7f26cbd1cf646bb5cf8341d0fd84e Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 17:34:44 -0500 Subject: [PATCH 14/84] mangairo url parser not dependent on base domain --- src/rust/en.mangairo/src/parser.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index 582225443..3f56a0056 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -150,10 +150,10 @@ pub fn get_filtered_url(filters: Vec, page: i32, url: &mut String) { } pub fn parse_incoming_url_manga_id(url: String) -> Option { - // https://w.mangairo.com/story-pn279847 - // https://w.mangairo.com/story-pn279847/chapter-52 + // https://chap.mangairo.com/story-pn279847 + // https://chap.mangairo.com/story-pn279847/chapter-52 let parts: Vec<&str> = url.split('/').collect(); - if parts.len() >= 3 && parts[2] == "w.mangairo.com" { + if parts.len() >= 3 { let manga_id = parts[3]; return Some(format!("{}", manga_id)); @@ -163,9 +163,9 @@ pub fn parse_incoming_url_manga_id(url: String) -> Option { } pub fn parse_incoming_url_chapter_id(url: String) -> Option { - // https://w.mangairo.com/story-pn279847/chapter-52 + // https://chap.mangairo.com/story-pn279847/chapter-52 let parts: Vec<&str> = url.split('/').collect(); - if parts.len() >= 4 && parts[2] == "w.mangairo.com" { + if parts.len() >= 4 { let chapter_id = parts.get(4); if chapter_id.is_none() { return None; From 070eb1dcf9778624a3a2f6da65d6064141ee9142 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 17:38:17 -0500 Subject: [PATCH 15/84] Implement mangairo parse_search --- src/rust/en.mangairo/src/parser.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index 3f56a0056..a72ca528c 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -11,7 +11,22 @@ pub fn parse_manga_list(html: Node, result: &mut Vec) { } pub fn parse_search(html: Node, result: &mut Vec) { - todo!() + for page in html.select(".story-item").array() { + let obj = page.as_node().expect("node array"); + + let id = obj.select(".story-name a").attr("href").read(); + let title = obj.select(".story-name a ").text().read(); + let img = obj.select(".story-list-img img").attr("src").read(); + + if !id.is_empty() && !title.is_empty() && !img.is_empty() { + result.push(Manga { + id, + cover: img, + title, + ..Default::default() + }); + } + } } pub fn parse_manga(obj: Node, id: String) -> Result { From d151420bfbf7b30c82b5603365122ebc05775d92 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 17:40:00 -0500 Subject: [PATCH 16/84] Implement mangairo get_manga_list --- src/rust/en.mangairo/src/lib.rs | 6 +----- src/rust/en.mangairo/src/parser.rs | 4 ---- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/rust/en.mangairo/src/lib.rs b/src/rust/en.mangairo/src/lib.rs index 993c34bfc..8c03a3d80 100644 --- a/src/rust/en.mangairo/src/lib.rs +++ b/src/rust/en.mangairo/src/lib.rs @@ -20,11 +20,7 @@ fn get_manga_list(filters: Vec, page: i32) -> Result { parser::get_filtered_url(filters, page, &mut url); let html = Request::new(url.as_str(), HttpMethod::Get).html()?; - if url.contains("search") { - parser::parse_search(html, &mut result); - } else { - parser::parse_manga_list(html, &mut result); - } + parser::parse_manga_list(html, &mut result); if result.len() >= 50 { Ok(MangaPageResult { diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index a72ca528c..fb1e2e66b 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -7,10 +7,6 @@ pub const BASE_URL: &str = "https://w.mangairo.com"; pub const USER_AGENT: &str = "Mozilla/5.0 (Macintosh; Intel Mac OS X 13_3_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36"; pub fn parse_manga_list(html: Node, result: &mut Vec) { - todo!() -} - -pub fn parse_search(html: Node, result: &mut Vec) { for page in html.select(".story-item").array() { let obj = page.as_node().expect("node array"); From 869b0bd7cc0175bc811e74d29a54329e07aa45be Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 17:45:52 -0500 Subject: [PATCH 17/84] Implement mangairo get_manga_listing --- src/rust/en.mangairo/src/lib.rs | 16 +++++++++++++++- src/rust/en.mangairo/src/parser.rs | 4 ---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/rust/en.mangairo/src/lib.rs b/src/rust/en.mangairo/src/lib.rs index 8c03a3d80..d0d6d97f3 100644 --- a/src/rust/en.mangairo/src/lib.rs +++ b/src/rust/en.mangairo/src/lib.rs @@ -53,7 +53,21 @@ fn get_manga_listing(listing: Listing, page: i32) -> Result { } _ => String::from(BASE_URL), }; - parser::parse_manga_listing(url) + let html = Request::new(url.as_str(), HttpMethod::Get).html()?; + let mut result: Vec = Vec::new(); + parser::parse_manga_list(html, &mut result); + + if result.len() >= 50 { + Ok(MangaPageResult { + manga: result, + has_more: true, + }) + } else { + Ok(MangaPageResult { + manga: result, + has_more: false, + }) + } } #[get_chapter_list] diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index fb1e2e66b..f80e9d1f1 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -33,10 +33,6 @@ pub fn get_chapter_list(obj: Node) -> Result> { todo!() } -pub fn parse_manga_listing(url: String) -> Result { - todo!() -} - pub fn get_page_list(obj: Node) -> Result> { todo!() } From f070ab4af2e5720a5b8fedd1f020110607933132 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 17:48:01 -0500 Subject: [PATCH 18/84] Use const page size variable --- src/rust/en.mangairo/src/lib.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/rust/en.mangairo/src/lib.rs b/src/rust/en.mangairo/src/lib.rs index d0d6d97f3..2c2c0ccc5 100644 --- a/src/rust/en.mangairo/src/lib.rs +++ b/src/rust/en.mangairo/src/lib.rs @@ -12,6 +12,8 @@ use aidoku::{ mod parser; use parser::{BASE_URL, USER_AGENT}; +const PAGE_SIZE: usize = 50; + #[get_manga_list] fn get_manga_list(filters: Vec, page: i32) -> Result { let mut result: Vec = Vec::new(); @@ -22,7 +24,7 @@ fn get_manga_list(filters: Vec, page: i32) -> Result { parser::parse_manga_list(html, &mut result); - if result.len() >= 50 { + if result.len() >= PAGE_SIZE { Ok(MangaPageResult { manga: result, has_more: true, @@ -57,7 +59,7 @@ fn get_manga_listing(listing: Listing, page: i32) -> Result { let mut result: Vec = Vec::new(); parser::parse_manga_list(html, &mut result); - if result.len() >= 50 { + if result.len() >= PAGE_SIZE { Ok(MangaPageResult { manga: result, has_more: true, @@ -99,7 +101,7 @@ fn handle_url(url: String) -> Result { if parsed_manga_id.is_none() { return Err(aidoku::error::AidokuError { reason: aidoku::error::AidokuErrorKind::Unimplemented, - }) + }); } Ok(DeepLink { manga: Some(get_manga_details(parsed_manga_id.unwrap())?), From c40348e83c22cadb2790ec15de6fa42371dfa3ea Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 17:48:28 -0500 Subject: [PATCH 19/84] Update parser.rs --- src/rust/en.mangairo/src/parser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index f80e9d1f1..c677e62b3 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -1,6 +1,6 @@ use aidoku::{ error::Result, prelude::*, std::html::Node, std::String, std::Vec, Chapter, Filter, FilterType, - Manga, MangaContentRating, MangaPageResult, MangaStatus, MangaViewer, Page, + Manga, MangaContentRating, MangaStatus, MangaViewer, Page, }; pub const BASE_URL: &str = "https://w.mangairo.com"; From 49faea512dc0ce55b064db1c6f7783eb0527b9eb Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 17:51:13 -0500 Subject: [PATCH 20/84] Fix warnings --- src/rust/en.mangairo/src/parser.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index c677e62b3..5718a3d5e 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -1,6 +1,6 @@ use aidoku::{ error::Result, prelude::*, std::html::Node, std::String, std::Vec, Chapter, Filter, FilterType, - Manga, MangaContentRating, MangaStatus, MangaViewer, Page, + Manga, /* MangaContentRating, MangaStatus, MangaViewer, */ Page, }; pub const BASE_URL: &str = "https://w.mangairo.com"; @@ -25,15 +25,15 @@ pub fn parse_manga_list(html: Node, result: &mut Vec) { } } -pub fn parse_manga(obj: Node, id: String) -> Result { +pub fn parse_manga(_html: Node, _id: String) -> Result { todo!() } -pub fn get_chapter_list(obj: Node) -> Result> { +pub fn get_chapter_list(_html: Node) -> Result> { todo!() } -pub fn get_page_list(obj: Node) -> Result> { +pub fn get_page_list(_html: Node) -> Result> { todo!() } @@ -172,16 +172,16 @@ pub fn parse_incoming_url_manga_id(url: String) -> Option { pub fn parse_incoming_url_chapter_id(url: String) -> Option { // https://chap.mangairo.com/story-pn279847/chapter-52 let parts: Vec<&str> = url.split('/').collect(); - if parts.len() >= 4 { - let chapter_id = parts.get(4); - if chapter_id.is_none() { - return None; - } - - return Some(format!("{}", chapter_id.unwrap())); - } else { - return None; - } + if parts.len() >= 4 { + let chapter_id = parts.get(4); + if chapter_id.is_none() { + return None; + } + + return Some(format!("{}", chapter_id.unwrap())); + } else { + return None; + } } // HELPER FUNCTIONS From 18943c9661f2ef741ad719210a607a77fa9c0d73 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 17:55:44 -0500 Subject: [PATCH 21/84] Update Icon.png --- src/rust/en.mangairo/res/Icon.png | Bin 70 -> 4637 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/rust/en.mangairo/res/Icon.png b/src/rust/en.mangairo/res/Icon.png index 52c591798eb01fe73a43a2e863e34ec04731e9a2..fcc61c624fb94fa84a0efd34ebcca05d2362761a 100644 GIT binary patch literal 4637 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 Date: Mon, 4 Sep 2023 18:02:15 -0500 Subject: [PATCH 22/84] Scale logo to 128x128 --- src/rust/en.mangairo/res/Icon.png | Bin 4637 -> 28677 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/rust/en.mangairo/res/Icon.png b/src/rust/en.mangairo/res/Icon.png index fcc61c624fb94fa84a0efd34ebcca05d2362761a..5d2782c1464059fe7a5f59b44cf0e490825b9c09 100644 GIT binary patch literal 28677 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

iA{j1Nws=dJBsU{j%i#1Bm3>=LJ*IdnaJ}jbEDU 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 Date: Mon, 4 Sep 2023 18:04:59 -0500 Subject: [PATCH 23/84] Add background to logo --- src/rust/en.mangairo/res/Icon.png | Bin 28677 -> 22999 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/rust/en.mangairo/res/Icon.png b/src/rust/en.mangairo/res/Icon.png index 5d2782c1464059fe7a5f59b44cf0e490825b9c09..53e5998edc13c2fa27085844cb24e3f214d4935f 100644 GIT binary patch delta 18522 zcmXt9Wl$VVw_e=c-7Uf0-CcvbLvYstg1ZHG5AN=ZhG2m}65K7g``!1ex?5el^Jlht zy5{tGtRps|mRq2bX`zNw=qLaQbTB1q&jDTl^axY(!CYI~-4_u6O7hpIwr~y(F9o~z zAh8>VO-5QJRs}6&cnytudPTyje9Pn+vW4dBa@DAzPhqDQ7^xeDx4y9PbvXtz5-xm8 zHO1SHd-%Izk^xw`zlG&R5QXCpp}bo0QZoXb&DX$q*31s!t1U^%&-%>GDlUe#i2Gz% zLxjnfpjJ3Zp5pvbR@wC81h^I*l8J>p`a_HhLVX5XwVGc|7bdW@9G2$1yf(ZREF7FX zTrAxD9Of(*+!od>mONHG*4Ea%oR(YyY3^`oNWA=focsbDoSZG_@NiJD+=ARZX#@x# z{&&$LgP;LTW5dH?$3!-RtK7shbmh_dXj?-$M%YSiU7>z2-* z%d!tZBh+f(<2d=Nh~;lE3ZjtTzc+Yd7(K^B(Bn0g^2LzMII(l(HT5M{OP#p5JQNiz zHq)WU&pshr^&!jzs!|V%Nxe-pj^NjtgiTWi(E8fd*3)n2crlQKTB38QR}`5uP|cI| z7NA1WR?%P?AKJuP0MiIRW}wVW8M`Nm_|&f{{vB2m5s@i{7CT1StUN-QnVGgM85291 z)GHn~yy={@YEwA;3M-T1g~A?mslqq*6Uu%Xp-L)@uZPKV3T=(svIvdAF(*~J4y|>G z@D@?T;VthDsf-tX$Jl-<7Lu!14A$+L07?&)se>4uJDghG!qHNg*m#a^{&heKBgcQf@sDubl@@#Lf{1&9}E|^*_7yVGQsV;qr z6Clk9Z6?OA)GFX{Zk3b@rgTktt-U_!k}wTKsubGYJ7aRtZ$`2CG$SfjvUeqakms7@6^Oq3k7p+kv|)J|a3hsee=_ey)?F{P;dQuLc>5WZhA0XHNfvDP9jO+H zC^+!B7m9XksRvx48_7JCd_Z(1ELU(a!TDsAV>5A;oMlYGsKm9>*jK$)=3X@&nz^6NN~YU;_`H zRdC;%rS&uPm^1WP!H!LOMMlgog#lj}-En0SxmAkWBvT1zi1raaP{A6f8g>ntS|c7e z2)^*I3uUS`5f~x6Ayu)o7E3Lc%8K0(K7~35tdx8s(e?roy6ZKT(TLxIbabn#%|JR{ z5;{6|Iq;ALaWeEYmJ=ABkTsjQrRnx#)%PfnM}*3z6@^k>!1c>0W;|4%at|G~kbrMA zuxK-~XfiVI((|P$5@S1hpTf4*e^AGAkD`rz{D)P$XKO9hl|`G3mHyh2ApY{hWN^3r z`Cpzz{}

o{phok=#yNdq2foJMQdCyM!@cRW)+v6x>0akeU0v*FvL&_RG5!uIDq! z6R~kz@i|9gDIn%y$2&6t2768B-uQ__UV;`>KNCte_jH*_1R)X6D^!$ovQIU5pFTeI z-J_C9{^XX)Na3K3WrquqEc%lB4L6_uYyZy&1zDgd*rvB|<3&By9)zz+Pfi2_azc%C zlV0(*r#^Am>DEY zs3(~i)#cgn7JOKEj9)m4-r0%1*k7P zy>!zTz8Z~*OG()&ZyGVtT4^&`0s6cOsJb5oU#hF;7H>=@du}yMwz17oD5X*v??AW} z;o)ppFlD5JrG;N9s|)w84mvg@8qVnTQ9cauL;ZV(a$4Bd)Zs6j;V)$4FP!DiSK}vB z<1a|$Cr9@c>fO4yi-b(8av_D>VWr5PIDQA@dbBU8B%dd_!bjSlHS+2RxR}w<6yb85 zA!2)D-hTL9^;MG*SrMBc4z4=ZJ-Pp6zD-1z1?_-tx6L124VPZYDw`T(5`m2}sBma# zul|qQM~N2=8g=0A!uWHaXduB9ZKf#kjvYHXO?LsgqSrT9!9rKzyu&eyB>w!c6|wxo zceP4P=)dhl?5OOr&)otF0QSZ`hV|kyM$p@j?YpyS)$*@gNpfFN{8|bCZ&r`CF?Lit zAy(a!?*}aD4XvV|Z(l>2nUnX3nzgAkVzFe7%&@9cL3&h_5f`!-wwKVim0UrNBEc^7 zUc9{|_+jdO=7bNMj{T1er058}eSKS}r)c9lP*LC^inm9139V&2UgmMMw!wh zMtGJUE#uz#_Oz2tbOPZsEXIW4k1x4;Q79NYRrBp=wM~al=w3Qo?gBeM8m6pIYQEN= z&)vho=%zE5FefW1$XQD9ElAFt5`_Ics!GBAKzuoS54szhP9hPhZzY2Fe#}I!Tp(f) zeOd0%?d|W~JUzv009+c@*C-MZj93X!!61GDeu>jvol?X`Q@X4d@nUW+OgEa2tT1D% zL$X~N@o%_sqtLlLU%z=xZw*C+q~z-Q-{675xkP0s@ho8zqp4xr(v`Mfj87Br?u=rngLl2bAI7&vs~IkvJw0+oE&@_h`gf_4WO7Eka$XsnCZ z!gbe|CLf{?oOi{@>@o9HZK4h85J}4W>wbV@Zp&@(i(3kST2_BLg2&uwwQmiLn3`U`T*ZsMJ;0duzl1I?6F~d1+UCvLiUv;U2W@9_v93pr#gzT+ zVOGb7Yi7Q$`xBBgN}Fn!&c*q=(;(X3@bX}F8Id)&J7F;a)jB_ak7>w`xuYp+a>Hl3 z#{5v^GT|}W2g=kQORwXfM6ElT_n}+Kh=2Z3^z}=BLns4@WC^-;N!#KD@i^8J zF@W)`$Qxpq-N(XNCSQzQQu}C29U`rG?vyJ1W`uz$CXH||Sd*qHDJgE~{k#@Z;q2CB z^s+V3E4=ggm@V2byaOqs@&C%1KM`wBQRVpQL8KFbxm;Q!9WtlRfdQ~h%cw=ug=|rJ z4pE2_<}GS=o;wtDjoi!?8e|(^=K;QT5~NrQfFNkZ~)^s;dlEbd>t-P zeaHo=tZkl_U2HI2@_GxQC4+ESXWh#Js@eK%N2D>MR&8AyVI-FEieJb3o%i6>6iR>K zBaCUmz~``k~s1{UG-wXC@pnhq$hoGLBOca3ZZJx({9r2%E`SOt` zEpF|}Z?7$S-M4C455B1`Eh2+9q5;j(7&Bbx)h|96?6Xz1>5`aGG9>Vpp3adr&A70aN0RkQ(R^GW2Dj%d6RVdIUIf?W~AJa zP4~r$?xSW25f;FDDA$||FNlE8Sgde0{uV`CUor7JngA}zBI@b%?6rx}f z<^1ziL~V%&<&S*(0|J$&GA1I?d&E=ih+S@RwZD7& zg0~*Vrb!+45_b@@Y2~%>ex0AEGkfz6?GU59NEv|DThYfHplA)!BoGN$nHi#(BI5S2 zKTxqP-2);y(h`VQ@?Qwe9}kucp2L4|Bu9}b?-0WEIf3tN3aJ%R6##%#u2m%=PBVRO zAJ?Lpd$fePaqTl?|8HHAPYoA|j96NgJ%et^>9v+e-moODX}Eu=#5@;f`WHo>z!t;0 z4#S^5JN|Nwq@zQnK+CD{1(0T`@H@7jD<+LATh^D@$qCFLbLQ;;qCt$gEqK)Uf{m3CwKS3@qMCr8f~3eYIBYsLKF21 zxR`h(GHmr4bnX7agXaRkdFf5~us!!M#^`#;Y2T)3`sZPk87yFAzN77E5LqLwocU8b z1Md3TT@L`iZ}h#da8Wg@-AZJ)pMvPOo}I0jk2GeuvKQw#Oi-%r-ThKJT0;7WMUFy+ zuLx3GsZGq35rh6Mkxijr&auA}_Ew+dXiOppZAf{DvLB};gt&%u*SmG)X)N=Eipz3Y z#N=9U%0+HIz-0*47;hBUe*^-inx%D4fjKXhu@r-?GfBqs&I4-xC?a#goB~Zns%g!# zZ;>1wN>aC2JqQ{{TieySeb;5vkq-ek;-m>vZN8>%g86gpEVXTU95vCKjQ#6faoOym z?{g6HFb>9Q6kp+bEWODQ@)t7qw`Z3q1J#t)}NMZfzWjsVE zGFjv@`jQ(Z_M8`IY2c`>1Luee99LBQ71Q8O-MfI{#VyArCns0O6%Z8@aa)k#LR;HI z-~D^i4e8axnQXx3{l`Hrb6b!gz8#(;j2{D9A zjM)Xgy&%2L!FgYx;d_YPROJ#!?O*}#<6;-j=YZS-cQPkcX?E{8`$QN~^#j)Fzfx0H zLm$2;U*VDygVl|5w=TfwrKb8rnf=(Kau~M)Rm3i=;qb63;WC(AoT=MaQDvjjKWT=H zQ4YzcgIYR1TjcCQZFO3REQ!BRwbN)ul$2JnSlsWIP+_pxj*Hcxc`b|v!_LYOC_ZVN zmA?P}j1 z*|&01Io|b}S5QFOVL&|PTTsCP>55REDgy+E(afR%Db37aizdqE==2G0D7lzpvo21z z>vV-}`hwjuV__7i^azLj%LOTRaq1V+cYb(fVM1}n=NZF@`l zuDQ!1fm9Vm2sV9w_d%+aT=z(yxH<*$Qlc?7rs3(4ngQm~*H@951Dy2ljIf{|;stNQ zX(Q(hKO8&nHE;!5KM{^&RW2Y6yubBecquZ~E<6777D{3&6P_&77DS>85~ED{sV3C( zHnj*Ui4tK5LWVb#H4X4t2UW)^azwr5&oC6?`VlWDfPE}=^)n>OSI6&*FzXwik}CWt z!x6;7f&q-LGJXX+XU<8@%nY5ca#b|eyT}lfZ=XS>v=AaMy)o69(mtsSP3=B$uhlNa z2%1G!m-H1iHS1bZONGsyCN30>n5)308&af`=h_o0`b&r!lq9WKb`+vqnP#Lg&2@(O z`5FzSv<^XHm7iDTs`IIkupjb7zxKk+Ndq-$00tK02l|MUeHMHaT)G6sZ?qt3*B^va z7Fc}5%-fM+@~aO`_!G^zK0ZEr4SbBTVNKAy(9uLI4E!$T*u;<6;spmU1$wFU+wgiy zu(SQ=E;ElPOq7>2i>K=N#Fkxv^9=&;@`>jGb~7N zfinAY=x>x`%QzdqDX*f?iquFHDO_OD&>T&wQF7Y594b|djiIvTab-ep{UfGr;`ZDn z%8fjwc7%tB17tQ`84L5akP=I&BjVDSmQK*xS5$CQORN_AY#yRCk*EG!#}Y=xvtJ9# z%aN%y^`-Dxn#d{(zBH6-4)Q4C6O*{WRY&5V(LQKhd~ahkD%hC=uFQ=#K4^buZR`sD z?@1#?6jjW)nr0Utn6_+nnl>$>L+8<~99??QRZa#e1bz967i8Cq&I7FUy}z`pbm$|; z%2;`YVZvT%)GB-(bV3P9eJZkU^jji4&e|NObm(do>4tWp@xKh_ijqJ%V{wBzt&jhUi2@ znUti3S*k?wGapIqA91ewJ{UPO)V|OPD$0MJV=1|&DpFd?9q!>S9IzavgUdWYeGi+n z2V4;8CtdL|NJtWutTQ=%5T&kb7{$T>R7Cod2ae|VFC?&X;gIc{%0eW7Ravpmw1ytE zQB2`))4u6oFy3TtjU7qXzQS^Le(vhyleA|s6yz_EqJ$)JUxaO=%$;oUrE?@aXNE?b zlM4FCy-T_v1xnAdn588*zW*nE8EW4C3*^qGjq^#8^zzC)Br3ZXN6$v~TTBxZ(~G?% zwh^Udr0VXg7=t{TKs7+0kZ<3BoZBfU@-dp>yqHvsS_Masrdad)n?3f5{~ov8P|K2J zf`J^fwyFWOv?3Sp=A*0APF+L&$HXAAQKGE}yDNI0l{M&c+}P$phqhtw7I1LKy;%5j z#|J8G=^VJ!$_C||^a5U&N!2N)EeVox&Fxj9sg~Hk7`0=m9GU>;-sd(0^PaUWXFzRt zL5Dp|TJtl-I9AYdaW$K7P>__#?vIn)Puk)7Sro*Fp6~Q3S{q+$PyX_)uLhJH?ZQP| zNH^YvTzR!{CREKjx_adiVcZWYT#{j}_3sb^CN1e)Wq`Eq_G?QMo}{(gqW98PjmTB7F6$&s*; z#mvtP)z1h}Yc#5FG^rCXXDQMgt)>3ZQtcnXJq2ZI)Ft z*6o*b>Lhz2M^HiZm5@ac9Q{Cz9^@HswEU=~)!(N8?HH=zIAM-RDvTfb~rMo`{k_Wl{D}&`t8;R*f@QdNr`N1cjQZ<63{I`DR7->E3j#;-<;PELXFr+hN z38P35*BjD*En5C4E6!88D|`**7#8#J+1LDT2AY{Bwxs!4mOlt?(#+hs>QGV2IMuI( zPU$|8)?aB;8h5+l_~!~io#C8D^|LsvL*bxiVVQyhVOk>kB-u~+-}IQP9fj14b2l2f zRr;u-(rmK%D#KBYr5%q3A+1B&qqj4Z`UtYcviTP z%N>xBq_MRI~~QLjScHnp6+^Z zm7J$j+|E(7b=hmnnsd{@ie*P>Hf%ZW_maWRbu-cg48PYoE|oXwbpAS##=Z3oxnfFQ zJLH875j3L1BW;VGzM`!-!cA9Y&&bxL5g>DscFMAjZdcuLr%tT1lil9Y#<`m!jKD63 z*mx!E3Xx>=&9MTb_apvcH+mG9B%TApU17T_U^Nt}nhY8)S6l|Dr?hI9Jy(qE+=Cvv zogs?j<;zR_!28ZS`1+cw`dBgu^({k|gz>9c&r0#T%Yb>8k;TA<8hJ%Lqt%%JU{|Nn zlnmawh|Ii2!6-=AW6At|>h0_A9~L2z0f#n}p3CL@U_}UTRE<3KE4Q-NCnzaJq@oFa zIM#;{eBKdPM;C$T<5nFzNu8F8GtUfVs*E+lCjwr%HMO*<;Rpq7!DR_xkvYUWsFgd8~l8#yX(hmzawa_a1X<;`5mHmXNvW2 zioGub+f$ihzDwdyQQ8@FZ#!adk-ywS+i468>^v33oJHC6SK3D*@Ysx0j#*Li!vZ(h zzh&&8ayA6CMB*^^kYm7P<(H_b#nxZ%n4`;xjO&+Wz-hqs?bjgkBa{>H;+-}Tfmg=^ z5tY`zV|^kkMo@K5?8_tAUZjbqVl1D{aL~#SuiUmeeOrc++WE-5VU$I4L&>` z?C@ZCL6vKi`et=lihiw!N&!iHz5WiJHni0=7~!T7^V9abr1Bp?*~{1%qt$^pKL_)N zv5b3Mzj?!C6U*!c)FANI@E$rlAe@5DMAEPzsf>LRlj9uAutex>rjljYE24N;H$fRH zsggY5aXxDP7Mzn~edIJp;i{`ZKkpiG!sE#G)!YY%>ntwGBqku*`D-V&b0Bw=ni0sM z>&#ndbwjL?br2fR&xuGhmTq#_@3zH@3n8?zj~z#Nf^Z9D2tx5Uj|P0N@Oz~6ATj6Q*E|iI ziu1a33sf=JiK0;*+MXUw%DQsgftDqq`oGOU>(i-n>?IJOFKMvm}AHvrTwAfO%LDZlV4_Nie`3jRA*J|7nR3Nj!>2WJywC z9An4VmU@rv)Mj?Svqu+cJ`MIlj{9>ZY?3~P1kih}zW@A$9m74HB^o4=V~wd!g9>=0O`Z{K^x7h3swe3q(vF+Y0+NTp92t{=RfFuYO#Y-v^;&Bz1Y&n$LxId(Z zR#~q9_%HwevjpuXf4vuXqzw<(3a`H?P#@IoV72`QwUW~W%E2~#sEB7fhY~&ls7tUP z!3CuK3u_%Fug?JyG0tCw)6CVVM6;SaaE23mGR@lzwk|KR6sRvY-tIc&1xHJa(mMw# zoh>I=cEjO_jYODI>z1WE6c=EF-ua;Y^k95DR(2M4XuMa*ApPYQPWwAy-{>IiOuM~r zk_6c$y+gh1*ksj8;bp$LKg9pv|0T>>@F0N1Cr+X(*cjsOB!R-UHzmYowi@a<5IXg@ zG3CUz^OdkYpY%m2O)~NM6RrSnE?PlBL9#8A==+r?-9kzFuuadl@r&iMSR}u?_>C6u3ri?_u%o$uIPwv~s>^6Vuq%CroXFis1t!gi!Z$6@zbn zb~;d26^19*wM8l-p5kbV=P=5->14I3Ya;4V$$P4=L|@pw_WCm;9UMtV%b;DVz@(VI zZ&|S07$lBP>QmD-Gmkp7g|)%UWbZ6g%8{q>7Q0Y}NYg`l>&hv7&l&yK2PM7SYE;@Ch`_C_7%#K+<+2~Q~R@LKXp^Us8=wV@T7Db8luQ~wL`J5Dm z_i&`ZWK?~%EBUNlD2cM!X&bO&El^$QwsR>zf*BbCo@1d`IA2D8C#J(q zv;Jz6T0>-GW9R}KYQtbAvB)bU;bs6`*znBp8EWX>37_!!k#YA_zgA*e> z<*O~&7)l>g4&dUr%E}KRR^G+tFBzQW?O{^ipKpw;DB7~FOl@!~RaaYR)MkEzKHf_B zn7&0OQX+O4U!v2IBO`SCnFoEBv;sq0Q05G(%$caD?Z|t^?tvPcD__U@y{tUSI~FbM z_Br2wVnjt2)nYks{xADS#v9yK$++-{k72dn+t!O{3;^^}+2woYFu$t>jfMuc+UUDs z?0W`?8&*KsuH1>&lB)Z{1PIK#MYos3v$2$PVQ@U&AD_(rRrO3`_mKd$hD=Z3nqFdX zK1VAS`2Ml_L5`m$Gj9-*9Ze@)gY2N8OnofEX?p zS5ILbz_RY_8bu^|P8(41`ADqmNk&^v;p1h@t6oKLE;NJ6$-dpL0#40Ov!Y!%H ztg4N$ks2&>Wx*f`=6En?$UK9eeU5n(Gq|8|6kCrvwUMS{qn#yj{BW%0?_&+Y2x0*G zakoXYa^S<&0qL8r+SvNT&FyM=&P1v4qi%Q%FgdXodG^Qj0BR)u`Fx6;p63P}QeAI9 zFt`6c!n&}#aq_<2K~`P+ z-U>I&DMREnw|~WOPBi@Npf5K5*LVfU;Z1CV_}9L{!9$n67e7CYoi9;gRvM-2H7x5@ z(@jq7Vx%h@w_jgR$fKVV42v2QMk0e<{ zz>VRj+LPjJewyiC<7+Jia3K;Odg!S7w|w~0vqT+By92;ob>7(HR^r@yfIg-2mIS7Fi=g0k&3ZkB0^_0zxwJ4jc7s8qBXb}JcZA@ByZO(uVSG$kZG zlf^cNU>5!2;grzFFAxm=;{;*e8ajB~7)B7QS1luEsI?FadSAYXqBQO*j~x>mUdbN* zXIs?JDoFfKt|8F>urCLOW6jPcguBNI&d`}95DT_L-$2N zss(qKgrj2ElJeYP(>&!w()ybr-lQCz%Y(%B7QT$sZ7?S7tUEJ43PcRdx$WtZDmWl~ zn(y$?N&Q*fQ7^STzrMWzyu-vQiKljJFcLnr6xwCM2 zZnHk9LBKg&7kLm)Fd|P1lJ+i?c4}jk_vx!RG?edFI8^#d(+Ne@!0V({?}YxZm-4oF z&UXqAFOSEza}^9rNFQUhOZfuQtgx`nLr9N{SVm@yh2ANm!hv&yjjxct@DG$U)J>-%cJUS@3o>TN7fG^qKV0qhJz|zYopxe85_t^Fmj3%A% zn5vs8(Fdw+236v?@>89s09utb!pGBU;1Y(C*7{OY;N zPyxx}tV|6zx1tA$S(o_pC~WG}eDBOXxA=Z@p)e*4p1UtH=Op`--@h`@Pah&k53o&B zqH9%ad-m*fh2#<*AWViW%<|E@5{Z2M@D~n~8#){UQy{@e0N+7b>*ogDDWwIr0DJWQ z-5PT53{W_=7hm0tDMqEkXTQC%167ueyQJ3@o?%_~c2`3idVQA2VWsD5*tz!wli zwS;_2(wV4H2cJ0SqM$`z@_cS_WfcDDVz0fcU-PxEbE`C&;4FCenOHXIQBP?WPP*`t z2#bnhe_=3y#%m}gpzeIM_c_fU#ksals<(Ciacl5!n7Q&2B$yA0yBsrwDx@>XX{3J! zEvl4hUKb3NLHCd)B)0)7V98%gE|iWrlx-)wM{E4cy(Kwzgl=AGO|g?D%&+c9_v>Y- zsAgbPD#W;vX=L{Xak}|^l_WY8%_MoFAfm4X`igu5jEE@bPxv$pVIDA94W>T1tXIP0 z&K=-`*@4Z=OQ@Aod~1FGWu0_5HI%iqP8<{pdrO9p^sJlZ(qN#Y^Av6S-nKOHTM$Bs zyW?LZdQk{>9=(2F;NGdTcGNTER5mheF|j07C~_ZIDYbVr!Ts}?dv*h@D)$|&EWP&V zB%F5_7B?5;v{R|oh#4ZyHR`ddOLGQ@*0Mt%9-3<+S4>2RNPd5{X*Q32FUg`(UcbGr zsBdQ0AMX8EGz)42w=N+Z^xm2owBzt6SmJf}5+DT;57y+d)UjJI(oFxfXp_ z7S{P4*M%UBg$AgAkyz-@M({2uEDXXo#r;pKZ8!WW;5j@EX`J}b)*2-zLd0cc{;Ujg zO9Zs$am!a<`;@=zToTre_4M^G@pP?ndBS%Z)T`rK1KjVfhRKHVnwGcbnKx;RR)f^E zCY=uDHcT-*>9x5ndd$5h`RUZms@o=WyJJmIoeeq}37vUFK_}GdMu;)U zPujRa5kp?jf+dH_5ye~~phJzL_vv1Kb<+;^8%j6Fd^r|stqR^xdp7J_8hI$MaTB|k zACsH<(tpw6|NfQ-^qzP;6{w4`%Y`{{9p*uksm%=Mt5Y5(i=Afqd33Sck_#PS@U-!B zfCjZk-z5z`zbgsfad#+gx=ar#t%icULuUmUg2AWxMIMN0Hj^jZ<|L%=bnv_@7MgZg zFv*VE`&c{V+;I>=$QJC-Y4S^6XDt1C;x*@}F%kvJ%UDi0JoFZM!{wXMP@Eg-3wiOh zO~Pw%6lDBzIIY2|#N5_>xne)B)_*XY0 z0b>`c;=<<=Eit*v?eer%jLNY~SMh31gO6zh5@UGgTEFXFha{~3g?H%UM<4qS93XQ2 zKAg1*qvl-yjFHdTK_g|UQn zXKmyNN@UVCkCU0SSRoNCd1OJD^Qn4O?8LFXX%3!wp*Vx zQKcXe>0Wko{S3zM*fJ)*Mj^qNTL>8RL{7BByOdhlw1>|HSBUR(+Y!L*nB{LbPU)ZQ zc%{yGmZw??!A!w<(QcMUHRCjq=7UF`(BicvdshjS;Ytni$C6Q6ytgN=wOg;j&Xado zDr>$+yaP}??ynqoYTB)EkZATVhlpGMgj@engDE1rfeqz0oWVv~t_&OKu{q!=mHGqJR5LA9S&6uQI>ca%h$*?PPUWjC}e9*0qdN{gV`ZRnTDk zPa%mo>MZg(oc}-{ZL~D9n*Mte>7XjsNDOS>g^|$Tf~Ci-^^2>2c|9J*q%Al(-OsC4 z`9FQXBtjK+8Ds<3;gYoS3$PGkvvQh&5j2FtCoju3i&9a4pAw7%<*CV3MpPH4VM3r0 znLrgP%umLJ{7DTX=m#lsmYRDg#Hst`kWgW7mXN(Fue&g|GO)TuWFmevfpQ7M)v?qq zmp4cmKV4@JEmE5*x+=}yCuCnnL$w{y8k#f;3PbY zGK+||mq48niVUp336|>D)b-cU*e57O0(}zKZlcVmww5ucI#<4 zB9dXA`v)vGuj5$e1fSbQe0(RB+JIqtpzlUF=>801@8_%IVLDka-I%p@+fP@k&H}wl*e@3>s+WV+#;n1{0)bMe&MPPa?QtF#Ci*|HR(6%vWOu{0EEC!;$ zJVMT>+Kc+3lk0cc`t?(E+WQx8Un#F|^yf^oD7$b@9}+06%$QbcE?a9nHO-oFhTJ&z z{S%xe+B}|7mrXCG*a{&s;W+lQep%lHiDXuC4rdC(6hy1AbQZ>3YiTr+_V*Z2vvd2_%p2{1y!LFTHeIek`oj?miY` z*KMNkS4BS^XE&4P^fC#lGP^#X8vB42vg;shqKM+S=VVfvwT&WC>pz3kxEfA<`Ga|? z(WK$>;v%H7l0jHRWb5n{$mq`#B%cBgbH-O}$ z#6BGhSBV_P4}>vz)eqS-$@vuiTx9sk^2KrEiemG5BlM6^Y?NFC?7$Vv z%QIcq2Uuk-G@g)-L4kiu(H-q73w^sGoF!9c{xR`RZ<5{93C!J+Vu@8JpN8`bY(`97#& zq+>WXc57?M7#J83W9Yweeh>N9Yxt(ZD*YRt7ZnDzV?RGSQjsi14(_X_n`OX5;MW*l zcwWt-5(C`cR!R&crA1|*G{G@c5Ndj=Z-5p*xtiSrl|Uz-iRlvyApF=#P(X`>9|w2$ zEYiM$mNE5pLvY{GxPwTGPBgfF*>NTdkI!#YBuD7wH5kE>Cu0hZshH#xM-rj_#+bq$ z7Ss60YZuqt`Od4*%NKM`m9Ge^XJ=(rcKbqw>g%FphcJluqieaMa(6%!8^45x4R z?jgTXp(ETrFFW zMrpn5Rdwue^|t@fLzENtz)6l3g{WbR=QSR{NNK8UwssU&=AvYnfvv!d3>fQys1A7E@gFbQ7c zmd_^m&0RMRZ0_9qD23e{iZd=BuQ~IH9zEY2;mmS7cfuG_>L80Kjoph(K9D$4`st@{ zd@iU64mxMqZ+ePuA{nrA_7v=*!Nf*umPZ>3fZ|%eli5=~5O6VGR{K;DsKRbuA4A}S z&xN6k7e7i6?B^~#hQCmj->5}xzvjO@-`$hamuG4Ruz|4s|KX&DtWO{JCh`|t=^DK#~<|Mr#3$7@^2+L`s4T%SpG2QCtlu(K2Hf4ag$ zT}pvsH2LKr^yh63bO_I+s2;xWqDb8NmoWZ|4eE+y`G3b;^b4MX1QnU=>L4*{`44r~ zfxslT>+xuq;Hy0br3W)sCmt4V;io1THzx)#*R2~wDBN}+UBM}AO{>=|)Gl}|sft(`o ziGKu3Dh}Jla^6d^vR($92UR6aV6tXY0r&L6l?kvp^1R-=^E6DbKT8Iwnar1z(&Mlt zfBF;toEEtJC;BotQdf3VdXF>J_v0R&Kh%+mo6F*#v`r*>x17$e-c?Oy-r`PhbQ>mF z-hu3NK4Qy8|B;S%g02?MMpfD-f%_%)c)Bf%_jA75izsCGyFQqO7`!Dbg?qgofW28) z4*u&Lc-p({+ZB$gwZ3bdNf3WPQU6$Ew!@Bw@V_T=DT=W|s4&v6niFxWHSX~{(Z`xw zuEHhtj%qfLEOmhmUB6|6bM04(262)s4HU%4!acmCK^~8nLK(fDm|PxoOAxi2^}6l^ zKSJLjQM{Bd`Z<&?hk`fx(yC?+aOxd*Ym@N&9Pr=5B^8Q3<^Tg5rOX7dpPQ((XkyB? zsbFzoL`VKih@|~>atJuje9DMA-F89k>>Pr3LTvB>)7MwDca1^_PVm6^K_!EpC4Uc{ zKHfcQG}4!vyiJkB^}4=3NaSfuxpo|9s!H|KjB4vMxLAn_+}TE;M-QOPF@M+-^i|PP z`_$H%ajNpiC{DhdhFmv|@#@E{gmAF`$U>#s!^B~ zY?O4%DphcY`NlbC$O%@xl_Z8wxRen3>KY98FDb8~e4)1ijxiWM&xefVIX+QbeyOZD zk`Xa+X5URjJUK<)@OB&R3bfx0_{ z3c7^L?(hozZ3(BL=s2`H!$VF%aY+F@lR#{9KH;FZ`0*%&fA;~neo*3?eH$3P0gd-P z#9y~NO>sppwT@ThX8IKv<~q>7N@lNNFvV~#)fV>Tv2$Z=pc?F3$3#o8(3KJauCy%GG320SOd4?Fl0`?fq|$A!_r4o4Bn(W!fN1+v-^0jdGs+KJaKERCMb{iW5TM(r_#K@3_l>=-K~B^} zU|xvNoSb}w0JOw$8kSNkf`bRx>y^W3+Mtb{K+%mKBo>l;c~$<7AG4#0Z>X^+adEuW zN5t3x-p^ajIH3k=)JTTHNQUxp(bEAP<7>f5kcto%4IXA2cnL#Tmu`k=Mz#W5Xx)YB z)nkNz4S#4U$*AhdXV}W?L_dqI!SH{~p!`g!g4PisNAVsRY{juxuuVFPPrVaf$p}fP zz(DIUBYt1|-zk-To1ma_xYY3-BuG`j1GO#x__2C(?tTe67^%q6_qVr;F#Aekj^8y4 zScD}gUqo3Id02d>6 z3c?2EZDWst62=SheYvE;4Q?{Y9`cy>M3%}s-M5!Hn3(8dHZ*_aqvW5Ul-NFSd=$us z58Xjd+=;#wyghrnOxau`+%2x($T@iCEJ?&@#jLhB^mIDFD4gIYDh{&V5{N#hn-Tj3 z^P>jZARV@W0a2ID@g5|93$~jU0gfOd8%c+;{f={j;VPyYWxgtg$s^YiMV2nT_!Az1 zSfyjoT-IYqB>i@An!qm3e8;!kOtYS~E6RVwK0f#Vov;CeVaUaThHdG+1D|eUnqR{f zWq39Mmwk`BXBq<3(xpwT^wR0Bs1Npa1t*^dbb-SjG)nlGRVaV_k1j;O!e6ry`Rl-! z{rsv>^~F+rNwc<}_+W|cI_eSV0_ik0tW~c_C_)H{#bV*x zyQk9;2bN=bd7%XiLOlD4pr&b1*EO`JQpimePs=xX1s{K{Ipbab%$o$#srBe#AYYP! z$C)^33O!7aJmN0wc$#;^vh+miu+cC_*HL?S95pNq&+=2uca(g0_>K(vy?=54PaD9r zHkS}W`OKR>r$X}5^%w??-t-uf9&*}E(+11VeGr!cIIs7R` zppa#PXU>U@>PEdhjLJydFpgeo#$g4EMTx#&L{mqFpLZuE3+gtW&v#yvz=edc#Z=EY zdfy%fK2q>hN=$f&u_iC&EMZ!TlD!=sA0MM${{2Xu z;c0K~;Nyzudueq))dNg0f%t{SyFNM;0*%ID zKqmyW*Tr$fQvytAAd(om^{1M)adPv=FKE1wP5>o}Qv;zo+(x@~owtZb&fr;2P7&1| zc9NtFw}!(GlTa&cHfyX_gy_*|TgThZn>*mt&0gdM z%90L0O@F-9`|#}SPt<#l6Q;OT>sZ0g-3^2Lp-G!qCSkjEOkVI{5BK7`o^XbysZrGx z&gU}@ha;(k`-`weK;w7kP$p!gCB;MUpEmB&Uf$A!^ABBv!0&w)Jt{@$?qa zGHE(6b;bJ+>VM&e>D=1W?I@h2$g*(g=~qad{}vwLR@@AnJibv`3e|Ccx)nfA5U17y zOlNug+Cj&pb*FfRn~)NgCu;kyL(`I&Gx6if$(UMIt-TkRGJV{JF*6xB4FerPw#Mu3 zs+5zk97Rbir{!Xa6-(UhwmWQh!2sepJx4(co7Qz?fPZ7eaF$+wiI_ZL8Fj4t|JQ6B z`N;-wi$@^6(ucg7nTvPv(LQ1k;pgt*q>zaOn8KaNpk^K#RH-i`+onYu6;qCy#4L@hk1e08v+f*(%~0=G@S4e5hbF=w}jo#8Y-G(;%N#G@Hli7KQu$} zFEfB!9e=zjGnSY$EKr0rReq`!OenE_$dQPaQX~xXQY)8Q(<#bwfkY-K@&enhP&@01WSSdhbboA@z~G4uX`WcxsCg4qM4bAk%lb{E zFED2YA)5Pl6@aT?CM$Agn^RmLK*Z#aXoI$hR@22FJ$%iYODb{7t3lxT*0zuTG6oQ% zX>r3OmNOZK-mhVIM$CHcl9jW_X{yGV}A#Qm_O|c*om~f-%H_gfe3q0AjHy5NjpF%05~ej zYs6Ja_pdzke;Wh%=z%1OK)gUhO6%i(*nP|K+{7iI@w&TVR&RFbv4Ys#8%2~b5wU-) w5RmNotwBgifc>l8{71k3H~sivsffn}sQ>@~07*qoM6N<$f*f_XhX4Qo delta 24244 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 From 911d7a41ce6eb421073b25169046677fedf0ae7d Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 18:11:24 -0500 Subject: [PATCH 24/84] Remove unused type --- src/rust/en.mangairo/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rust/en.mangairo/src/lib.rs b/src/rust/en.mangairo/src/lib.rs index 2c2c0ccc5..fc3096fad 100644 --- a/src/rust/en.mangairo/src/lib.rs +++ b/src/rust/en.mangairo/src/lib.rs @@ -1,6 +1,6 @@ #![no_std] use aidoku::{ - error::{AidokuError, Result}, + error::Result, prelude::*, std::{ net::{HttpMethod, Request}, From 4f0ffbe065542f7a7f6a4a48600ad359e8b3c6c4 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 18:32:53 -0500 Subject: [PATCH 25/84] Fix mangairo listings --- src/rust/en.mangairo/res/source.json | 21 ++++++++++++++------- src/rust/en.mangairo/src/lib.rs | 2 +- src/rust/en.mangairo/src/parser.rs | 5 +++-- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/rust/en.mangairo/res/source.json b/src/rust/en.mangairo/res/source.json index 4bce31d11..6b4e2e812 100644 --- a/src/rust/en.mangairo/res/source.json +++ b/src/rust/en.mangairo/res/source.json @@ -7,12 +7,19 @@ "url": "https://w.mangairo.com/", "nsfw": 1 }, - "languages": [ - - ], + "languages": [], "listings": [ - { "name": "Hot" }, - { "name": "Latest" }, - { "name": "Completed" } + { + "name": "Latest" + }, + { + "name": "New Releases" + }, + { + "name": "Hot" + }, + { + "name": "Completed" + } ] -} +} \ No newline at end of file diff --git a/src/rust/en.mangairo/src/lib.rs b/src/rust/en.mangairo/src/lib.rs index fc3096fad..281cc6110 100644 --- a/src/rust/en.mangairo/src/lib.rs +++ b/src/rust/en.mangairo/src/lib.rs @@ -53,7 +53,7 @@ fn get_manga_listing(listing: Listing, page: i32) -> Result { "Completed" => { format!("{BASE_URL}/manga-list/type-latest/ctg-all/state-completed/page-{page}") } - _ => String::from(BASE_URL), + _ => format!("{BASE_URL}/manga-list/type-latest/ctg-all/state-all/page-{page}"), }; let html = Request::new(url.as_str(), HttpMethod::Get).html()?; let mut result: Vec = Vec::new(); diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index 5718a3d5e..4c512614b 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -69,13 +69,14 @@ pub fn get_filtered_url(filters: Vec, page: i32, url: &mut String) { .clone(); if let Ok(filter_value) = title_filter.value.as_string() { - // filter_value.read().to_lowercase(); search_string.push_str(urlencode(filter_value.read().to_lowercase()).as_str()); is_searching = true; } if let Ok(filter_value) = author_filter.value.as_string() { - // filter_value.read().to_lowercase(); + if !search_string.is_empty() { + search_string.push_str("+"); + } search_string.push_str(urlencode(filter_value.read().to_lowercase()).as_str()); is_searching = true; } From 49e54ffda6ce1cde6b8596f885ad6645ecaf59a7 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 18:35:34 -0500 Subject: [PATCH 26/84] Fix lint --- src/rust/en.mangairo/src/parser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index 4c512614b..6f79b9693 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -75,7 +75,7 @@ pub fn get_filtered_url(filters: Vec, page: i32, url: &mut String) { if let Ok(filter_value) = author_filter.value.as_string() { if !search_string.is_empty() { - search_string.push_str("+"); + search_string.push('+'); } search_string.push_str(urlencode(filter_value.read().to_lowercase()).as_str()); is_searching = true; From fd45ec89ca8ad6c26940145c041a8cce25811cae Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 18:36:47 -0500 Subject: [PATCH 27/84] Fix lints --- src/rust/en.mangairo/src/parser.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index 6f79b9693..e4d69d730 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -98,12 +98,12 @@ pub fn get_filtered_url(filters: Vec, page: i32, url: &mut String) { url.push_str("/ctg-"); match genre_filter.value.as_int().unwrap_or(-1) { 0 => url.push_str("all"), // "All", - 1 => url.push_str("2"), // "Action", - 2 => url.push_str("3"), // "Adult", - 3 => url.push_str("4"), // "Adventure", - 4 => url.push_str("6"), // "Comedy", - 5 => url.push_str("7"), // "Cooking", - 6 => url.push_str("9"), // "Doujinshi", + 1 => url.push('2'), // "Action", + 2 => url.push('3'), // "Adult", + 3 => url.push('4'), // "Adventure", + 4 => url.push('6'), // "Comedy", + 5 => url.push('7'), // "Cooking", + 6 => url.push('9'), // "Doujinshi", 7 => url.push_str("10"), // "Drama", 8 => url.push_str("11"), // "Ecchi", 9 => url.push_str("48"), // "Erotica", From 5eb6aaed710aedf11049ec8a2b5619cd9afa2900 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 18:38:51 -0500 Subject: [PATCH 28/84] Fix unneeded return lint --- src/rust/en.mangairo/src/parser.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index e4d69d730..2c5ecef65 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -164,10 +164,10 @@ pub fn parse_incoming_url_manga_id(url: String) -> Option { if parts.len() >= 3 { let manga_id = parts[3]; - return Some(format!("{}", manga_id)); - } else { - return None; + Some(format!("{}", manga_id)); } + + None } pub fn parse_incoming_url_chapter_id(url: String) -> Option { @@ -180,9 +180,9 @@ pub fn parse_incoming_url_chapter_id(url: String) -> Option { } return Some(format!("{}", chapter_id.unwrap())); - } else { - return None; } + + None } // HELPER FUNCTIONS From bb066e5d217cbd89b2e04614dd5b2563980aab44 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 18:48:03 -0500 Subject: [PATCH 29/84] Fix All listing and search --- src/rust/en.mangairo/src/parser.rs | 51 +++++++++++++++--------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index 2c5ecef65..37bd58996 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -42,43 +42,42 @@ pub fn get_filtered_url(filters: Vec, page: i32, url: &mut String) { let mut search_string = String::new(); url.push_str("https://w.mangairo.com"); - let title_filter: Filter = filters + let title_filter: Option = filters .iter() .find(|&x| x.kind == FilterType::Title) - .unwrap() - .clone(); - let author_filter: Filter = filters + .cloned(); + let author_filter: Option = filters .iter() .find(|&x| x.kind == FilterType::Author) - .unwrap() - .clone(); - let status_filter: Filter = filters + .cloned(); + let status_filter: Option = filters .iter() .find(|&x| x.kind == FilterType::Select && x.name == "Status") - .unwrap() - .clone(); - let sort_filter: Filter = filters + .cloned(); + let sort_filter: Option = filters .iter() .find(|&x| x.kind == FilterType::Select && x.name == "Sort") - .unwrap() - .clone(); - let genre_filter: Filter = filters + .cloned(); + let genre_filter: Option = filters .iter() .find(|&x| x.kind == FilterType::Select && x.name == "Genre") - .unwrap() - .clone(); + .cloned(); - if let Ok(filter_value) = title_filter.value.as_string() { - search_string.push_str(urlencode(filter_value.read().to_lowercase()).as_str()); - is_searching = true; + if title_filter.is_some() { + if let Ok(filter_value) = title_filter.unwrap().value.as_string() { + search_string.push_str(urlencode(filter_value.read().to_lowercase()).as_str()); + is_searching = true; + } } - if let Ok(filter_value) = author_filter.value.as_string() { - if !search_string.is_empty() { - search_string.push('+'); + if author_filter.is_some() { + if let Ok(filter_value) = author_filter.unwrap().value.as_string() { + if !search_string.is_empty() { + search_string.push('+'); + } + search_string.push_str(urlencode(filter_value.read().to_lowercase()).as_str()); + is_searching = true; } - search_string.push_str(urlencode(filter_value.read().to_lowercase()).as_str()); - is_searching = true; } if is_searching { @@ -88,7 +87,7 @@ pub fn get_filtered_url(filters: Vec, page: i32, url: &mut String) { url.push_str(&i32_to_string(page)); } else { url.push_str("/manga-list/type-"); - match sort_filter.value.as_int().unwrap_or(-1) { + match sort_filter.unwrap().value.as_int().unwrap_or(-1) { 0 => url.push_str("latest"), 1 => url.push_str("newest"), 2 => url.push_str("topview"), @@ -96,7 +95,7 @@ pub fn get_filtered_url(filters: Vec, page: i32, url: &mut String) { } // Genre url.push_str("/ctg-"); - match genre_filter.value.as_int().unwrap_or(-1) { + match genre_filter.unwrap().value.as_int().unwrap_or(-1) { 0 => url.push_str("all"), // "All", 1 => url.push('2'), // "Action", 2 => url.push('3'), // "Adult", @@ -145,7 +144,7 @@ pub fn get_filtered_url(filters: Vec, page: i32, url: &mut String) { // State url.push_str("/state-"); - match status_filter.value.as_int().unwrap_or(0) { + match status_filter.unwrap().value.as_int().unwrap_or(0) { 0 => url.push_str("all"), 1 => url.push_str("ongoing"), 2 => url.push_str("completed"), From 851276eed51906865988dc061793b03c74e2aa8e Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 18:54:10 -0500 Subject: [PATCH 30/84] Update parser.rs --- src/rust/en.mangairo/src/parser.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index 37bd58996..dc7f49cd3 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -174,10 +174,8 @@ pub fn parse_incoming_url_chapter_id(url: String) -> Option { let parts: Vec<&str> = url.split('/').collect(); if parts.len() >= 4 { let chapter_id = parts.get(4); - if chapter_id.is_none() { - return None; - } - + + chapter_id?; return Some(format!("{}", chapter_id.unwrap())); } From b1d128c44e5ac8a39d5fe9ad62145735d8ec4bb8 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 18:56:10 -0500 Subject: [PATCH 31/84] Update lib.rs --- src/rust/en.mangairo/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rust/en.mangairo/src/lib.rs b/src/rust/en.mangairo/src/lib.rs index 281cc6110..5b7043784 100644 --- a/src/rust/en.mangairo/src/lib.rs +++ b/src/rust/en.mangairo/src/lib.rs @@ -105,9 +105,9 @@ fn handle_url(url: String) -> Result { } Ok(DeepLink { manga: Some(get_manga_details(parsed_manga_id.unwrap())?), - chapter: if parsed_chapter_id.is_some() { + chapter: if let Some(chapter_id_value) = parsed_chapter_id { Some(Chapter { - id: parsed_chapter_id.unwrap(), + id: chapter_id_value, ..Default::default() }) } else { From efe577321b6637014f1ed792bed65b615b5e2416 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 18:59:44 -0500 Subject: [PATCH 32/84] Fix lint --- src/rust/en.mangairo/src/lib.rs | 12 ++++-------- src/rust/en.mangairo/src/parser.rs | 10 +++++----- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/rust/en.mangairo/src/lib.rs b/src/rust/en.mangairo/src/lib.rs index 5b7043784..6aa2d77f0 100644 --- a/src/rust/en.mangairo/src/lib.rs +++ b/src/rust/en.mangairo/src/lib.rs @@ -105,13 +105,9 @@ fn handle_url(url: String) -> Result { } Ok(DeepLink { manga: Some(get_manga_details(parsed_manga_id.unwrap())?), - chapter: if let Some(chapter_id_value) = parsed_chapter_id { - Some(Chapter { - id: chapter_id_value, - ..Default::default() - }) - } else { - None - }, + chapter: parsed_chapter_id.map(|chapter_id_value| Chapter { + id: chapter_id_value, + ..Default::default() + }), }) } diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index dc7f49cd3..8be091d49 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -63,15 +63,15 @@ pub fn get_filtered_url(filters: Vec, page: i32, url: &mut String) { .find(|&x| x.kind == FilterType::Select && x.name == "Genre") .cloned(); - if title_filter.is_some() { - if let Ok(filter_value) = title_filter.unwrap().value.as_string() { + if let Some(title_filter_value) = title_filter { + if let Ok(filter_value) = title_filter_value.value.as_string() { search_string.push_str(urlencode(filter_value.read().to_lowercase()).as_str()); is_searching = true; } } - if author_filter.is_some() { - if let Ok(filter_value) = author_filter.unwrap().value.as_string() { + if let Some(author_filter_value) = author_filter { + if let Ok(filter_value) = author_filter_value.value.as_string() { if !search_string.is_empty() { search_string.push('+'); } @@ -174,7 +174,7 @@ pub fn parse_incoming_url_chapter_id(url: String) -> Option { let parts: Vec<&str> = url.split('/').collect(); if parts.len() >= 4 { let chapter_id = parts.get(4); - + chapter_id?; return Some(format!("{}", chapter_id.unwrap())); } From b056df1db011d1f43de9d2d8bd9ba4d73b4b72a7 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 19:07:05 -0500 Subject: [PATCH 33/84] Update parser.rs --- src/rust/en.mangairo/src/parser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index 8be091d49..b3ec65092 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -73,7 +73,7 @@ pub fn get_filtered_url(filters: Vec, page: i32, url: &mut String) { if let Some(author_filter_value) = author_filter { if let Ok(filter_value) = author_filter_value.value.as_string() { if !search_string.is_empty() { - search_string.push('+'); + search_string.push('_'); } search_string.push_str(urlencode(filter_value.read().to_lowercase()).as_str()); is_searching = true; From 4f8be8a9828d35d7ac5e99b454fd416d7c119f9e Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 19:10:00 -0500 Subject: [PATCH 34/84] Remove redundant "latest" listing --- src/rust/en.mangairo/res/source.json | 3 --- src/rust/en.mangairo/src/lib.rs | 1 - 2 files changed, 4 deletions(-) diff --git a/src/rust/en.mangairo/res/source.json b/src/rust/en.mangairo/res/source.json index 6b4e2e812..0634e9202 100644 --- a/src/rust/en.mangairo/res/source.json +++ b/src/rust/en.mangairo/res/source.json @@ -9,9 +9,6 @@ }, "languages": [], "listings": [ - { - "name": "Latest" - }, { "name": "New Releases" }, diff --git a/src/rust/en.mangairo/src/lib.rs b/src/rust/en.mangairo/src/lib.rs index 6aa2d77f0..25af5fdc8 100644 --- a/src/rust/en.mangairo/src/lib.rs +++ b/src/rust/en.mangairo/src/lib.rs @@ -47,7 +47,6 @@ fn get_manga_details(manga_id: String) -> Result { #[get_manga_listing] fn get_manga_listing(listing: Listing, page: i32) -> Result { let url = match listing.name.as_str() { - "Latest" => format!("{BASE_URL}/manga-list/type-latest/ctg-all/state-all/page-{page}"), "New Releases" => format!("{BASE_URL}/manga-list/type-newest/ctg-7/state-all/page-{page}"), "Hot" => format!("{BASE_URL}/manga-list/type-topview/ctg-all/state-all/page-{page}"), "Completed" => { From 4b4ebd9e43e5a52c3e51b1406d55667dc5811ac0 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 19:36:48 -0500 Subject: [PATCH 35/84] Parse some manga details --- src/rust/en.mangairo/src/lib.rs | 2 +- src/rust/en.mangairo/src/parser.rs | 33 +++++++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/rust/en.mangairo/src/lib.rs b/src/rust/en.mangairo/src/lib.rs index 25af5fdc8..435a1b201 100644 --- a/src/rust/en.mangairo/src/lib.rs +++ b/src/rust/en.mangairo/src/lib.rs @@ -41,7 +41,7 @@ fn get_manga_list(filters: Vec, page: i32) -> Result { fn get_manga_details(manga_id: String) -> Result { let url = format!("https://w.mangairo.com/{}", &manga_id); let html = Request::new(url.as_str(), HttpMethod::Get).html()?; - parser::parse_manga(html, manga_id) + parser::parse_manga_details(html, manga_id) } #[get_manga_listing] diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index b3ec65092..d2c6cc08f 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -1,6 +1,6 @@ use aidoku::{ error::Result, prelude::*, std::html::Node, std::String, std::Vec, Chapter, Filter, FilterType, - Manga, /* MangaContentRating, MangaStatus, MangaViewer, */ Page, + Manga, MangaContentRating, MangaStatus, MangaViewer, Page, }; pub const BASE_URL: &str = "https://w.mangairo.com"; @@ -25,8 +25,35 @@ pub fn parse_manga_list(html: Node, result: &mut Vec) { } } -pub fn parse_manga(_html: Node, _id: String) -> Result { - todo!() +pub fn parse_manga_details(html: Node, id: String) -> Result { + let title = html + .select(".breadcrumbs p span a span") + .last() + .text() + .read(); + let cover = html.select(".avatar").attr("src").read(); + let description = String::from(html.select(".story_content p").text().read().trim()); + + let url = format!("https://chap.mangairo.com/{}", &id); + + // TODO: + let categories: Vec = Vec::new(); + let status = MangaStatus::Ongoing; + let nsfw = MangaContentRating::Safe; + let viewer = MangaViewer::Rtl; + + Ok(Manga { + id, + cover, + title, + description, + url, + categories, + status, + nsfw, + viewer, + ..Default::default() + }) } pub fn get_chapter_list(_html: Node) -> Result> { From 7c06b6587cddcc42d08634b8146a3f304c42d083 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 20:04:58 -0500 Subject: [PATCH 36/84] Fix manga description not always working --- src/rust/en.mangairo/src/lib.rs | 2 +- src/rust/en.mangairo/src/parser.rs | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/rust/en.mangairo/src/lib.rs b/src/rust/en.mangairo/src/lib.rs index 435a1b201..ea8036cf1 100644 --- a/src/rust/en.mangairo/src/lib.rs +++ b/src/rust/en.mangairo/src/lib.rs @@ -39,7 +39,7 @@ fn get_manga_list(filters: Vec, page: i32) -> Result { #[get_manga_details] fn get_manga_details(manga_id: String) -> Result { - let url = format!("https://w.mangairo.com/{}", &manga_id); + let url = format!("https://chap.mangairo.com/{}", &manga_id); let html = Request::new(url.as_str(), HttpMethod::Get).html()?; parser::parse_manga_details(html, manga_id) } diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index d2c6cc08f..78cac6b82 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -2,6 +2,8 @@ use aidoku::{ error::Result, prelude::*, std::html::Node, std::String, std::Vec, Chapter, Filter, FilterType, Manga, MangaContentRating, MangaStatus, MangaViewer, Page, }; +extern crate alloc; +use alloc::string::ToString; pub const BASE_URL: &str = "https://w.mangairo.com"; pub const USER_AGENT: &str = "Mozilla/5.0 (Macintosh; Intel Mac OS X 13_3_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36"; @@ -32,7 +34,12 @@ pub fn parse_manga_details(html: Node, id: String) -> Result { .text() .read(); let cover = html.select(".avatar").attr("src").read(); - let description = String::from(html.select(".story_content p").text().read().trim()); + let description = html + .select("div#story_discription p") + .text() + .read() + .trim() + .to_string(); let url = format!("https://chap.mangairo.com/{}", &id); @@ -57,7 +64,8 @@ pub fn parse_manga_details(html: Node, id: String) -> Result { } pub fn get_chapter_list(_html: Node) -> Result> { - todo!() + let chapters: Vec = Vec::new(); + Ok(chapters) } pub fn get_page_list(_html: Node) -> Result> { From 36197f49e53f4514d237d2b9e4507b69a8308cd7 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 20:39:49 -0500 Subject: [PATCH 37/84] Implement mangairo get_chapter_list --- src/rust/en.mangairo/src/parser.rs | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index 78cac6b82..c70bf7c76 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -63,8 +63,28 @@ pub fn parse_manga_details(html: Node, id: String) -> Result { }) } -pub fn get_chapter_list(_html: Node) -> Result> { - let chapters: Vec = Vec::new(); +pub fn get_chapter_list(html: Node) -> Result> { + let mut chapters: Vec = Vec::new(); + for chapter in html.select(".chapter_list ul li a").array() { + let obj = chapter.as_node().expect("node array"); + let url = obj.attr("href").read(); + // let url = format!("https://www.mangapill.com{}", &id); + let id = parse_incoming_url_chapter_id(url.clone()); + + if let Some(id_value) = id { + let split = id_value.split('-'); + let vec = split.collect::>(); + let chap_num = vec[vec.len() - 1].parse().unwrap(); + + chapters.push(Chapter { + id: id_value, + chapter: chap_num, + url, + lang: String::from("en"), + ..Default::default() + }); + } + } Ok(chapters) } From 1c7b308cd456d903106114a0f7b444da539b2df5 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 21:09:17 -0500 Subject: [PATCH 38/84] Implement get_page_list and fix ids --- src/rust/en.mangairo/src/lib.rs | 12 ++++++++--- src/rust/en.mangairo/src/parser.rs | 34 +++++++++++++++++++----------- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/rust/en.mangairo/src/lib.rs b/src/rust/en.mangairo/src/lib.rs index ea8036cf1..b41cb1103 100644 --- a/src/rust/en.mangairo/src/lib.rs +++ b/src/rust/en.mangairo/src/lib.rs @@ -73,14 +73,20 @@ fn get_manga_listing(listing: Listing, page: i32) -> Result { #[get_chapter_list] fn get_chapter_list(manga_id: String) -> Result> { - let url = format!("https://w.mangairo.com/{}", &manga_id); + let url = format!("{}", &manga_id); + aidoku::prelude::println!("url: {}", url); + aidoku::prelude::println!("manga id: {}", manga_id); let html = Request::new(url.as_str(), HttpMethod::Get).html()?; parser::get_chapter_list(html) } #[get_page_list] -fn get_page_list(chapter_id: String, manga_id: String) -> Result> { - let url = format!("https://w.mangairo.com/{}/{}", &manga_id, &chapter_id); +fn get_page_list(manga_id: String, chapter_id: String,) -> Result> { + let url = format!("{}/{}", &manga_id, &chapter_id); + + aidoku::prelude::println!("url: {}", url); + aidoku::prelude::println!("manga id: {}", manga_id); + aidoku::prelude::println!("chapter id: {}", chapter_id); let html = Request::new(url.as_str(), HttpMethod::Get).html()?; parser::get_page_list(html) } diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index c70bf7c76..45f6ed424 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -68,7 +68,6 @@ pub fn get_chapter_list(html: Node) -> Result> { for chapter in html.select(".chapter_list ul li a").array() { let obj = chapter.as_node().expect("node array"); let url = obj.attr("href").read(); - // let url = format!("https://www.mangapill.com{}", &id); let id = parse_incoming_url_chapter_id(url.clone()); if let Some(id_value) = id { @@ -88,8 +87,21 @@ pub fn get_chapter_list(html: Node) -> Result> { Ok(chapters) } -pub fn get_page_list(_html: Node) -> Result> { - todo!() +pub fn get_page_list(html: Node) -> Result> { + let mut pages: Vec = Vec::new(); + + for (i, page) in html.select(".panel-read-story img").array().enumerate() { + let obj = page.as_node().expect("node array"); + let url = obj.attr("src").read(); + aidoku::prelude::println!("url: {}", url); + + pages.push(Page { + index: i as i32, + url, + ..Default::default() + }); + } + Ok(pages) } pub fn get_filtered_url(filters: Vec, page: i32, url: &mut String) { @@ -214,24 +226,22 @@ pub fn get_filtered_url(filters: Vec, page: i32, url: &mut String) { pub fn parse_incoming_url_manga_id(url: String) -> Option { // https://chap.mangairo.com/story-pn279847 // https://chap.mangairo.com/story-pn279847/chapter-52 - let parts: Vec<&str> = url.split('/').collect(); - if parts.len() >= 3 { - let manga_id = parts[3]; - - Some(format!("{}", manga_id)); + let mut parts: Vec<&str> = url.split('/').collect(); + if parts.len() >= 4 { + parts.truncate(4); } - None + Some(parts.join("/")) } pub fn parse_incoming_url_chapter_id(url: String) -> Option { // https://chap.mangairo.com/story-pn279847/chapter-52 let parts: Vec<&str> = url.split('/').collect(); if parts.len() >= 4 { - let chapter_id = parts.get(4); + let chapter_id = parts[4]; + aidoku::prelude::println!("chapter id: {}", chapter_id); - chapter_id?; - return Some(format!("{}", chapter_id.unwrap())); + return Some(format!("{}", chapter_id)); } None From b60f9346150b3d24578a6de741e310a1eaefc0e5 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 21:09:55 -0500 Subject: [PATCH 39/84] Remove logs --- src/rust/en.mangairo/src/lib.rs | 9 +-------- src/rust/en.mangairo/src/parser.rs | 2 -- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/src/rust/en.mangairo/src/lib.rs b/src/rust/en.mangairo/src/lib.rs index b41cb1103..0fa89cc0e 100644 --- a/src/rust/en.mangairo/src/lib.rs +++ b/src/rust/en.mangairo/src/lib.rs @@ -73,20 +73,13 @@ fn get_manga_listing(listing: Listing, page: i32) -> Result { #[get_chapter_list] fn get_chapter_list(manga_id: String) -> Result> { - let url = format!("{}", &manga_id); - aidoku::prelude::println!("url: {}", url); - aidoku::prelude::println!("manga id: {}", manga_id); - let html = Request::new(url.as_str(), HttpMethod::Get).html()?; + let html = Request::new(manga_id, HttpMethod::Get).html()?; parser::get_chapter_list(html) } #[get_page_list] fn get_page_list(manga_id: String, chapter_id: String,) -> Result> { let url = format!("{}/{}", &manga_id, &chapter_id); - - aidoku::prelude::println!("url: {}", url); - aidoku::prelude::println!("manga id: {}", manga_id); - aidoku::prelude::println!("chapter id: {}", chapter_id); let html = Request::new(url.as_str(), HttpMethod::Get).html()?; parser::get_page_list(html) } diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index 45f6ed424..ea4a59a37 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -239,8 +239,6 @@ pub fn parse_incoming_url_chapter_id(url: String) -> Option { let parts: Vec<&str> = url.split('/').collect(); if parts.len() >= 4 { let chapter_id = parts[4]; - aidoku::prelude::println!("chapter id: {}", chapter_id); - return Some(format!("{}", chapter_id)); } From 3e5ad2016a406f88ebe9bde417e52823b62a9411 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 21:27:25 -0500 Subject: [PATCH 40/84] Finish parsing mangairo manga details --- src/rust/en.mangairo/src/parser.rs | 40 +++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index ea4a59a37..46920fc02 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -40,14 +40,42 @@ pub fn parse_manga_details(html: Node, id: String) -> Result { .read() .trim() .to_string(); + let status_str = html + .select(".story_info_right li:nth-child(5) a") + .text() + .read() + .to_lowercase(); + + let url = format!("{}", &id); + + let mut categories: Vec = Vec::new(); + html.select(".story_info_right .a-h") + .array() + .for_each(|tag| categories.push(tag.as_node().expect("node array").text().read())); + + let status = match status_str.as_str() { + "ongoing" => MangaStatus::Ongoing, + "completed" => MangaStatus::Completed, + _ => MangaStatus::Unknown, + }; - let url = format!("https://chap.mangairo.com/{}", &id); + let nsfw = if categories.contains(&String::from("Pornographic")) { + MangaContentRating::Nsfw + } else if categories.contains(&String::from("Adult")) + || categories.contains(&String::from("Ecchi")) + { + MangaContentRating::Suggestive + } else { + MangaContentRating::Safe + }; - // TODO: - let categories: Vec = Vec::new(); - let status = MangaStatus::Ongoing; - let nsfw = MangaContentRating::Safe; - let viewer = MangaViewer::Rtl; + let viewer = if categories.contains(&String::from("Manhua")) + || categories.contains(&String::from("Manhwa")) + { + MangaViewer::Scroll + } else { + MangaViewer::Rtl + }; Ok(Manga { id, From 906b3f891a33eeccfb5deef78476550a7907c6e9 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 21:34:04 -0500 Subject: [PATCH 41/84] Mark sm*t as suggestive --- src/rust/en.mangairo/src/parser.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index 46920fc02..4ad4d0ded 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -63,6 +63,7 @@ pub fn parse_manga_details(html: Node, id: String) -> Result { MangaContentRating::Nsfw } else if categories.contains(&String::from("Adult")) || categories.contains(&String::from("Ecchi")) + || categories.contains(&String::from("Smut")) { MangaContentRating::Suggestive } else { From c92cf479aa5801446483e60bd6365b0b9fc1f2b9 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 21:39:57 -0500 Subject: [PATCH 42/84] Fix nsfw and viewer categories --- src/rust/en.mangairo/src/parser.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index 4ad4d0ded..0e7923b87 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -59,12 +59,12 @@ pub fn parse_manga_details(html: Node, id: String) -> Result { _ => MangaStatus::Unknown, }; - let nsfw = if categories.contains(&String::from("Pornographic")) { - MangaContentRating::Nsfw - } else if categories.contains(&String::from("Adult")) - || categories.contains(&String::from("Ecchi")) + let nsfw = if categories.contains(&String::from("Pornographic")) + || categories.contains(&String::from("Adult")) || categories.contains(&String::from("Smut")) { + MangaContentRating::Nsfw + } else if categories.contains(&String::from("Ecchi")) { MangaContentRating::Suggestive } else { MangaContentRating::Safe @@ -72,6 +72,7 @@ pub fn parse_manga_details(html: Node, id: String) -> Result { let viewer = if categories.contains(&String::from("Manhua")) || categories.contains(&String::from("Manhwa")) + || categories.contains(&String::from("Webtoons")) { MangaViewer::Scroll } else { From 2703318cf3419c15278aee0f957e4d01d2d82d46 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 21:44:08 -0500 Subject: [PATCH 43/84] Fix home url for mangairo --- src/rust/en.mangairo/res/source.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rust/en.mangairo/res/source.json b/src/rust/en.mangairo/res/source.json index 0634e9202..eaf0f3489 100644 --- a/src/rust/en.mangairo/res/source.json +++ b/src/rust/en.mangairo/res/source.json @@ -4,7 +4,7 @@ "lang": "en", "name": "MangaIro", "version": 1, - "url": "https://w.mangairo.com/", + "url": "https://w.mangairo.com/home", "nsfw": 1 }, "languages": [], From 6d8b05a482052f9340425b0ae2082eb7687f5080 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 21:48:19 -0500 Subject: [PATCH 44/84] Delete settings.json --- src/rust/en.mangairo/res/settings.json | 29 -------------------------- 1 file changed, 29 deletions(-) delete mode 100644 src/rust/en.mangairo/res/settings.json diff --git a/src/rust/en.mangairo/res/settings.json b/src/rust/en.mangairo/res/settings.json deleted file mode 100644 index c27ec2f1f..000000000 --- a/src/rust/en.mangairo/res/settings.json +++ /dev/null @@ -1,29 +0,0 @@ -[ - { - "type": "group", - "title": "Settings", - "footer": "You can have footers for a setting group", - "items": [ - { - "type": "select", - "key": "something", - "title": "A select option", - "values": ["your", "values", "here"], - "titles": ["your", "titles", "here"], - "default": "here" - }, - { - "type": "switch", - "key": "something2", - "title": "A switch", - "subtitle": "A subtext to describe this option", - "default": false - }, - { - "type": "text", - "key": "something3", - "placeholder": "A text box" - } - ] - } -] \ No newline at end of file From 86cbcb21a68e17b746a3a2b6f440f5f937695437 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 21:49:15 -0500 Subject: [PATCH 45/84] Create settings.json --- src/rust/en.mangairo/res/settings.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/rust/en.mangairo/res/settings.json diff --git a/src/rust/en.mangairo/res/settings.json b/src/rust/en.mangairo/res/settings.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/src/rust/en.mangairo/res/settings.json @@ -0,0 +1 @@ +{} \ No newline at end of file From b9bb0a5d428a97ebbfaa625d6f678c81dd25c15b Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 21:55:02 -0500 Subject: [PATCH 46/84] Fix settings file format --- src/rust/en.mangairo/res/settings.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rust/en.mangairo/res/settings.json b/src/rust/en.mangairo/res/settings.json index 9e26dfeeb..2789070d6 100644 --- a/src/rust/en.mangairo/res/settings.json +++ b/src/rust/en.mangairo/res/settings.json @@ -1 +1,3 @@ -{} \ No newline at end of file +[ + {} +] \ No newline at end of file From 4e1942f43861af7584250a18b8d739532e24902d Mon Sep 17 00:00:00 2001 From: getBoolean Date: Mon, 4 Sep 2023 21:57:17 -0500 Subject: [PATCH 47/84] Fix mangairo settings (2) --- src/rust/en.mangairo/res/settings.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rust/en.mangairo/res/settings.json b/src/rust/en.mangairo/res/settings.json index 2789070d6..4efbaf484 100644 --- a/src/rust/en.mangairo/res/settings.json +++ b/src/rust/en.mangairo/res/settings.json @@ -1,3 +1,6 @@ [ - {} + { + "type": "group", + "items": [] + } ] \ No newline at end of file From 81afdda87e8805430dd75e0141705472aa97e838 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Tue, 5 Sep 2023 01:30:09 -0500 Subject: [PATCH 48/84] Add category to nsfw --- src/rust/en.mangairo/src/parser.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index 0e7923b87..f2d366e8e 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -62,6 +62,7 @@ pub fn parse_manga_details(html: Node, id: String) -> Result { let nsfw = if categories.contains(&String::from("Pornographic")) || categories.contains(&String::from("Adult")) || categories.contains(&String::from("Smut")) + || categories.contains(&String::from("Erotica")) { MangaContentRating::Nsfw } else if categories.contains(&String::from("Ecchi")) { From f2fa915b68604e7aa184de2221413bde055d5794 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Tue, 5 Sep 2023 02:01:56 -0500 Subject: [PATCH 49/84] Remove debug print --- src/rust/en.mangairo/src/parser.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index f2d366e8e..67bc4f603 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -124,7 +124,6 @@ pub fn get_page_list(html: Node) -> Result> { for (i, page) in html.select(".panel-read-story img").array().enumerate() { let obj = page.as_node().expect("node array"); let url = obj.attr("src").read(); - aidoku::prelude::println!("url: {}", url); pages.push(Page { index: i as i32, From cc012921556ec8f58d42b4fea91db872e2da7a36 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Tue, 5 Sep 2023 02:09:25 -0500 Subject: [PATCH 50/84] Fix mangairo pagination --- src/rust/en.mangairo/src/lib.rs | 52 ++++++++++++++++-------------- src/rust/en.mangairo/src/parser.rs | 15 ++++++++- 2 files changed, 41 insertions(+), 26 deletions(-) diff --git a/src/rust/en.mangairo/src/lib.rs b/src/rust/en.mangairo/src/lib.rs index 0fa89cc0e..378fcb99a 100644 --- a/src/rust/en.mangairo/src/lib.rs +++ b/src/rust/en.mangairo/src/lib.rs @@ -12,8 +12,6 @@ use aidoku::{ mod parser; use parser::{BASE_URL, USER_AGENT}; -const PAGE_SIZE: usize = 50; - #[get_manga_list] fn get_manga_list(filters: Vec, page: i32) -> Result { let mut result: Vec = Vec::new(); @@ -22,19 +20,21 @@ fn get_manga_list(filters: Vec, page: i32) -> Result { parser::get_filtered_url(filters, page, &mut url); let html = Request::new(url.as_str(), HttpMethod::Get).html()?; - parser::parse_manga_list(html, &mut result); + let total_results = parser::parse_manga_list(html, &mut result); - if result.len() >= PAGE_SIZE { - Ok(MangaPageResult { - manga: result, - has_more: true, - }) - } else { - Ok(MangaPageResult { - manga: result, - has_more: false, - }) + if let Some(total_results_value) = total_results { + if total_results_value > result.len() as i32 * page { + return Ok(MangaPageResult { + manga: result, + has_more: true, + }); + } } + + Ok(MangaPageResult { + manga: result, + has_more: false, + }) } #[get_manga_details] @@ -56,19 +56,21 @@ fn get_manga_listing(listing: Listing, page: i32) -> Result { }; let html = Request::new(url.as_str(), HttpMethod::Get).html()?; let mut result: Vec = Vec::new(); - parser::parse_manga_list(html, &mut result); + let total_results = parser::parse_manga_list(html, &mut result); - if result.len() >= PAGE_SIZE { - Ok(MangaPageResult { - manga: result, - has_more: true, - }) - } else { - Ok(MangaPageResult { - manga: result, - has_more: false, - }) + if let Some(total_results_value) = total_results { + if total_results_value > result.len() as i32 * page { + return Ok(MangaPageResult { + manga: result, + has_more: true, + }); + } } + + Ok(MangaPageResult { + manga: result, + has_more: false, + }) } #[get_chapter_list] @@ -78,7 +80,7 @@ fn get_chapter_list(manga_id: String) -> Result> { } #[get_page_list] -fn get_page_list(manga_id: String, chapter_id: String,) -> Result> { +fn get_page_list(manga_id: String, chapter_id: String) -> Result> { let url = format!("{}/{}", &manga_id, &chapter_id); let html = Request::new(url.as_str(), HttpMethod::Get).html()?; parser::get_page_list(html) diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index 67bc4f603..58b747e37 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -8,7 +8,7 @@ use alloc::string::ToString; pub const BASE_URL: &str = "https://w.mangairo.com"; pub const USER_AGENT: &str = "Mozilla/5.0 (Macintosh; Intel Mac OS X 13_3_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36"; -pub fn parse_manga_list(html: Node, result: &mut Vec) { +pub fn parse_manga_list(html: Node, result: &mut Vec) -> Option { for page in html.select(".story-item").array() { let obj = page.as_node().expect("node array"); @@ -25,6 +25,19 @@ pub fn parse_manga_list(html: Node, result: &mut Vec) { }); } } + + // Example: 'Total: 38,202 stories' + let mut total_str = html.select(".quantitychapter").text().read(); + total_str = total_str.replace("Total: ", ""); + total_str = total_str.replace(" stories", ""); + total_str = total_str.chars().filter(|&c| c != ',').collect(); + + let total_result = total_str.parse::(); + if let Ok(total) = total_result { + Some(total) + } else { + None + } } pub fn parse_manga_details(html: Node, id: String) -> Result { From 1960a2546dbd23ec5ef91c436a38b73269404dea Mon Sep 17 00:00:00 2001 From: getBoolean Date: Tue, 5 Sep 2023 02:22:32 -0500 Subject: [PATCH 51/84] Fix manga details manga url --- src/rust/en.mangairo/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rust/en.mangairo/src/lib.rs b/src/rust/en.mangairo/src/lib.rs index 378fcb99a..566bd35a6 100644 --- a/src/rust/en.mangairo/src/lib.rs +++ b/src/rust/en.mangairo/src/lib.rs @@ -39,8 +39,7 @@ fn get_manga_list(filters: Vec, page: i32) -> Result { #[get_manga_details] fn get_manga_details(manga_id: String) -> Result { - let url = format!("https://chap.mangairo.com/{}", &manga_id); - let html = Request::new(url.as_str(), HttpMethod::Get).html()?; + let html = Request::new(manga_id.clone(), HttpMethod::Get).html()?; parser::parse_manga_details(html, manga_id) } From be2e1b10a10b1fb377f6e1345c8d0f036fb9c725 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Tue, 5 Sep 2023 19:51:55 -0500 Subject: [PATCH 52/84] Fix url encoding for mangairo --- src/rust/en.mangairo/src/parser.rs | 57 +++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 12 deletions(-) diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index 58b747e37..2261c9a52 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -313,20 +313,53 @@ pub fn i32_to_string(mut integer: i32) -> String { } pub fn urlencode(string: String) -> String { - let mut result: Vec = Vec::with_capacity(string.len() * 3); - let hex = "0123456789abcdef".as_bytes(); - let bytes = string.as_bytes(); - - for byte in bytes { - let curr = *byte; - if curr.is_ascii_lowercase() || curr.is_ascii_uppercase() || curr.is_ascii_digit() { - result.push(curr); + let mut str = string.to_lowercase(); + + let match_a = [ + 'à', 'á', 'ạ', 'ả', 'ã', 'â', 'ầ', 'ấ', 'ậ', 'ẩ', 'ẫ', 'ă', 'ằ', 'ắ', 'ặ', 'ẳ', 'ẵ', + ]; + let match_e = ['è', 'é', 'ẹ', 'ẻ', 'ẽ', 'ê', 'ề', 'ế', 'ệ', 'ể', 'ễ']; + let match_i = ['ì', 'í', 'ị', 'ỉ', 'ĩ']; + let match_o = [ + 'ò', 'ó', 'ọ', 'ỏ', 'õ', 'ô', 'ồ', 'ố', 'ộ', 'ổ', 'ỗ', 'ơ', 'ờ', 'ớ', 'ợ', 'ở', 'ỡ', + ]; + let match_u = ['ù', 'ú', 'ụ', 'ủ', 'ũ', 'ư', 'ừ', 'ứ', 'ự', 'ử', 'ữ']; + let match_y = ['ỳ', 'ý', 'ỵ', 'ỷ', 'ỹ']; + let match_d = "đ"; + let match_symbols = [ + '!', '@', '%', '^', '*', '(', ')', '+', '=', '<', '>', '?', '/', ',', '.', ':', ';', '\'', + ' ', '"', '&', '#', '[', ']', '~', '-', '$', '|', '_', + ]; + + str = str.replace(&match_a, "a"); + str = str.replace(&match_e, "e"); + str = str.replace(&match_i, "i"); + str = str.replace(&match_o, "o"); + str = str.replace(&match_u, "u"); + str = str.replace(&match_y, "y"); + str = str.replace(match_d, "d"); + str = str.replace(&match_symbols, "_"); + str = replace_consecutive_underscores(str); + str = str.trim_matches('_').to_string(); + + str +} + +fn replace_consecutive_underscores(input: String) -> String { + let mut result = String::new(); + let mut consecutive_underscore = false; + + for c in input.chars() { + if c == '_' { + if !consecutive_underscore { + result.push(c); + consecutive_underscore = true; + } } else { - result.push(b'%'); - result.push(hex[curr as usize >> 4]); - result.push(hex[curr as usize & 15]); + result.push(c); + consecutive_underscore = false; } } - String::from_utf8(result).unwrap_or_default() + result } From e2a6c042a5d17fe25d61c6b59db417496d0fc2dc Mon Sep 17 00:00:00 2001 From: getBoolean Date: Wed, 6 Sep 2023 00:58:14 -0500 Subject: [PATCH 53/84] MangaIro manga details now includes author --- src/rust/en.mangairo/src/parser.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index 2261c9a52..bcc8a0e35 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -61,6 +61,16 @@ pub fn parse_manga_details(html: Node, id: String) -> Result { let url = format!("{}", &id); + let mut authors: Vec = Vec::new(); + html.select(".story_info_right li:nth-child(3) a") + .array() + .for_each(|tag| { + authors.push(String::from( + tag.as_node().expect("node array").text().read().trim(), + )) + }); + let author = authors.join(", "); + let mut categories: Vec = Vec::new(); html.select(".story_info_right .a-h") .array() @@ -97,6 +107,7 @@ pub fn parse_manga_details(html: Node, id: String) -> Result { id, cover, title, + author, description, url, categories, From 1d2bc7e7e8f06796ab03b5dc9dd3e39c611ab974 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Thu, 7 Sep 2023 22:54:50 -0500 Subject: [PATCH 54/84] Use `alloc::ToString` for converting int to str --- src/rust/en.mangairo/src/parser.rs | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index bcc8a0e35..e999f7366 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -205,7 +205,7 @@ pub fn get_filtered_url(filters: Vec, page: i32, url: &mut String) { url.push_str("/list/search/"); url.push_str(&search_string); url.push_str("?page="); - url.push_str(&i32_to_string(page)); + url.push_str(&page.to_string()); } else { url.push_str("/manga-list/type-"); match sort_filter.unwrap().value.as_int().unwrap_or(-1) { @@ -273,7 +273,7 @@ pub fn get_filtered_url(filters: Vec, page: i32, url: &mut String) { } url.push_str("/page-"); - url.push_str(&i32_to_string(page)); + url.push_str(&page.to_string()); } } @@ -301,28 +301,6 @@ pub fn parse_incoming_url_chapter_id(url: String) -> Option { // HELPER FUNCTIONS -pub fn i32_to_string(mut integer: i32) -> String { - if integer == 0 { - return String::from("0"); - } - let mut string = String::with_capacity(11); - let pos = if integer < 0 { - string.insert(0, '-'); - 1 - } else { - 0 - }; - while integer != 0 { - let mut digit = integer % 10; - if pos == 1 { - digit *= -1; - } - string.insert(pos, char::from_u32((digit as u32) + ('0' as u32)).unwrap()); - integer /= 10; - } - string -} - pub fn urlencode(string: String) -> String { let mut str = string.to_lowercase(); From 67d0407f07b88c1c4bcdd6163877937f3481717a Mon Sep 17 00:00:00 2001 From: getBoolean Date: Thu, 7 Sep 2023 22:56:38 -0500 Subject: [PATCH 55/84] Use `BASE_URL` in `parser::get_filtered_url` --- src/rust/en.mangairo/src/parser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index e999f7366..a3d69f7e9 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -161,7 +161,7 @@ pub fn get_page_list(html: Node) -> Result> { pub fn get_filtered_url(filters: Vec, page: i32, url: &mut String) { let mut is_searching = false; let mut search_string = String::new(); - url.push_str("https://w.mangairo.com"); + url.push_str(BASE_URL); let title_filter: Option = filters .iter() From b1e42643183cfb4328b705f11bd85354943c857b Mon Sep 17 00:00:00 2001 From: getBoolean Date: Thu, 7 Sep 2023 22:58:34 -0500 Subject: [PATCH 56/84] Rename variable `img` to `cover` --- src/rust/en.mangairo/src/parser.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index a3d69f7e9..dd2a9a7d5 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -14,12 +14,12 @@ pub fn parse_manga_list(html: Node, result: &mut Vec) -> Option { let id = obj.select(".story-name a").attr("href").read(); let title = obj.select(".story-name a ").text().read(); - let img = obj.select(".story-list-img img").attr("src").read(); + let cover = obj.select(".story-list-img img").attr("src").read(); - if !id.is_empty() && !title.is_empty() && !img.is_empty() { + if !id.is_empty() && !title.is_empty() && !cover.is_empty() { result.push(Manga { id, - cover: img, + cover, title, ..Default::default() }); From e3876d6bd38bd7a66a732368f2f0611649b3d3ef Mon Sep 17 00:00:00 2001 From: getBoolean Date: Thu, 7 Sep 2023 23:02:02 -0500 Subject: [PATCH 57/84] Use scaled down icon instead of stretched icon --- src/rust/en.mangairo/res/Icon.png | Bin 22999 -> 8903 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/rust/en.mangairo/res/Icon.png b/src/rust/en.mangairo/res/Icon.png index 53e5998edc13c2fa27085844cb24e3f214d4935f..3475e431ccb91d5311f68e6d4cbc01fae45ed500 100644 GIT binary patch delta 5620 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 From af4783fb772eaa6218c615d8385d5837a79b688c Mon Sep 17 00:00:00 2001 From: getBoolean Date: Thu, 7 Sep 2023 23:02:24 -0500 Subject: [PATCH 58/84] Add newlines to end of json --- src/rust/en.mangairo/res/filters.json | 2 +- src/rust/en.mangairo/res/settings.json | 2 +- src/rust/en.mangairo/res/source.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rust/en.mangairo/res/filters.json b/src/rust/en.mangairo/res/filters.json index e0090d0a9..f562eb671 100644 --- a/src/rust/en.mangairo/res/filters.json +++ b/src/rust/en.mangairo/res/filters.json @@ -72,4 +72,4 @@ "Yuri" ] } -] \ No newline at end of file +] diff --git a/src/rust/en.mangairo/res/settings.json b/src/rust/en.mangairo/res/settings.json index 4efbaf484..5d027a19b 100644 --- a/src/rust/en.mangairo/res/settings.json +++ b/src/rust/en.mangairo/res/settings.json @@ -3,4 +3,4 @@ "type": "group", "items": [] } -] \ No newline at end of file +] diff --git a/src/rust/en.mangairo/res/source.json b/src/rust/en.mangairo/res/source.json index eaf0f3489..ba54527e5 100644 --- a/src/rust/en.mangairo/res/source.json +++ b/src/rust/en.mangairo/res/source.json @@ -19,4 +19,4 @@ "name": "Completed" } ] -} \ No newline at end of file +} From 965ad196f86ee0d0d0f2a8ccbbe2b6870f7062b5 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Thu, 7 Sep 2023 23:02:58 -0500 Subject: [PATCH 59/84] Fix clippy lints --- src/rust/en.mangairo/src/parser.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index dd2a9a7d5..ab2d9b3b8 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -320,14 +320,14 @@ pub fn urlencode(string: String) -> String { ' ', '"', '&', '#', '[', ']', '~', '-', '$', '|', '_', ]; - str = str.replace(&match_a, "a"); - str = str.replace(&match_e, "e"); - str = str.replace(&match_i, "i"); - str = str.replace(&match_o, "o"); - str = str.replace(&match_u, "u"); - str = str.replace(&match_y, "y"); + str = str.replace(match_a, "a"); + str = str.replace(match_e, "e"); + str = str.replace(match_i, "i"); + str = str.replace(match_o, "o"); + str = str.replace(match_u, "u"); + str = str.replace(match_y, "y"); str = str.replace(match_d, "d"); - str = str.replace(&match_symbols, "_"); + str = str.replace(match_symbols, "_"); str = replace_consecutive_underscores(str); str = str.trim_matches('_').to_string(); From 2aa120fbe5afcca5b40d8e4a353cb36513b31509 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Thu, 7 Sep 2023 23:06:42 -0500 Subject: [PATCH 60/84] Return URL from `parser::get_filtered_url` --- src/rust/en.mangairo/src/lib.rs | 3 +-- src/rust/en.mangairo/src/parser.rs | 5 ++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/rust/en.mangairo/src/lib.rs b/src/rust/en.mangairo/src/lib.rs index 566bd35a6..17ecf2ed4 100644 --- a/src/rust/en.mangairo/src/lib.rs +++ b/src/rust/en.mangairo/src/lib.rs @@ -16,8 +16,7 @@ use parser::{BASE_URL, USER_AGENT}; fn get_manga_list(filters: Vec, page: i32) -> Result { let mut result: Vec = Vec::new(); - let mut url = String::new(); - parser::get_filtered_url(filters, page, &mut url); + let url = parser::get_filtered_url(filters, page); let html = Request::new(url.as_str(), HttpMethod::Get).html()?; let total_results = parser::parse_manga_list(html, &mut result); diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index ab2d9b3b8..8dcbf8906 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -158,7 +158,8 @@ pub fn get_page_list(html: Node) -> Result> { Ok(pages) } -pub fn get_filtered_url(filters: Vec, page: i32, url: &mut String) { +pub fn get_filtered_url(filters: Vec, page: i32) -> String { + let mut url = String::new(); let mut is_searching = false; let mut search_string = String::new(); url.push_str(BASE_URL); @@ -275,6 +276,8 @@ pub fn get_filtered_url(filters: Vec, page: i32, url: &mut String) { url.push_str("/page-"); url.push_str(&page.to_string()); } + + url } pub fn parse_incoming_url_manga_id(url: String) -> Option { From d8f93d2017436dab6171777791218f3f1cb16b75 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Thu, 7 Sep 2023 23:08:45 -0500 Subject: [PATCH 61/84] Simplify has_more calculation --- src/rust/en.mangairo/src/lib.rs | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/src/rust/en.mangairo/src/lib.rs b/src/rust/en.mangairo/src/lib.rs index 17ecf2ed4..b5decdcc7 100644 --- a/src/rust/en.mangairo/src/lib.rs +++ b/src/rust/en.mangairo/src/lib.rs @@ -21,18 +21,10 @@ fn get_manga_list(filters: Vec, page: i32) -> Result { let total_results = parser::parse_manga_list(html, &mut result); - if let Some(total_results_value) = total_results { - if total_results_value > result.len() as i32 * page { - return Ok(MangaPageResult { - manga: result, - has_more: true, - }); - } - } - + let has_more = total_results.map_or(false, |value| value > result.len() as i32 * page); Ok(MangaPageResult { manga: result, - has_more: false, + has_more: has_more, }) } @@ -56,18 +48,10 @@ fn get_manga_listing(listing: Listing, page: i32) -> Result { let mut result: Vec = Vec::new(); let total_results = parser::parse_manga_list(html, &mut result); - if let Some(total_results_value) = total_results { - if total_results_value > result.len() as i32 * page { - return Ok(MangaPageResult { - manga: result, - has_more: true, - }); - } - } - + let has_more = total_results.map_or(false, |value| value > result.len() as i32 * page); Ok(MangaPageResult { manga: result, - has_more: false, + has_more: has_more, }) } From 5111bddc2899a4e11b47682773fd6e6f73abb1b4 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Thu, 7 Sep 2023 23:13:14 -0500 Subject: [PATCH 62/84] Use tuple for `parser::get_manga_list` --- src/rust/en.mangairo/src/lib.rs | 6 ++---- src/rust/en.mangairo/src/parser.rs | 10 +++------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/rust/en.mangairo/src/lib.rs b/src/rust/en.mangairo/src/lib.rs index b5decdcc7..1bb3d10ab 100644 --- a/src/rust/en.mangairo/src/lib.rs +++ b/src/rust/en.mangairo/src/lib.rs @@ -14,12 +14,11 @@ use parser::{BASE_URL, USER_AGENT}; #[get_manga_list] fn get_manga_list(filters: Vec, page: i32) -> Result { - let mut result: Vec = Vec::new(); let url = parser::get_filtered_url(filters, page); let html = Request::new(url.as_str(), HttpMethod::Get).html()?; - let total_results = parser::parse_manga_list(html, &mut result); + let (result, total_results) = parser::parse_manga_list(html); let has_more = total_results.map_or(false, |value| value > result.len() as i32 * page); Ok(MangaPageResult { @@ -45,8 +44,7 @@ fn get_manga_listing(listing: Listing, page: i32) -> Result { _ => format!("{BASE_URL}/manga-list/type-latest/ctg-all/state-all/page-{page}"), }; let html = Request::new(url.as_str(), HttpMethod::Get).html()?; - let mut result: Vec = Vec::new(); - let total_results = parser::parse_manga_list(html, &mut result); + let (result, total_results) = parser::parse_manga_list(html); let has_more = total_results.map_or(false, |value| value > result.len() as i32 * page); Ok(MangaPageResult { diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index 8dcbf8906..d045ce428 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -8,7 +8,8 @@ use alloc::string::ToString; pub const BASE_URL: &str = "https://w.mangairo.com"; pub const USER_AGENT: &str = "Mozilla/5.0 (Macintosh; Intel Mac OS X 13_3_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36"; -pub fn parse_manga_list(html: Node, result: &mut Vec) -> Option { +pub fn parse_manga_list(html: Node) -> (Vec, Option) { + let mut result: Vec = Vec::new(); for page in html.select(".story-item").array() { let obj = page.as_node().expect("node array"); @@ -32,12 +33,7 @@ pub fn parse_manga_list(html: Node, result: &mut Vec) -> Option { total_str = total_str.replace(" stories", ""); total_str = total_str.chars().filter(|&c| c != ',').collect(); - let total_result = total_str.parse::(); - if let Ok(total) = total_result { - Some(total) - } else { - None - } + (result, total_str.parse::().ok()) } pub fn parse_manga_details(html: Node, id: String) -> Result { From 59a9dd8984077e98441a4427836d5f88296a782e Mon Sep 17 00:00:00 2001 From: getBoolean Date: Thu, 7 Sep 2023 23:14:12 -0500 Subject: [PATCH 63/84] Rename `result` to `manga` and fix lint --- src/rust/en.mangairo/src/lib.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/rust/en.mangairo/src/lib.rs b/src/rust/en.mangairo/src/lib.rs index 1bb3d10ab..85d580fa5 100644 --- a/src/rust/en.mangairo/src/lib.rs +++ b/src/rust/en.mangairo/src/lib.rs @@ -18,12 +18,12 @@ fn get_manga_list(filters: Vec, page: i32) -> Result { let url = parser::get_filtered_url(filters, page); let html = Request::new(url.as_str(), HttpMethod::Get).html()?; - let (result, total_results) = parser::parse_manga_list(html); + let (manga, total_results) = parser::parse_manga_list(html); - let has_more = total_results.map_or(false, |value| value > result.len() as i32 * page); + let has_more = total_results.map_or(false, |value| value > manga.len() as i32 * page); Ok(MangaPageResult { - manga: result, - has_more: has_more, + manga, + has_more, }) } @@ -44,12 +44,12 @@ fn get_manga_listing(listing: Listing, page: i32) -> Result { _ => format!("{BASE_URL}/manga-list/type-latest/ctg-all/state-all/page-{page}"), }; let html = Request::new(url.as_str(), HttpMethod::Get).html()?; - let (result, total_results) = parser::parse_manga_list(html); + let (manga, total_results) = parser::parse_manga_list(html); - let has_more = total_results.map_or(false, |value| value > result.len() as i32 * page); + let has_more = total_results.map_or(false, |value| value > manga.len() as i32 * page); Ok(MangaPageResult { - manga: result, - has_more: has_more, + manga, + has_more, }) } From dea87569c8b75500adb707aa2144c0ee04f98719 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Thu, 7 Sep 2023 23:19:23 -0500 Subject: [PATCH 64/84] Return `has_more` instead of total result size --- src/rust/en.mangairo/src/lib.rs | 7 ++----- src/rust/en.mangairo/src/parser.rs | 5 +++-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/rust/en.mangairo/src/lib.rs b/src/rust/en.mangairo/src/lib.rs index 85d580fa5..1564bdac4 100644 --- a/src/rust/en.mangairo/src/lib.rs +++ b/src/rust/en.mangairo/src/lib.rs @@ -18,9 +18,7 @@ fn get_manga_list(filters: Vec, page: i32) -> Result { let url = parser::get_filtered_url(filters, page); let html = Request::new(url.as_str(), HttpMethod::Get).html()?; - let (manga, total_results) = parser::parse_manga_list(html); - - let has_more = total_results.map_or(false, |value| value > manga.len() as i32 * page); + let (manga, has_more) = parser::parse_manga_list(html, page); Ok(MangaPageResult { manga, has_more, @@ -44,9 +42,8 @@ fn get_manga_listing(listing: Listing, page: i32) -> Result { _ => format!("{BASE_URL}/manga-list/type-latest/ctg-all/state-all/page-{page}"), }; let html = Request::new(url.as_str(), HttpMethod::Get).html()?; - let (manga, total_results) = parser::parse_manga_list(html); + let (manga, has_more) = parser::parse_manga_list(html, page); - let has_more = total_results.map_or(false, |value| value > manga.len() as i32 * page); Ok(MangaPageResult { manga, has_more, diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index d045ce428..29e43e66a 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -8,7 +8,7 @@ use alloc::string::ToString; pub const BASE_URL: &str = "https://w.mangairo.com"; pub const USER_AGENT: &str = "Mozilla/5.0 (Macintosh; Intel Mac OS X 13_3_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36"; -pub fn parse_manga_list(html: Node) -> (Vec, Option) { +pub fn parse_manga_list(html: Node, page: i32) -> (Vec, bool) { let mut result: Vec = Vec::new(); for page in html.select(".story-item").array() { let obj = page.as_node().expect("node array"); @@ -33,7 +33,8 @@ pub fn parse_manga_list(html: Node) -> (Vec, Option) { total_str = total_str.replace(" stories", ""); total_str = total_str.chars().filter(|&c| c != ',').collect(); - (result, total_str.parse::().ok()) + let has_more = total_str.parse::().map_or(false, |value| value > result.len() as i32 * page); + (result, has_more) } pub fn parse_manga_details(html: Node, id: String) -> Result { From 4474d9a3669933ad23780cd71d9bf60f63f3852b Mon Sep 17 00:00:00 2001 From: getBoolean Date: Thu, 7 Sep 2023 23:37:35 -0500 Subject: [PATCH 65/84] Use `QueryParameters` for `page` URL parameter --- src/rust/en.mangairo/Cargo.lock | 9 +++++++++ src/rust/en.mangairo/Cargo.toml | 2 +- src/rust/en.mangairo/src/parser.rs | 10 ++++++---- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/rust/en.mangairo/Cargo.lock b/src/rust/en.mangairo/Cargo.lock index d6b4d1334..06525e42a 100644 --- a/src/rust/en.mangairo/Cargo.lock +++ b/src/rust/en.mangairo/Cargo.lock @@ -7,12 +7,21 @@ name = "aidoku" version = "0.2.0" source = "git+https://github.com/Aidoku/aidoku-rs#004bddabade7b24c58cf925b08f90dd093b00c9d" dependencies = [ + "aidoku_helpers", "aidoku_imports", "aidoku_macros", "aidoku_proc_macros", "dlmalloc", ] +[[package]] +name = "aidoku_helpers" +version = "0.1.0" +source = "git+https://github.com/Aidoku/aidoku-rs#004bddabade7b24c58cf925b08f90dd093b00c9d" +dependencies = [ + "aidoku_imports", +] + [[package]] name = "aidoku_imports" version = "0.2.0" diff --git a/src/rust/en.mangairo/Cargo.toml b/src/rust/en.mangairo/Cargo.toml index a18b61da3..39ff375fa 100644 --- a/src/rust/en.mangairo/Cargo.toml +++ b/src/rust/en.mangairo/Cargo.toml @@ -16,4 +16,4 @@ strip = true lto = true [dependencies] -aidoku = { git = "https://github.com/Aidoku/aidoku-rs" } +aidoku = { git = "https://github.com/Aidoku/aidoku-rs", features = ["helpers"] } diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index 29e43e66a..00b5c932c 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -1,6 +1,7 @@ use aidoku::{ error::Result, prelude::*, std::html::Node, std::String, std::Vec, Chapter, Filter, FilterType, Manga, MangaContentRating, MangaStatus, MangaViewer, Page, + helpers::uri::QueryParameters, }; extern crate alloc; use alloc::string::ToString; @@ -156,10 +157,8 @@ pub fn get_page_list(html: Node) -> Result> { } pub fn get_filtered_url(filters: Vec, page: i32) -> String { - let mut url = String::new(); let mut is_searching = false; let mut search_string = String::new(); - url.push_str(BASE_URL); let title_filter: Option = filters .iter() @@ -199,11 +198,14 @@ pub fn get_filtered_url(filters: Vec, page: i32) -> String { } } + let mut url = String::new(); + url.push_str(BASE_URL); if is_searching { url.push_str("/list/search/"); url.push_str(&search_string); - url.push_str("?page="); - url.push_str(&page.to_string()); + let mut query = QueryParameters::new(); + query.set("page", Some(page.to_string().as_str())); + url.push_str(&format!("?{query}")); } else { url.push_str("/manga-list/type-"); match sort_filter.unwrap().value.as_int().unwrap_or(-1) { From b6098f65f1dff0c0348f0c92d635855af027d9f1 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Fri, 8 Sep 2023 00:25:33 -0500 Subject: [PATCH 66/84] Refactor `parser::get_filtered_url` to use match --- src/rust/en.mangairo/src/parser.rs | 221 ++++++++++++++--------------- 1 file changed, 109 insertions(+), 112 deletions(-) diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index 00b5c932c..95e595ee3 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -1,7 +1,7 @@ use aidoku::{ - error::Result, prelude::*, std::html::Node, std::String, std::Vec, Chapter, Filter, FilterType, - Manga, MangaContentRating, MangaStatus, MangaViewer, Page, - helpers::uri::QueryParameters, + error::Result, helpers::uri::QueryParameters, prelude::*, std::html::Node, std::String, + std::Vec, Chapter, Filter, FilterType, Manga, MangaContentRating, MangaStatus, MangaViewer, + Page, }; extern crate alloc; use alloc::string::ToString; @@ -34,7 +34,9 @@ pub fn parse_manga_list(html: Node, page: i32) -> (Vec, bool) { total_str = total_str.replace(" stories", ""); total_str = total_str.chars().filter(|&c| c != ',').collect(); - let has_more = total_str.parse::().map_or(false, |value| value > result.len() as i32 * page); + let has_more = total_str + .parse::() + .map_or(false, |value| value > result.len() as i32 * page); (result, has_more) } @@ -158,125 +160,120 @@ pub fn get_page_list(html: Node) -> Result> { pub fn get_filtered_url(filters: Vec, page: i32) -> String { let mut is_searching = false; - let mut search_string = String::new(); - let title_filter: Option = filters - .iter() - .find(|&x| x.kind == FilterType::Title) - .cloned(); - let author_filter: Option = filters - .iter() - .find(|&x| x.kind == FilterType::Author) - .cloned(); - let status_filter: Option = filters - .iter() - .find(|&x| x.kind == FilterType::Select && x.name == "Status") - .cloned(); - let sort_filter: Option = filters - .iter() - .find(|&x| x.kind == FilterType::Select && x.name == "Sort") - .cloned(); - let genre_filter: Option = filters - .iter() - .find(|&x| x.kind == FilterType::Select && x.name == "Genre") - .cloned(); - - if let Some(title_filter_value) = title_filter { - if let Ok(filter_value) = title_filter_value.value.as_string() { - search_string.push_str(urlencode(filter_value.read().to_lowercase()).as_str()); - is_searching = true; + let mut title_filter = String::new(); + let mut author_filter = String::new(); + let mut sort_filter = -1; + let mut genre_filter = -1; + let mut status_filter = -1; + for filter in filters { + match filter.kind { + FilterType::Title => { + if let Ok(filter) = filter.value.as_string() { + title_filter = urlencode(filter.read().to_lowercase()); + is_searching = true; + } + } + FilterType::Author => { + if let Ok(filter) = filter.value.as_string() { + author_filter = urlencode(filter.read().to_lowercase()); + is_searching = true; + } + } + FilterType::Select => { + if filter.name.as_str() == "Sort" { + sort_filter = filter.value.as_int().unwrap_or(-1); + } + if filter.name.as_str() == "Genre" { + genre_filter = filter.value.as_int().unwrap_or(-1) + } + if filter.name.as_str() == "Status" { + status_filter = filter.value.as_int().unwrap_or(-1) + } + } + _ => continue, } } - if let Some(author_filter_value) = author_filter { - if let Ok(filter_value) = author_filter_value.value.as_string() { - if !search_string.is_empty() { - search_string.push('_'); - } - search_string.push_str(urlencode(filter_value.read().to_lowercase()).as_str()); - is_searching = true; - } + let mut search_string = String::new(); + search_string.push_str(&title_filter); + if !search_string.is_empty() && !author_filter.is_empty() { + search_string.push('_'); } + search_string.push_str(&author_filter); - let mut url = String::new(); - url.push_str(BASE_URL); if is_searching { - url.push_str("/list/search/"); - url.push_str(&search_string); let mut query = QueryParameters::new(); query.set("page", Some(page.to_string().as_str())); - url.push_str(&format!("?{query}")); - } else { - url.push_str("/manga-list/type-"); - match sort_filter.unwrap().value.as_int().unwrap_or(-1) { - 0 => url.push_str("latest"), - 1 => url.push_str("newest"), - 2 => url.push_str("topview"), - _ => url.push_str("latest"), - } - // Genre - url.push_str("/ctg-"); - match genre_filter.unwrap().value.as_int().unwrap_or(-1) { - 0 => url.push_str("all"), // "All", - 1 => url.push('2'), // "Action", - 2 => url.push('3'), // "Adult", - 3 => url.push('4'), // "Adventure", - 4 => url.push('6'), // "Comedy", - 5 => url.push('7'), // "Cooking", - 6 => url.push('9'), // "Doujinshi", - 7 => url.push_str("10"), // "Drama", - 8 => url.push_str("11"), // "Ecchi", - 9 => url.push_str("48"), // "Erotica", - 10 => url.push_str("12"), // "Fantasy", - 11 => url.push_str("13"), // "Gender bender", - 12 => url.push_str("14"), // "Harem", - 13 => url.push_str("15"), // "Historical", - 14 => url.push_str("16"), // "Horror", - 15 => url.push_str("45"), // "Isekai", - 16 => url.push_str("17"), // "Josei", - 17 => url.push_str("44"), // "Manhua", - 18 => url.push_str("43"), // "Manhwa", - 19 => url.push_str("19"), // "Martial arts", - 20 => url.push_str("20"), // "Mature", - 21 => url.push_str("21"), // "Mecha", - 22 => url.push_str("22"), // "Medical", - 23 => url.push_str("24"), // "Mystery", - 24 => url.push_str("25"), // "One shot", - 25 => url.push_str("47"), // "Pornographic", - 26 => url.push_str("26"), // "Phychological", - 27 => url.push_str("27"), // "Romance", - 28 => url.push_str("28"), // "School life", - 29 => url.push_str("29"), // "Sci fi", - 30 => url.push_str("30"), // "Seinen", - 31 => url.push_str("31"), // "Shoujo", - 32 => url.push_str("32"), // "Shoujo ai", - 33 => url.push_str("33"), // "Shounen", - 34 => url.push_str("34"), // "Shounen ai", - 35 => url.push_str("35"), // "Slice of Life", - 36 => url.push_str("36"), // "Smut", - 37 => url.push_str("37"), // "Sports", - 38 => url.push_str("38"), // "Supernatural", - 39 => url.push_str("39"), // "Tragedy", - 40 => url.push_str("40"), // "Webtoons", - 41 => url.push_str("41"), // "Yaoi", - 42 => url.push_str("42"), // "Yuri" - _ => url.push_str("all"), - } - // State - url.push_str("/state-"); - match status_filter.unwrap().value.as_int().unwrap_or(0) { - 0 => url.push_str("all"), - 1 => url.push_str("ongoing"), - 2 => url.push_str("completed"), - _ => url.push_str("all"), - } - - url.push_str("/page-"); - url.push_str(&page.to_string()); + return format!("{BASE_URL}/list/search/{search_string}?{query}"); } - - url + + let sort = match sort_filter { + 0 => "latest", + 1 => "newest", + 2 => "topview", + _ => "latest", + }; + + // Genre + let ctg = match genre_filter { + 0 => "all", // "All", + 1 => "2", // "Action", + 2 => "3", // "Adult", + 3 => "4", // "Adventure", + 4 => "6", // "Comedy", + 5 => "7", // "Cooking", + 6 => "9", // "Doujinshi", + 7 => "10", // "Drama", + 8 => "11", // "Ecchi", + 9 => "48", // "Erotica", + 10 => "12", // "Fantasy", + 11 => "13", // "Gender bender", + 12 => "14", // "Harem", + 13 => "15", // "Historical", + 14 => "16", // "Horror", + 15 => "45", // "Isekai", + 16 => "17", // "Josei", + 17 => "44", // "Manhua", + 18 => "43", // "Manhwa", + 19 => "19", // "Martial arts", + 20 => "20", // "Mature", + 21 => "21", // "Mecha", + 22 => "22", // "Medical", + 23 => "24", // "Mystery", + 24 => "25", // "One shot", + 25 => "47", // "Pornographic", + 26 => "26", // "Phychological", + 27 => "27", // "Romance", + 28 => "28", // "School life", + 29 => "29", // "Sci fi", + 30 => "30", // "Seinen", + 31 => "31", // "Shoujo", + 32 => "32", // "Shoujo ai", + 33 => "33", // "Shounen", + 34 => "34", // "Shounen ai", + 35 => "35", // "Slice of Life", + 36 => "36", // "Smut", + 37 => "37", // "Sports", + 38 => "38", // "Supernatural", + 39 => "39", // "Tragedy", + 40 => "40", // "Webtoons", + 41 => "41", // "Yaoi", + 42 => "42", // "Yuri" + _ => "all", + }; + + // State + let status = match status_filter { + 0 => "all", + 1 => "ongoing", + 2 => "completed", + _ => "all", + }; + + let page = &page.to_string(); + format!("{BASE_URL}/manga-list/type-{sort}/ctg-{ctg}/state-{status}/page-{page}") } pub fn parse_incoming_url_manga_id(url: String) -> Option { From 71d1ad73275d34e2f3086a0011a7ff81dd7313e9 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Fri, 8 Sep 2023 00:26:33 -0500 Subject: [PATCH 67/84] Rename `chap_num` to `chapter` --- src/rust/en.mangairo/src/parser.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index 95e595ee3..85694c8d8 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -128,11 +128,11 @@ pub fn get_chapter_list(html: Node) -> Result> { if let Some(id_value) = id { let split = id_value.split('-'); let vec = split.collect::>(); - let chap_num = vec[vec.len() - 1].parse().unwrap(); + let chapter = vec[vec.len() - 1].parse().unwrap(); chapters.push(Chapter { id: id_value, - chapter: chap_num, + chapter, url, lang: String::from("en"), ..Default::default() From da4db1fec8ddf412280a0c33a106333759a65190 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Fri, 8 Sep 2023 00:30:37 -0500 Subject: [PATCH 68/84] Don't rename when unwrapping --- src/rust/en.mangairo/src/parser.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index 85694c8d8..7de4beeaa 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -125,13 +125,14 @@ pub fn get_chapter_list(html: Node) -> Result> { let url = obj.attr("href").read(); let id = parse_incoming_url_chapter_id(url.clone()); - if let Some(id_value) = id { - let split = id_value.split('-'); + if let Some(id) = id { + let split = id.split('-'); let vec = split.collect::>(); let chapter = vec[vec.len() - 1].parse().unwrap(); + let lang: String = "en".to_string(); chapters.push(Chapter { - id: id_value, + id, chapter, url, lang: String::from("en"), From 5756bfa32d1f4e9f61c69499c55d3463232e9077 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Fri, 8 Sep 2023 00:30:47 -0500 Subject: [PATCH 69/84] Extract lang to variable --- src/rust/en.mangairo/src/parser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index 7de4beeaa..f6f1e88d8 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -135,7 +135,7 @@ pub fn get_chapter_list(html: Node) -> Result> { id, chapter, url, - lang: String::from("en"), + lang, ..Default::default() }); } From 77fbf8a66c7bbcb19425cd8a90c0c10b42c6ca80 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Fri, 8 Sep 2023 00:36:15 -0500 Subject: [PATCH 70/84] Default chapter number to -1 --- src/rust/en.mangairo/src/parser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index f6f1e88d8..e8c1fa5f9 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -128,7 +128,7 @@ pub fn get_chapter_list(html: Node) -> Result> { if let Some(id) = id { let split = id.split('-'); let vec = split.collect::>(); - let chapter = vec[vec.len() - 1].parse().unwrap(); + let chapter: f32 = vec[vec.len() - 1].parse().unwrap_or(-1.0); let lang: String = "en".to_string(); chapters.push(Chapter { From 9a6a40118e671167720f42203632c14610d9b7e8 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Fri, 8 Sep 2023 00:43:57 -0500 Subject: [PATCH 71/84] Run `cargo fmt` --- src/rust/en.mangairo/src/lib.rs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/rust/en.mangairo/src/lib.rs b/src/rust/en.mangairo/src/lib.rs index 1564bdac4..51f339ae0 100644 --- a/src/rust/en.mangairo/src/lib.rs +++ b/src/rust/en.mangairo/src/lib.rs @@ -14,15 +14,11 @@ use parser::{BASE_URL, USER_AGENT}; #[get_manga_list] fn get_manga_list(filters: Vec, page: i32) -> Result { - let url = parser::get_filtered_url(filters, page); let html = Request::new(url.as_str(), HttpMethod::Get).html()?; let (manga, has_more) = parser::parse_manga_list(html, page); - Ok(MangaPageResult { - manga, - has_more, - }) + Ok(MangaPageResult { manga, has_more }) } #[get_manga_details] @@ -44,10 +40,7 @@ fn get_manga_listing(listing: Listing, page: i32) -> Result { let html = Request::new(url.as_str(), HttpMethod::Get).html()?; let (manga, has_more) = parser::parse_manga_list(html, page); - Ok(MangaPageResult { - manga, - has_more, - }) + Ok(MangaPageResult { manga, has_more }) } #[get_chapter_list] From 801a2fa0ed347bdfd88f21fd6681e1ff253df090 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Fri, 8 Sep 2023 01:12:29 -0500 Subject: [PATCH 72/84] Add "Latest" Listing --- src/rust/en.mangairo/res/source.json | 3 +++ src/rust/en.mangairo/src/lib.rs | 1 + 2 files changed, 4 insertions(+) diff --git a/src/rust/en.mangairo/res/source.json b/src/rust/en.mangairo/res/source.json index ba54527e5..d1ba9e6c6 100644 --- a/src/rust/en.mangairo/res/source.json +++ b/src/rust/en.mangairo/res/source.json @@ -9,6 +9,9 @@ }, "languages": [], "listings": [ + { + "name": "Latest" + }, { "name": "New Releases" }, diff --git a/src/rust/en.mangairo/src/lib.rs b/src/rust/en.mangairo/src/lib.rs index 51f339ae0..5825e1036 100644 --- a/src/rust/en.mangairo/src/lib.rs +++ b/src/rust/en.mangairo/src/lib.rs @@ -30,6 +30,7 @@ fn get_manga_details(manga_id: String) -> Result { #[get_manga_listing] fn get_manga_listing(listing: Listing, page: i32) -> Result { let url = match listing.name.as_str() { + "Latest" => format!("{BASE_URL}/manga-list/type-latest/ctg-all/state-all/page-{page}"), "New Releases" => format!("{BASE_URL}/manga-list/type-newest/ctg-7/state-all/page-{page}"), "Hot" => format!("{BASE_URL}/manga-list/type-topview/ctg-all/state-all/page-{page}"), "Completed" => { From 2f37a5d5ee68780347d8f3e63174fdbd33c49341 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Sun, 17 Sep 2023 01:52:47 -0500 Subject: [PATCH 73/84] Fix info source link for mangairo --- src/rust/en.mangairo/res/source.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rust/en.mangairo/res/source.json b/src/rust/en.mangairo/res/source.json index d1ba9e6c6..e57bb5312 100644 --- a/src/rust/en.mangairo/res/source.json +++ b/src/rust/en.mangairo/res/source.json @@ -4,7 +4,7 @@ "lang": "en", "name": "MangaIro", "version": 1, - "url": "https://w.mangairo.com/home", + "url": "https://w.mangairo.com", "nsfw": 1 }, "languages": [], From 786e87dcc7463da8c68a6aa60bcb7f16da06b0ca Mon Sep 17 00:00:00 2001 From: getBoolean Date: Sun, 17 Sep 2023 01:57:53 -0500 Subject: [PATCH 74/84] Unwrap parsed_manga_id with if let --- src/rust/en.mangairo/src/lib.rs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/rust/en.mangairo/src/lib.rs b/src/rust/en.mangairo/src/lib.rs index 5825e1036..351377d3e 100644 --- a/src/rust/en.mangairo/src/lib.rs +++ b/src/rust/en.mangairo/src/lib.rs @@ -69,16 +69,17 @@ fn handle_url(url: String) -> Result { let parsed_manga_id = parser::parse_incoming_url_manga_id(url.clone()); let parsed_chapter_id = parser::parse_incoming_url_chapter_id(url); - if parsed_manga_id.is_none() { - return Err(aidoku::error::AidokuError { + if let Some(parsed_manga_id) = parsed_manga_id { + Ok(DeepLink { + manga: Some(get_manga_details(parsed_manga_id)?), + chapter: parsed_chapter_id.map(|chapter_id_value| Chapter { + id: chapter_id_value, + ..Default::default() + }), + }) + } else { + Err(aidoku::error::AidokuError { reason: aidoku::error::AidokuErrorKind::Unimplemented, - }); + }) } - Ok(DeepLink { - manga: Some(get_manga_details(parsed_manga_id.unwrap())?), - chapter: parsed_chapter_id.map(|chapter_id_value| Chapter { - id: chapter_id_value, - ..Default::default() - }), - }) } From 05a65812123f61573476f9d39e9598d14df8f36b Mon Sep 17 00:00:00 2001 From: getBoolean Date: Sun, 17 Sep 2023 02:00:15 -0500 Subject: [PATCH 75/84] total_str no longer mut --- src/rust/en.mangairo/src/parser.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index e8c1fa5f9..f47742348 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -29,10 +29,15 @@ pub fn parse_manga_list(html: Node, page: i32) -> (Vec, bool) { } // Example: 'Total: 38,202 stories' - let mut total_str = html.select(".quantitychapter").text().read(); - total_str = total_str.replace("Total: ", ""); - total_str = total_str.replace(" stories", ""); - total_str = total_str.chars().filter(|&c| c != ',').collect(); + let total_str: String = html + .select(".quantitychapter") + .text() + .read() + .replace("Total: ", "") + .replace(" stories", "") + .chars() + .filter(|&c| c != ',') + .collect(); let has_more = total_str .parse::() From 5a80bb5d14fbe68cfbf86beec2ed37e31ac5c8c1 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Sun, 17 Sep 2023 02:03:04 -0500 Subject: [PATCH 76/84] Use suggested change for chapter number parsing --- src/rust/en.mangairo/src/parser.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index f47742348..58a00cdb3 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -131,9 +131,11 @@ pub fn get_chapter_list(html: Node) -> Result> { let id = parse_incoming_url_chapter_id(url.clone()); if let Some(id) = id { - let split = id.split('-'); - let vec = split.collect::>(); - let chapter: f32 = vec[vec.len() - 1].parse().unwrap_or(-1.0); + let chapter = id + .rsplit_once('-') + .map(|v| v.1.parse::().ok()) + .flatten() + .unwrap_or(-1.0); let lang: String = "en".to_string(); chapters.push(Chapter { From 1f93bb9cc5cb60cad573142cde6add0a592bfdfc Mon Sep 17 00:00:00 2001 From: getBoolean Date: Sun, 17 Sep 2023 02:15:35 -0500 Subject: [PATCH 77/84] Calculate author and categories with map --- src/rust/en.mangairo/src/parser.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index 58a00cdb3..ba1ad22dd 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -66,20 +66,19 @@ pub fn parse_manga_details(html: Node, id: String) -> Result { let url = format!("{}", &id); - let mut authors: Vec = Vec::new(); - html.select(".story_info_right li:nth-child(3) a") + let author: String = html.select(".story_info_right li:nth-child(3) a") .array() - .for_each(|tag| { - authors.push(String::from( + .map(|tag| { + String::from( tag.as_node().expect("node array").text().read().trim(), - )) - }); - let author = authors.join(", "); + ) + }).collect::>().join(", "); - let mut categories: Vec = Vec::new(); - html.select(".story_info_right .a-h") + let categories: Vec = html + .select(".story_info_right .a-h") .array() - .for_each(|tag| categories.push(tag.as_node().expect("node array").text().read())); + .map(|tag| tag.as_node().expect("node array").text().read()) + .collect(); let status = match status_str.as_str() { "ongoing" => MangaStatus::Ongoing, From 706a6cc0ac28e615547579b3678c18508b5f3a43 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Sun, 17 Sep 2023 02:16:26 -0500 Subject: [PATCH 78/84] Resolve lint for calculating chapter --- src/rust/en.mangairo/src/parser.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index ba1ad22dd..9e220e52d 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -132,8 +132,7 @@ pub fn get_chapter_list(html: Node) -> Result> { if let Some(id) = id { let chapter = id .rsplit_once('-') - .map(|v| v.1.parse::().ok()) - .flatten() + .and_then(|v| v.1.parse::().ok()) .unwrap_or(-1.0); let lang: String = "en".to_string(); From 2a68712e2a2f56b9e3923c1b04dbd7736ffd1310 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Sun, 17 Sep 2023 02:21:45 -0500 Subject: [PATCH 79/84] Use simpler replace_consecutive_underscores --- src/rust/en.mangairo/src/parser.rs | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index 9e220e52d..f6c3db8ba 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -333,27 +333,8 @@ pub fn urlencode(string: String) -> String { str = str.replace(match_y, "y"); str = str.replace(match_d, "d"); str = str.replace(match_symbols, "_"); - str = replace_consecutive_underscores(str); + str = str.replace("__", "_"); str = str.trim_matches('_').to_string(); str } - -fn replace_consecutive_underscores(input: String) -> String { - let mut result = String::new(); - let mut consecutive_underscore = false; - - for c in input.chars() { - if c == '_' { - if !consecutive_underscore { - result.push(c); - consecutive_underscore = true; - } - } else { - result.push(c); - consecutive_underscore = false; - } - } - - result -} From 092b8e04039eca4629fa0caf0826ce4359ea3163 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Sat, 23 Sep 2023 20:37:44 -0500 Subject: [PATCH 80/84] Delete settings.json --- src/rust/en.mangairo/res/settings.json | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 src/rust/en.mangairo/res/settings.json diff --git a/src/rust/en.mangairo/res/settings.json b/src/rust/en.mangairo/res/settings.json deleted file mode 100644 index 5d027a19b..000000000 --- a/src/rust/en.mangairo/res/settings.json +++ /dev/null @@ -1,6 +0,0 @@ -[ - { - "type": "group", - "items": [] - } -] From 03718ee47428171e121a775ae1a1041253b1bbdd Mon Sep 17 00:00:00 2001 From: getBoolean Date: Sat, 23 Sep 2023 20:40:40 -0500 Subject: [PATCH 81/84] Use & instead of clone --- src/rust/en.mangairo/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rust/en.mangairo/src/lib.rs b/src/rust/en.mangairo/src/lib.rs index 351377d3e..712f9ba3b 100644 --- a/src/rust/en.mangairo/src/lib.rs +++ b/src/rust/en.mangairo/src/lib.rs @@ -23,7 +23,7 @@ fn get_manga_list(filters: Vec, page: i32) -> Result { #[get_manga_details] fn get_manga_details(manga_id: String) -> Result { - let html = Request::new(manga_id.clone(), HttpMethod::Get).html()?; + let html = Request::new(&manga_id, HttpMethod::Get).html()?; parser::parse_manga_details(html, manga_id) } From 1df96e67c0fb6d2eddfa449a26edb383c959ce99 Mon Sep 17 00:00:00 2001 From: getBoolean Date: Sat, 23 Sep 2023 20:41:57 -0500 Subject: [PATCH 82/84] Add chap url variant for deeplinks --- src/rust/en.mangairo/res/source.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rust/en.mangairo/res/source.json b/src/rust/en.mangairo/res/source.json index e57bb5312..3a9885776 100644 --- a/src/rust/en.mangairo/res/source.json +++ b/src/rust/en.mangairo/res/source.json @@ -4,7 +4,10 @@ "lang": "en", "name": "MangaIro", "version": 1, - "url": "https://w.mangairo.com", + "urls": [ + "https://w.mangairo.com", + "https://chap.mangairo.com" + ], "nsfw": 1 }, "languages": [], From 94fbfc8a9fc4876299b99da0345012a2be11230b Mon Sep 17 00:00:00 2001 From: getBoolean Date: Sat, 23 Sep 2023 21:52:12 -0500 Subject: [PATCH 83/84] Assign URL var instead of using manga_id directly --- src/rust/en.mangairo/src/lib.rs | 16 +++++++--- src/rust/en.mangairo/src/parser.rs | 47 ++++++++++++++++++------------ 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/src/rust/en.mangairo/src/lib.rs b/src/rust/en.mangairo/src/lib.rs index 712f9ba3b..c42383e33 100644 --- a/src/rust/en.mangairo/src/lib.rs +++ b/src/rust/en.mangairo/src/lib.rs @@ -15,6 +15,7 @@ use parser::{BASE_URL, USER_AGENT}; #[get_manga_list] fn get_manga_list(filters: Vec, page: i32) -> Result { let url = parser::get_filtered_url(filters, page); + // aidoku::prelude::println!("get_manga_list: {}", url); let html = Request::new(url.as_str(), HttpMethod::Get).html()?; let (manga, has_more) = parser::parse_manga_list(html, page); @@ -23,7 +24,8 @@ fn get_manga_list(filters: Vec, page: i32) -> Result { #[get_manga_details] fn get_manga_details(manga_id: String) -> Result { - let html = Request::new(&manga_id, HttpMethod::Get).html()?; + let url = format!("{}", &manga_id); + let html = Request::new(url, HttpMethod::Get).html()?; parser::parse_manga_details(html, manga_id) } @@ -38,6 +40,7 @@ fn get_manga_listing(listing: Listing, page: i32) -> Result { } _ => format!("{BASE_URL}/manga-list/type-latest/ctg-all/state-all/page-{page}"), }; + // aidoku::prelude::println!("get_manga_listing: {}", url); let html = Request::new(url.as_str(), HttpMethod::Get).html()?; let (manga, has_more) = parser::parse_manga_list(html, page); @@ -46,13 +49,16 @@ fn get_manga_listing(listing: Listing, page: i32) -> Result { #[get_chapter_list] fn get_chapter_list(manga_id: String) -> Result> { - let html = Request::new(manga_id, HttpMethod::Get).html()?; + let url = format!("{}", &manga_id); + // aidoku::prelude::println!("get_chapter_list: {}", url); + let html = Request::new(url, HttpMethod::Get).html()?; parser::get_chapter_list(html) } #[get_page_list] fn get_page_list(manga_id: String, chapter_id: String) -> Result> { let url = format!("{}/{}", &manga_id, &chapter_id); + // aidoku::prelude::println!("get_page_list: {}", url); let html = Request::new(url.as_str(), HttpMethod::Get).html()?; parser::get_page_list(html) } @@ -66,8 +72,10 @@ fn modify_image_request(request: Request) { #[handle_url] fn handle_url(url: String) -> Result { - let parsed_manga_id = parser::parse_incoming_url_manga_id(url.clone()); - let parsed_chapter_id = parser::parse_incoming_url_chapter_id(url); + let parsed_manga_id = parser::parse_incoming_url_manga_id(&url); + let parsed_chapter_id = parser::parse_incoming_url_chapter_id(&url); + // aidoku::prelude::println!("handle_url manga id: {:?}", parsed_manga_id); + // aidoku::prelude::println!("handle_url chapter id: {:?}", parsed_chapter_id); if let Some(parsed_manga_id) = parsed_manga_id { Ok(DeepLink { diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index f6c3db8ba..c6f34b87a 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -14,17 +14,20 @@ pub fn parse_manga_list(html: Node, page: i32) -> (Vec, bool) { for page in html.select(".story-item").array() { let obj = page.as_node().expect("node array"); - let id = obj.select(".story-name a").attr("href").read(); + let url = obj.select(".story-name a").attr("href").read(); + let id = parse_incoming_url_manga_id(&url); let title = obj.select(".story-name a ").text().read(); let cover = obj.select(".story-list-img img").attr("src").read(); - if !id.is_empty() && !title.is_empty() && !cover.is_empty() { - result.push(Manga { - id, - cover, - title, - ..Default::default() - }); + if let Some(id) = id { + if !id.is_empty() && !title.is_empty() && !cover.is_empty() { + result.push(Manga { + id, + cover, + title, + ..Default::default() + }); + } } } @@ -66,13 +69,12 @@ pub fn parse_manga_details(html: Node, id: String) -> Result { let url = format!("{}", &id); - let author: String = html.select(".story_info_right li:nth-child(3) a") + let author: String = html + .select(".story_info_right li:nth-child(3) a") .array() - .map(|tag| { - String::from( - tag.as_node().expect("node array").text().read().trim(), - ) - }).collect::>().join(", "); + .map(|tag| String::from(tag.as_node().expect("node array").text().read().trim())) + .collect::>() + .join(", "); let categories: Vec = html .select(".story_info_right .a-h") @@ -127,7 +129,7 @@ pub fn get_chapter_list(html: Node) -> Result> { for chapter in html.select(".chapter_list ul li a").array() { let obj = chapter.as_node().expect("node array"); let url = obj.attr("href").read(); - let id = parse_incoming_url_chapter_id(url.clone()); + let id = parse_incoming_url_chapter_id(&url); if let Some(id) = id { let chapter = id @@ -282,18 +284,27 @@ pub fn get_filtered_url(filters: Vec, page: i32) -> String { format!("{BASE_URL}/manga-list/type-{sort}/ctg-{ctg}/state-{status}/page-{page}") } -pub fn parse_incoming_url_manga_id(url: String) -> Option { +pub fn parse_incoming_url_manga_id(url: &str) -> Option { // https://chap.mangairo.com/story-pn279847 // https://chap.mangairo.com/story-pn279847/chapter-52 let mut parts: Vec<&str> = url.split('/').collect(); + // Manga URL as ID because otherwise we cannot differentiate `w` and `chap` + // subdomains for manga if parts.len() >= 4 { parts.truncate(4); } - Some(parts.join("/")) + + // + // if parts.len() >= 3 { + // let manga_id = parts[3]; + // return Some(format!("{}", manga_id)); + // } + + // None } -pub fn parse_incoming_url_chapter_id(url: String) -> Option { +pub fn parse_incoming_url_chapter_id(url: &str) -> Option { // https://chap.mangairo.com/story-pn279847/chapter-52 let parts: Vec<&str> = url.split('/').collect(); if parts.len() >= 4 { From aded53e50c1a09f1c7f3871f0411aac74d78119c Mon Sep 17 00:00:00 2001 From: getBoolean Date: Sat, 23 Sep 2023 22:12:15 -0500 Subject: [PATCH 84/84] Add comment to commented out code --- src/rust/en.mangairo/src/parser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rust/en.mangairo/src/parser.rs b/src/rust/en.mangairo/src/parser.rs index c6f34b87a..dadb9b039 100644 --- a/src/rust/en.mangairo/src/parser.rs +++ b/src/rust/en.mangairo/src/parser.rs @@ -295,7 +295,7 @@ pub fn parse_incoming_url_manga_id(url: &str) -> Option { } Some(parts.join("/")) - // + // Excludes the base URL from the manga id. // if parts.len() >= 3 { // let manga_id = parts[3]; // return Some(format!("{}", manga_id));