diff --git a/src/AsuraScans/AsuraScans.ts b/src/AsuraScans/AsuraScans.ts index 139c9f3..d04a6bc 100644 --- a/src/AsuraScans/AsuraScans.ts +++ b/src/AsuraScans/AsuraScans.ts @@ -42,13 +42,13 @@ import { SourceStateManager } from '@paperback/types/lib' -const simpleUrl = require('simple-url') +import simpleUrl from 'simple-url' const ASURASCANS_DOMAIN = 'https://asuracomic.net' const ASURASCANS_API_DOMAIN = 'https://gg.asuracomic.net' export const AsuraScansInfo: SourceInfo = { - version: '4.1.7', + version: '4.1.8', name: 'AsuraScans', description: 'Extension that pulls manga from AsuraScans', author: 'Seyden', @@ -113,7 +113,7 @@ export class AsuraScans implements ChapterProviding, HomePageSectionsProviding, request.headers = { ...(request.headers ?? {}), ...{ 'user-agent': await this.requestManager.getDefaultUserAgent(), - referer: `${url}/`, + referer: `${url}/` } } @@ -123,14 +123,14 @@ export class AsuraScans implements ChapterProviding, HomePageSectionsProviding, request.url = simpleUrl.create(path) } - if (path.host.includes(`localhost`)) { + if (path.host.includes('localhost')) { const url: string = await this.getBaseUrl() path.host = simpleUrl.parse(url, true).host request.url = simpleUrl.create(path) } if (isImgLink(request.url)) { - let overrideUrl: string = await this.stateManager.retrieve('Domain') + const overrideUrl: string = await this.stateManager.retrieve('Domain') if (overrideUrl && overrideUrl != this.baseUrl) { const basePath: any = simpleUrl.parse(this.baseUrl, true) const overridePath: any = simpleUrl.parse(overrideUrl, true) @@ -166,7 +166,7 @@ export class AsuraScans implements ChapterProviding, HomePageSectionsProviding, /** * The language code which this source supports. */ - language: string = '🇬🇧' + language = '🇬🇧' /** * The pathname between the domain and the manga. @@ -214,14 +214,14 @@ export class AsuraScans implements ChapterProviding, HomePageSectionsProviding, selectorFunc: ($: CheerioStatic) => $('div.w-full', $('h3:contains(Latest Updates)')?.parent()?.next()), titleSelectorFunc: ($: CheerioStatic, element: CheerioElement) => $('span.font-medium', element).text().trim(), subtitleSelectorFunc: ($: CheerioStatic, element: CheerioElement) => { - let obj = $('div.text-sm', element).first() - let hiddenObj = $('div.hidden', obj) + const obj = $('div.text-sm', element).first() + const hiddenObj = $('div.hidden', obj) if (hiddenObj.length != 0) return hiddenObj.text().trim() return obj.text().trim() }, getViewMoreItemsFunc: (page: string) => `page/${page}`, - sortIndex: 20, + sortIndex: 20 }, 'top_alltime': { ...DefaultHomeSectionData, @@ -250,16 +250,16 @@ export class AsuraScans implements ChapterProviding, HomePageSectionsProviding, } // Ugly workaround to fasten up migrations and updates, paperback doesnt support any other way for not double requesting - mangaDataRequests: { [Key: string]: { expires: number, data: Promise } } = { } + mangaDataRequests: { [Key: string]: { expires: number, data: Promise } } = {} async getMangaRequest(mangaId: string): Promise { - let request = this.mangaDataRequests[mangaId] + const request = this.mangaDataRequests[mangaId] if (request && request.expires > Date.now()) { return request.data } for (const key in this.mangaDataRequests) { - let tempRequest = this.mangaDataRequests[key] + const tempRequest = this.mangaDataRequests[key] if (tempRequest!.expires < Date.now()) { delete this.mangaDataRequests[key] } @@ -344,8 +344,13 @@ export class AsuraScans implements ChapterProviding, HomePageSectionsProviding, } async getSearchTags(): Promise { - const data = await this.loadRequestData(`${ASURASCANS_API_DOMAIN}/api/series/filters`) - return this.parser.parseTags(data) + try { + const data = await this.loadRequestData(`${ASURASCANS_API_DOMAIN}/api/series/filters`) + return this.parser.parseTags(JSON.parse(data) + ) + } catch (error) { + throw new Error(error as any) + } } async getSearchResults(query: SearchRequest, metadata: any): Promise { @@ -355,8 +360,7 @@ export class AsuraScans implements ChapterProviding, HomePageSectionsProviding, } let manga: PartialSourceManga[] = [] - while (manga.length == 0) - { + while (manga.length == 0) { result = await this.search(metadata, query) metadata = result.metadata manga = result.manga @@ -385,7 +389,7 @@ export class AsuraScans implements ChapterProviding, HomePageSectionsProviding, const manga: PartialSourceManga[] = [] for (const result of results) { if (chapterTag) { - const chapterCount = parseInt(chapterTag.id.replace(`chapters:`, '')) + const chapterCount = parseInt(chapterTag.id.replace('chapters:', '')) const chapterCountRegex = result.subtitle?.match(/(\d+)/) if (!chapterCountRegex || chapterCountRegex?.[1] && parseInt(chapterCountRegex[1]) < chapterCount) continue @@ -400,8 +404,8 @@ export class AsuraScans implements ChapterProviding, HomePageSectionsProviding, } metadata = !this.parser.isLastPage($, query?.title ? 'search_request' : 'view_more') - ? { page: page + 1 } - : undefined + ? { page: page + 1 } + : undefined return { metadata, manga @@ -467,7 +471,7 @@ export class AsuraScans implements ChapterProviding, HomePageSectionsProviding, } async getViewMoreItems(homepageSectionId: string, metadata: any): Promise { - throw new Error(`Not implemented yet!`) + throw new Error('Not implemented yet!') /*const page: number = metadata?.page ?? 1 @@ -491,7 +495,7 @@ export class AsuraScans implements ChapterProviding, HomePageSectionsProviding, })*/ } - async loadRequestData(url: string, method: string = 'GET'): Promise { + async loadRequestData(url: string, method = 'GET'): Promise { const request = App.createRequest({ url, method @@ -502,7 +506,7 @@ export class AsuraScans implements ChapterProviding, HomePageSectionsProviding, return response.data as string } - async loadCheerioData(url: string, method: string = 'GET'): Promise { + async loadCheerioData(url: string, method = 'GET'): Promise { return this.cheerio.load(await this.loadRequestData(url, method), { _useHtmlParser2: true }) } diff --git a/src/AsuraScans/AsuraScansInterfaces.ts b/src/AsuraScans/AsuraScansInterfaces.ts index fbd12ff..cb694f2 100644 --- a/src/AsuraScans/AsuraScansInterfaces.ts +++ b/src/AsuraScans/AsuraScansInterfaces.ts @@ -20,4 +20,11 @@ export interface StatusTypes { DROPPED: string; SEASONEND: string; COMINGSOON: string; +} + +export interface Filters { + types: any[]; + genres: any[]; + statuses: any[]; + order: any[]; } \ No newline at end of file diff --git a/src/AsuraScans/AsuraScansParser.ts b/src/AsuraScans/AsuraScansParser.ts index ff36aea..c249fce 100644 --- a/src/AsuraScans/AsuraScansParser.ts +++ b/src/AsuraScans/AsuraScansParser.ts @@ -3,6 +3,7 @@ import { ChapterDetails, PartialSourceManga, SourceManga, + Tag, TagSection } from '@paperback/types' @@ -12,10 +13,14 @@ import { } from './AsuraScansHelper' import entities = require('entities') +import { + FilterItem, + Filters +} from './AsuraScansInterfaces' export class AsuraScansParser { async parseMangaDetails(data: string, mangaId: string, source: any): Promise { - const obj = extractMangaData(data.replace(/\\"/g, '"').replace(/\\\\"/g, '\\"'), "comic") ?? '' + const obj = extractMangaData(data.replace(/\\"/g, '"').replace(/\\\\"/g, '\\"'), 'comic') ?? '' if (obj == '') { throw new Error(`Failed to parse comic object for manga ${mangaId}`) // If null, throw error, else parse data to json. } @@ -35,7 +40,7 @@ export class AsuraScansParser { const rating = comicObj.comic.rating const slug = comicObj.comic.slug?.trim() - if (slug) { + if (slug) { await source.setMangaSlug(mangaId, `series/${slug}`) } @@ -69,7 +74,7 @@ export class AsuraScansParser { App.createTagSection({ id: '0', label: 'genres', - tags: comicObj.comic.genres.map((tag: any) => App.createTag( { id: `genres:${tag.id.toString()}`, label: tag.name } )) + tags: comicObj.comic.genres.map((tag: any) => App.createTag({ id: `genres:${tag.id.toString()}`, label: tag.name })) }) ] @@ -91,14 +96,14 @@ export class AsuraScansParser { async parseChapterList(data: string, mangaId: string, source: any): Promise { const tempData = data.replace(/\\"/g, '"').replace(/\\\\"/g, '\\"') - let obj = extractMangaData(tempData, "comic") ?? '' + let obj = extractMangaData(tempData, 'comic') ?? '' if (obj == '') { throw new Error(`Failed to parse comic object for manga ${mangaId}`) // If null, throw error, else parse data to json. } const comicObj = JSON.parse(obj) - obj = extractMangaData(tempData, "chapters") ?? '' + obj = extractMangaData(tempData, 'chapters') ?? '' if (obj == '') { throw new Error(`Failed to parse chapters object for manga ${mangaId}`) // If null, throw error, else parse data to json. } @@ -106,8 +111,8 @@ export class AsuraScansParser { const chaptersObj = JSON.parse(obj) const slug = comicObj.comic.slug?.trim() - let mangaUrl: string = '' - if (slug) { + let mangaUrl = '' + if (slug) { mangaUrl = `series/${slug}` await source.setMangaSlug(mangaId, mangaUrl) } @@ -118,8 +123,7 @@ export class AsuraScansParser { const chapters: Chapter[] = [] let sortingIndex = 0 - for (const chapter of chaptersObj.chapters.reverse()) - { + for (const chapter of chaptersObj.chapters.reverse()) { const id = chapter.id.toString() if (!id || typeof id === 'undefined') { throw new Error(`Could not parse out ID when getting chapters for postId:${mangaId}`) @@ -167,36 +171,65 @@ export class AsuraScansParser { }) } - parseTags(data: string): TagSection[] { - const tagSections: any[] = [ - { id: '0', label: 'chapters', tags: [ - App.createTag({ id: 'chapters:10', label: '+10' }), - App.createTag({ id: 'chapters:20', label: '+20' }), - App.createTag({ id: 'chapters:30', label: '+30' }), - App.createTag({ id: 'chapters:40', label: '+40' }), - App.createTag({ id: 'chapters:50', label: '+50' }), - App.createTag({ id: 'chapters:60', label: '+60' }), - App.createTag({ id: 'chapters:70', label: '+70' }), - App.createTag({ id: 'chapters:80', label: '+80' }), - App.createTag({ id: 'chapters:90', label: '+90' }), - App.createTag({ id: 'chapters:100', label: '+100' }), - App.createTag({ id: 'chapters:150', label: '+150' }), - App.createTag({ id: 'chapters:200', label: '+200' }), - App.createTag({ id: 'chapters:250', label: '+250' }), - ]}, - { id: '1', label: 'genres', tags: [] }, - { id: '2', label: 'status', tags: [] }, - { id: '3', label: 'type', tags: [] }, - { id: '4', label: 'order', tags: [] } + parseTags(filters: Filters): TagSection[] { + + // Predefined chapters tags + const predefinedChaptersTags: Tag[] = [ + { id: 'chapters:10', label: '+10' }, + { id: 'chapters:20', label: '+20' }, + { id: 'chapters:30', label: '+30' }, + { id: 'chapters:40', label: '+40' }, + { id: 'chapters:50', label: '+50' }, + { id: 'chapters:60', label: '+60' }, + { id: 'chapters:70', label: '+70' }, + { id: 'chapters:80', label: '+80' }, + { id: 'chapters:90', label: '+90' }, + { id: 'chapters:100', label: '+100' }, + { id: 'chapters:150', label: '+150' }, + { id: 'chapters:200', label: '+200' }, + { id: 'chapters:250', label: '+250' } ] - const filters = JSON.parse(data) - filters.types.forEach((type: any) => { tagSections[3].tags.push(App.createTag({ id: `type:${type.id.toString()}`, label: type.name })) }) - filters.genres.forEach((type: any) => { tagSections[1].tags.push(App.createTag({ id: `genres:${type.id.toString()}`, label: type.name })) }) - filters.statuses.forEach((type: any) => { tagSections[2].tags.push(App.createTag({ id:`status:${type.id.toString()}`, label: type.name })) }) - filters.order.forEach((type: any) => { tagSections[4].tags.push(App.createTag({ id: `order:${type}`, label: type })) }) + const createTags = (filterItems: FilterItem[], prefix: string): Tag[] => { + return filterItems.map(item => ({ + id: `${prefix}:${item.id ?? item.value}`, // Use `id` or `value` for `order` items + label: item.name + })) + } - return tagSections.map((x) => App.createTagSection(x)) + const tagSections: TagSection[] = [ + // Tag section for genres + App.createTagSection({ + id: '0', + label: 'genres', + tags: createTags(filters.genres, 'genres').map(x => App.createTag(x)) + }), + // Tag section for status + App.createTagSection({ + id: '1', + label: 'status', + tags: createTags(filters.statuses, 'status').map(x => App.createTag(x)) + }), + // Tag section for types + App.createTagSection({ + id: '2', + label: 'type', + tags: createTags(filters.types, 'type').map(x => App.createTag(x)) + }), + // Tag section for order + App.createTagSection({ + id: '3', + label: 'order', + tags: createTags(filters.order.map(order => ({ id: order.value, name: order.name })), 'order').map(x => App.createTag(x)) + }), + // Predefined chapters tag section + App.createTagSection({ + id: '4', + label: 'chapters', + tags: predefinedChaptersTags.map(x => App.createTag(x)) + }) + ] + return tagSections } async parseSearchResults($: CheerioSelector, source: any): Promise { @@ -204,7 +237,7 @@ export class AsuraScansParser { const mangas = $('a', $('h3:contains(Series list)')?.parent()?.next()?.next()) if (!mangas.length) { - console.log(`Unable to parse search results!`) + console.log('Unable to parse search results!') return results } @@ -245,10 +278,10 @@ export class AsuraScansParser { const path: string = ($('a', manga).attr('href') ?? '').replace(/\/$/, '').split('/').slice(-2).shift() ?? '' const postId = $('a', manga).attr('rel') const mangaId: string = source.usePostIds - ? (isNaN(Number(postId)) - ? await source.slugToPostId(slug, path) - : postId) - : slug + ? (isNaN(Number(postId)) + ? await source.slugToPostId(slug, path) + : postId) + : slug if (!mangaId || !title) { console.log(`Failed to parse homepage sections for ${source.baseUrl}`)