import chalk from "chalk" ; import fetch from "node-fetch-extra" ; import { JSDOM } from "jsdom" ; import { error } from "./index" ; import { Chapter , ScraperError , ScraperResponse } from "../types" ; import { Scraper , SearchOptions } from "./types" ; import { getProviderId , isProviderId } from "../routers/manga-page" ; import updateManga from "../util/updateManga" ; export class comicextraClass extends Scraper { constructor ( ) { super ( ) ; this . provider = "ComicExtra" ; this . canSearch = true ; this . nsfw = false ; } public async search ( query , options ? ) { let resultCount = 10 ; let pageUrl ; if ( query === "" ) { pageUrl = "https://www.comicextra.com/popular-comic" ; resultCount = 5 ; } else { pageUrl = `https://www.comicextra.com/comic-search?key=${ 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 ( ".cartoon-box" ) ] . map ( ( box ) => box . querySelector ( "a" ) ) ; const ids = anchors . map ( ( anchor ) => ( anchor ?. href || "" ) . split ( "/comic/" ) . pop ( ) ) . filter ( Boolean ) . slice ( 0 , resultCount ) ; const searchResultData = await Promise . all ( ids . map ( ( id ) => updateManga ( "comicextra" , 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 ( "[COMICEXTRA]" ) + ` 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://www.comicextra.com/comic/${ slug }` ) ; const pageHtml = await pageReq . text ( ) ; const dom = new JSDOM ( pageHtml ) ; const document = dom . window . document ; const title = document . querySelector ( ".title-1" ) . textContent ; let posterUrl = document . querySelector ( ".movie-l-img img" ) . src ; if ( posterUrl . startsWith ( "/" ) ) posterUrl = "https://www.comicextra.com" + posterUrl ; const genres = [ ... document . querySelectorAll ( ".movie-dd" ) [ 4 ] . querySelectorAll ( "a" ) , ] . map ( ( v ) => v . textContent ) ; const alternateTitles = [ ] ; const status = ( document . querySelector ( ".movie-dd.status" ) ?. textContent || "" ) . toLowerCase ( ) ; const nav = document . querySelector ( ".general-nav" ) ; let chapterLinks = nav ? Array . from ( new Set ( [ ... nav . querySelectorAll ( "a" ) ] . map ( ( a ) => a . href ) ) ) : [ ] ; const seriesUrlNodes = { } ; const getSeriesNode = async ( url ) => { if ( seriesUrlNodes [ url ] ) return seriesUrlNodes [ url ] ; const pageHtml = await ( await fetch ( url ) ) . text ( ) ; const dom = new JSDOM ( pageHtml ) ; const document = dom . window . document ; seriesUrlNodes [ url ] = document ; for ( let a of document . querySelectorAll ( ".general-nav a" ) ) { await getSeriesNode ( a . href ) ; } return document ; } ; for ( let url of chapterLinks ) { await getSeriesNode ( url ) ; } let allChapterDocuments = await Promise . all ( [ document , ... Object . values ( seriesUrlNodes ) , ] ) ; const chapterNodes = [ ... allChapterDocuments . map ( ( d ) => d . querySelectorAll ( ".episode-list tr" ) ) , ] . map ( ( nodelist ) => [ ... nodelist ] ) . flat ( ) ; const knownChapters = [ ] ; const chaptersWithDupes = chapterNodes . reverse ( ) . map ( ( row , i ) : Chapter => { const label = row . querySelector ( "a" ) . textContent ; const slug = row . querySelector ( "a" ) . href . split ( "/" ) . pop ( ) ; const date = new Date ( row . querySelectorAll ( "td" ) [ 1 ] . textContent ) ; const chapterOptA = Number ( slug . split ( "#" ) . pop ( ) ) ; const chapterOptB = Number ( slug . split ( "-" ) . pop ( ) ) ; let chapter = 0 ; if ( ! isNaN ( chapterOptA ) ) { chapter = chapterOptA ; } else if ( ! isNaN ( chapterOptB ) ) { chapter = chapterOptB ; } else { chapter = - chapterNodes . length + i ; } return { label , hrefString : slug , season : 1 , chapter , date , combined : chapter } ; } ) . sort ( ( a , b ) => a . combined - b . combined ) ; const chapters = chaptersWithDupes . filter ( ( c ) => { if ( knownChapters . includes ( c . combined ) ) { return false ; } else { knownChapters . push ( c . combined ) ; return true ; } } ) ; let chapterImages = [ ] ; if ( chapterId != "-1" ) { const url = `https://www.comicextra.com/${ slug }/${ chapterId }/full` ; const chapterPageReq = await fetch ( url ) ; const chapterPageHtml = await chapterPageReq . text ( ) ; const dom = new JSDOM ( chapterPageHtml ) ; const chapterDocument = dom . window . document ; const images = [ ... chapterDocument . querySelectorAll ( ".chapter_img" ) ] ; chapterImages = images . map ( ( v ) => v . src ) ; } const descriptionParagraphs = document . querySelector ( "#film-content" ) . textContent . split ( /\n|<br>/g ) ; const providerId = getProviderId ( this . provider ) ; return { constant : { title , slug , posterUrl , alternateTitles , descriptionParagraphs , genres , nsfw : false } , data : { chapters , chapterImages , status } , success : true , provider : isProviderId ( providerId ) ? providerId : null } ; } catch ( e ) { console . log ( e ) ; return { success : false , status : 0 , err : e } ; } } } const comicextra = new comicextraClass ( ) ; export default comicextra ;