feat(weebs): add anime/manga info, whatanime, waifu endpoints
Deploy Scraper / build-and-deploy (push) Canceled after 0s

Ported from Shirokami-API scraper/weebs/*.js:
- /weebs/anime-info (jikan.moe anime search)
- /weebs/manga-info (jikan.moe manga search)
- /weebs/whatanime (trace.moe reverse image search)
- /weebs/sfw-waifu + /weebs/nsfw-waifu (waifu.pics)

Tested: anime-info (Naruto real data) + whatanime (real match). manga-info
+ waifu currently report upstream 504/DNS-down (jikan manga endpoint &
waifu.pics unreachable from VPS right now) - proxy logic verified via the
working anime/whatanime siblings.
This commit is contained in:
asepharyana
2026-09-01 19:35:32 +07:00
parent 242e3498fe
commit 091a68bd55
7 changed files with 316 additions and 0 deletions
+1
View File
@@ -6,3 +6,4 @@ pub mod komik;
pub mod misc;
pub mod proxy;
pub mod stalk;
pub mod weebs;
+65
View File
@@ -0,0 +1,65 @@
//! Axum handlers for weebs endpoints.
//!
//! Ported from Shirokami-API `scraper/weebs/*.js`.
use axum::extract::Query;
use axum::http::StatusCode;
use axum::Json;
use serde::Deserialize;
use serde_json::Value;
use crate::application::weebs as use_cases;
use crate::presentation::error::AppError;
#[derive(Deserialize)]
pub struct WeebsParams {
pub query: Option<String>,
pub q: Option<String>,
pub url: Option<String>,
pub tag: Option<String>,
}
fn require<'a>(v: Option<&'a String>, name: &str) -> Result<String, AppError> {
v.cloned()
.ok_or_else(|| AppError::BadRequest(format!("Missing '{}' parameter", name)))
}
/// GET /weebs/anime-info?query=naruto
pub async fn anime_info_handler(
Query(p): Query<WeebsParams>,
) -> Result<(StatusCode, Json<Value>), AppError> {
let query = require(p.query.as_ref().or(p.q.as_ref()), "query")?;
Ok((StatusCode::OK, Json(use_cases::anime_info(&query).await?)))
}
/// GET /weebs/manga-info?query=one%20piece
pub async fn manga_info_handler(
Query(p): Query<WeebsParams>,
) -> Result<(StatusCode, Json<Value>), AppError> {
let query = require(p.query.as_ref().or(p.q.as_ref()), "query")?;
Ok((StatusCode::OK, Json(use_cases::manga_info(&query).await?)))
}
/// GET /weebs/whatanime?url=<image-url>
pub async fn whatanime_handler(
Query(p): Query<WeebsParams>,
) -> Result<(StatusCode, Json<Value>), AppError> {
let url = require(p.url.as_ref(), "url")?;
Ok((StatusCode::OK, Json(use_cases::whatanime(&url).await?)))
}
/// GET /weebs/sfw-waifu?tag=waifu
pub async fn sfw_waifu_handler(
Query(p): Query<WeebsParams>,
) -> Result<(StatusCode, Json<Value>), AppError> {
let tag = require(p.tag.as_ref(), "tag")?;
Ok((StatusCode::OK, Json(use_cases::sfw_waifu(&tag).await?)))
}
/// GET /weebs/nsfw-waifu?tag=waifu
pub async fn nsfw_waifu_handler(
Query(p): Query<WeebsParams>,
) -> Result<(StatusCode, Json<Value>), AppError> {
let tag = require(p.tag.as_ref(), "tag")?;
Ok((StatusCode::OK, Json(use_cases::nsfw_waifu(&tag).await?)))
}
+21
View File
@@ -282,6 +282,27 @@ pub fn build_router(app_state: Arc<AppState>) -> anyhow::Result<Router> {
"/stalk/youtube",
axum::routing::get(crate::presentation::handler::stalk::youtube_handler),
)
// Weebs routes
.route(
"/weebs/anime-info",
axum::routing::get(crate::presentation::handler::weebs::anime_info_handler),
)
.route(
"/weebs/manga-info",
axum::routing::get(crate::presentation::handler::weebs::manga_info_handler),
)
.route(
"/weebs/whatanime",
axum::routing::get(crate::presentation::handler::weebs::whatanime_handler),
)
.route(
"/weebs/sfw-waifu",
axum::routing::get(crate::presentation::handler::weebs::sfw_waifu_handler),
)
.route(
"/weebs/nsfw-waifu",
axum::routing::get(crate::presentation::handler::weebs::nsfw_waifu_handler),
)
// Health
.route(
"/health",