feat: add ja.senmanga - #683
Conversation
| "info": { | ||
| "id": "ja.senmanga", | ||
| "name": "Sen Manga", | ||
| "version": 1, |
There was a problem hiding this comment.
this is once again a legacy source rewrite, so the version should be incremented. https://github.com/Skittyblock/aidoku-community-sources/blob/main/src/rust/ja.senmanga/res/source.json
you're using different keys, so you should optionally implement the MigrationHandler and set the breaking change version to 2.
| "version": 1, | |
| "version": 2, |
| /// Order values accepted by `/api/directory`, in the same order as the sort | ||
| /// options in res/filters.json. Any other value makes the api return a 500. | ||
| const SORT_VALUES: [&str; 4] = ["popular", "title", "updated", "rating"]; | ||
|
|
||
| /// Format of `chapterList[].datetime`, e.g. "2026-08-06T12:04:20Z". The offset | ||
| /// has to be read with `XXX` rather than a literal `'Z'`, or the timestamp gets | ||
| /// interpreted in the device timezone instead of utc. | ||
| const DATE_FORMAT: &str = "yyyy-MM-dd'T'HH:mm:ssXXX"; | ||
|
|
||
| /// Tags that mark an entry as explicit. | ||
| const NSFW_TAGS: [&str; 6] = ["Adult", "Smut", "Lolicon", "Shotacon", "Yaoi", "Yuri"]; | ||
|
|
||
| /// Tags that mark an entry as suggestive. | ||
| const SUGGESTIVE_TAGS: [&str; 2] = ["Ecchi", "Mature"]; |
There was a problem hiding this comment.
personally, I believe comments like these make code less readable. it is fairly obviously what all of these variables do, and good code should be self-documenting. I wouldn't mind if they were normal comments, but the unnecessary full sentence documentation comments are a hallmark of ai. that being said, it's still up to you whether you want them or not.
| fn parse_status(status: Option<&str>) -> MangaStatus { | ||
| match status { | ||
| Some("Ongoing") => MangaStatus::Ongoing, | ||
| Some("Completed") => MangaStatus::Completed, | ||
| Some("Cancelled") => MangaStatus::Cancelled, | ||
| Some("Hiatus") => MangaStatus::Hiatus, | ||
| _ => MangaStatus::Unknown, | ||
| } | ||
| } | ||
|
|
||
| fn content_rating(tags: &[String]) -> ContentRating { | ||
| if tags.iter().any(|tag| NSFW_TAGS.contains(&tag.as_str())) { | ||
| ContentRating::NSFW | ||
| } else if tags | ||
| .iter() | ||
| .any(|tag| SUGGESTIVE_TAGS.contains(&tag.as_str())) | ||
| { | ||
| ContentRating::Suggestive | ||
| } else { | ||
| ContentRating::Safe | ||
| } | ||
| } |
There was a problem hiding this comment.
I don't think making these separate functions provides much benefit. you can just inline it directly in the details updating.
Adds a source for raw.senmanga.com.
The site is now a React SPA — the server only returns
<div id="root"></div>, so there is nothing to parse out of the HTML. The implementation targets the JSON API the site's own bundle calls, athttps://raw.senmanga.com/api.Endpoints
/directory?page=&query=&order=&status=&type=— backs both browsing and search/manga/{slug}— details and chapter list/read/{slug}/{chapter}— page listDecisions worth explaining
/directory?query=, not/search. The site's own search page calls/api/search?q=, but that endpoint ignoresqand returns the whole directory./directory?query=filters correctly, and since it matches againstalt_nameas well, japanese queries reach the romanized entries (ワンピースfinds One Piece).has_next_pagecomparescurrentPageagainsttotalPages.totalPagesis the pager's display window rather than the real total — it reports 5 on page 1 and 202 on page 200 — so it cannot be used as an absolute bound. Both fields come back null when the results fit on a single page, which is treated as having no next page.status, while the directory entry the manga came from carries one, so the existing value is kept rather than overwritten withUnknown. There is a test covering this.genre=does not change the response and/api/genresis 404, so the filter is not exposed rather than shipped broken.ImageRequestProvider. Pages are served fromcdn.kumacdn.cluband hotlink fine without aReferer.datetimefield, parsed withyyyy-MM-dd'T'HH:mm:ssXXX.Chapter.languageis left unset, since the source is japanese-only and setting it would put every chapter behind the app's language filter.Verification
cargo fmtandcargo clippy --releaseare cleancargo test --release— 6 tests: browse, search, details, status, page list, deep linksaidoku packageandaidoku verifypassOne caveat for anyone running the tests: the api returns a sporadic 500 on otherwise valid requests, unrelated to load, so a run occasionally fails on a different test each time. Re-running clears it.