import chalk from "chalk" ; import fetch from "node-fetch-extra" ; import { JSDOM } from "jsdom" ; import { error } 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 = "nhentai" ; this . canSearch = false ; this . nsfw = true ; } public async search ( query , options ? ) { const { resultCount } = { resultCount : 40 , ... options } ; let pageUrl ; if ( query === "" ) { pageUrl = "https://nhentai.to/" ; } else { pageUrl = `https://nhentai.to/search?q=${ encodeURIComponent ( query ) }` ; } const pageReq = await fetch ( pageUrl ) ; const pageHtml = await pageReq . text ( ) ; const dom = new JSDOM ( pageHtml ) ; const document = dom . window . document ; const anchors = [ ... document . querySelectorAll ( ".index-container .gallery a" ) , ] ; const ids = anchors . map ( ( anchor ) => anchor . href . match ( /(\d+)/ ) [ 1 ] ) ; const searchResultData = await Promise . all ( ids . map ( ( id ) => updateManga ( "nhentai" , 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 ; } private async doScrape ( slug , chapterId ) { try { const pageReq = await fetch ( `https://nhentai.to/g/${ slug }` ) ; const pageHtml = await pageReq . text ( ) ; const dom = new JSDOM ( pageHtml ) ; const document = dom . window . document ; const title = document . querySelector ( "#bigcontainer h1" ) . textContent ; const posterUrl = document . querySelector ( "img[data-src]" ) . getAttribute ( "data-src" ) ; const genres = [ ... document . querySelectorAll ( ".tag" ) ] . map ( ( v ) => v . textContent ) . map ( ( str ) => str . slice ( 0 , 1 ) . toUpperCase ( ) + str . slice ( 1 ) ) ; const alternateTitles = [ ... document . querySelectorAll ( "h2" ) ] . map ( ( node ) => node . textContent ) ; const chapterImages = [ ... document . querySelectorAll ( "#thumbnail-container img" ) , ] . map ( ( v ) => v . getAttribute ( "data-src" ) ) . filter ( Boolean ) . map ( ( url ) => url . replace ( /t\./g , "." ) ) ; const date = new Date ( document . querySelector ( "time" ) . getAttribute ( "datetime" ) ) ; 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 ;