import fetch from "node-fetch-extra" ; import chalk from "chalk" ; import BBCodeParser from "bbcode-parser" ; import { XmlEntities } from "html-entities" ; import * as scrapers from "../scrapers" ; import db from "../db" ; import { ScraperError , ScraperResponse } from "../types" ; import getMangaProgress from "./getMangaProgress" ; import config from "../config.json" ; import { Provider , Scraper } from "../scrapers/types" ; import { getProviderId , getProviderName } from "../routers/manga-page" ; import { disallowedGenres } from "../config.json" ; import cache from "../util/cache" ; const Entities = new XmlEntities ( ) ; const parser = new BBCodeParser ( BBCodeParser . defaultTags ( ) ) ; const bannerCache = { } ; const nsfwError = { success : false , status : 0 , err : "This is NSFW content" } ; export default async function updateManga ( provider , slug , ignoreExisting = false , chapterId = - 1 ) { const existing = cache ?. [ getProviderId ( provider ) ] ?. [ slug ] ; if ( existing && existing . savedAt > Date . now ( ) - config . cache . duration && ! ignoreExisting && chapterId === - 1 ) { const d = await addInfo ( existing ) ; if ( d . success && d . constant . nsfw && db . get ( "settings.show-nsfw" ) === "no" ) return nsfwError ; return d ; } const scraperName = getProviderName ( provider ) || provider ; const scraper = scrapers [ scraperName ] ; if ( ! scraper ) { console . error ( "No scraper: " + provider ) ; return { err : "No such scraper exists" , status : 0 , success : false } ; } const data = await scraper . scrape ( slug , chapterId ) ; if ( data . success ) { const nData = JSON . parse ( JSON . stringify ( data ) ) ; nData . savedAt = Date . now ( ) ; nData . data . chapters . forEach ( ( d ) => { delete d . progress ; delete d . realProgress ; } ) ; delete nData . data . chapterImages ; delete nData . realProgress ; delete nData . progress ; const p = getProviderId ( provider ) ; if ( ! cache [ p ] ) cache [ p ] = [ ] ; cache [ getProviderId ( p ) ] [ slug ] = nData ; } const d = await addInfo ( data ) ; if ( ! d ?. success ) { if ( typeof d ?. success === "undefined" ) { return { success : false , status : - 1 , err : "Unkown error" } ; } return d ; } if ( d . constant . nsfw && db . get ( "settings.show-nsfw" ) === "no" ) return nsfwError ; return d ; } async function addInfo ( data ) { if ( data . success ) { const chapterPromises = data . data . chapters . map ( async ( ch ) => { ch . progress = await getMangaProgress ( data . provider , data . constant . slug , ch . hrefString ) ; return ch ; } ) ; await Promise . all ( chapterPromises ) ; if ( db . get ( "settings.show-unread-chapter-count" ) === "yes" ) { const readChapters = data . data . chapters . filter ( ( v ) => v . progress ?. percentage >= 90 ) ; const unreadChapterCount = data . data . chapters . length - readChapters . length ; data . unreadChapterCount = unreadChapterCount ; } else { data . unreadChapterCount = null ; } data . constant . descriptionParagraphs = data . constant . descriptionParagraphs . map ( ( s ) => Entities . decode ( parser . parseString ( s ) ) . replace ( /&rsquo;/g , "'" ) . replace ( /\[\/?spoiler\]/g , "" ) ) ; const seasonSet = new Set ( data . data . chapters . map ( ( c ) => c . season . toString ( ) ) ) ; const chapters = Array . from ( seasonSet ) ; data . data . hasSeasons = chapters . length > 1 ; for ( const genre of data . constant . genres ) { if ( disallowedGenres . includes ( genre . toLowerCase ( ) ) ) { data . constant . nsfw = true ; } } if ( data . constant . descriptionParagraphs . join ( "\n" ) . includes ( "sex" ) ) { data . constant . nsfw = true ; } try { if ( data . provider === "mangadex5" || data . provider === "mangasee" || data . provider === "manganelo" || data . provider === "mangahere" ) { const existingBanner = bannerCache [ data . constant . title ] ; if ( existingBanner ) { data . constant . banner = existingBanner ; return data ; } const query = `
				query media($search:String, $type:MediaType) { 
					Media(search:$search, type:$type){
						id
						bannerImage
					}
				}
				` ; const aniListData = await fetch ( "https://graphql.anilist.co" , { headers : { "content-type" : "application/json" } , body : JSON . stringify ( { query , variables : { search : data . constant . title , type : "MANGA" } } ) , method : "POST" } ) . then ( ( d ) => d . json ( ) ) ; const banner = aniListData ?. data ?. Media ?. bannerImage ?? null ; data . constant . banner = banner ; if ( banner ) bannerCache [ data . constant . title ] = banner ; } else { data . constant . banner = data . constant . posterUrl ; } if ( ! db . get ( `seriesMetaData.${ data . provider }.${ data . constant . slug }` ) ) { db . set ( `seriesNames.${ data . provider }.${ data . constant . slug }` , data . constant ) ; } return data ; } catch ( err ) { data . constant . banner = data . constant . posterUrl ; console . error ( chalk . red ( "[ANILIST]" ) + ` Unable to fetch banner for ${ data . constant . title }:` , err ) ; return data ; } } }