import chalk from "chalk" ; import { error , getDataFromURL } from "./index" ; import { ScraperError , ScraperResponse } from "../types" ; import { Scraper , SearchOptions } from "./types" ; import { getProviderId , isProviderId } from "../routers/manga-page" ; import updateManga from "../util/updateManga" ; export class nhentaiClass extends Scraper { constructor ( ) { super ( ) ; this . provider = "nhentainet" ; this . searchDisplay = "nhentai.net" ; this . canSearch = true ; this . nsfw = true ; } public async search ( query , options ? ) { const { resultCount } = { resultCount : 40 , ... options } ; let pageUrl ; if ( query === "" ) { pageUrl = "https://nhentai.net/api/galleries/search?query=%20" ; } else { pageUrl = `https://nhentai.net/api/galleries/search?query=${ encodeURIComponent ( query ) }` ; } const searchData = await getDataFromURL ( pageUrl ) ; const ids = searchData . result . map ( ( result ) => result . id ) . slice ( 0 , resultCount ) ; const searchResultData = await Promise . all ( ids . map ( ( id ) => updateManga ( "nhentainet" , id ) ) ) ; return searchResultData ; } /**
     * The scrape function
     */ public async scrape ( slug , chapterId ) { const maxTimeout = new Promise ( ( resolve ) => { setTimeout ( ( ) => { resolve ( error ( 0 , "This request took too long" ) ) ; } , 25e3 ) ; } ) ; const scraping = this . doScrape ( slug , chapterId ) ; const raceResult = await Promise . race ( [ maxTimeout , scraping ] ) ; if ( raceResult . success === false && raceResult . err === "This request took too long" ) { console . error ( chalk . red ( "[NHENTAI]" ) + ` A request for '${ slug }' at '${ chapterId }' took too long and has timed out` ) ; } return raceResult ; } getImageType ( type ) { const types = { j : "jpg" , p : "png" } ; return types [ type ] ?? "jpg" ; } private async doScrape ( slug , chapterId ) { try { const data = await getDataFromURL ( `https://nhentai.net/api/gallery/${ slug }` ) ; const language = data . tags . find ( ( tag ) => tag . type === "language" ) ?. name || "unknown" ; const textTitle = data . title . pretty ?? data . title . english ?? Object . values ( data . title ) [ 0 ] ; const title = `${ language . slice ( 0 , 1 ) . toUpperCase ( ) + language . slice ( 1 , 2 ) }: ${ textTitle }` ; const alternateTitles = Object . values ( data . title ) ; const posterUrlMain = `https://t.nhentai.net/galleries/${ data . media_id }/cover.${ this . getImageType ( data . images . cover . t ) }` ; const posterUrl = `/proxy-image?url=${ encodeURIComponent ( posterUrlMain ) }` ; const genres = data . tags . map ( ( tag ) => tag . name . slice ( 0 , 1 ) . toUpperCase ( ) + tag . name . slice ( 1 ) ) ; const date = new Date ( data . upload_date * 1e3 ) ; const chapterImages = data . images . pages . map ( ( pageInfo , i ) => { return `https://i.nhentai.net/galleries/${ data . media_id }/${ i + 1 }.${ this . getImageType ( pageInfo . t ) }` ; } ) ; const providerId = getProviderId ( this . provider ) ; return { constant : { title , slug , posterUrl , alternateTitles , descriptionParagraphs : [ "nhentai does not provide descriptions." ] , genres , nsfw : true } , data : { chapters : [ { season : 1 , chapter : 1 , label : "Chapter 1" , date , hrefString : "read" } , ] , chapterImages , status : "ended" } , success : true , provider : isProviderId ( providerId ) ? providerId : null } ; } catch ( e ) { return { success : false , status : 0 , err : e } ; } } } const nhentai = new nhentaiClass ( ) ; export default nhentai ;