Skip to content

Commit

Permalink
FlameComics : website changes (#898)
Browse files Browse the repository at this point in the history
* FlameComics : website changes

* add old exclusions back
  • Loading branch information
MikeZeDev authored Nov 25, 2024
1 parent 301011e commit 2ac5cf4
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 51 deletions.
112 changes: 69 additions & 43 deletions web/src/engine/websites/FlameComics.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,48 @@
import { Tags } from '../Tags';
import icon from './FlameComics.webp';
import { type Chapter, DecoratableMangaScraper, Page } from '../providers/MangaPlugin';
import * as MangaStream from './decorators/WordPressMangaStream';
import { Chapter, DecoratableMangaScraper, Page, type MangaPlugin, Manga } from '../providers/MangaPlugin';
import * as Common from './decorators/Common';
import { Fetch, FetchCSS } from '../platform/FetchProvider';
import type { Priority } from '../taskpool/DeferredTask';
import DeScramble from '../transformers/ImageDescrambler';
import { FetchJSON, FetchWindowScript } from '../platform/FetchProvider';

@MangaStream.MangaCSS(/^{origin}\/series\/[^/]+\/$/)
@MangaStream.MangasSinglePageCSS('div.postbody div.soralist ul li a.series', '/series/list-mode/')
@MangaStream.ChaptersSinglePageCSS('div#chapterlist ul li a')
type JSONMangas = {
pageProps: {
series : JSONManga[]
}
}

type JSONMangaDetails = {
pageProps: {
series: JSONManga,
chapters: JSONChapter[]
}
}

type JSONChapterDetails = {
pageProps: {
chapter: JSONChapter
}
}

type JSONManga = {
series_id: number,
title: string,
}

type JSONChapter = {
token: string
chapter: string,
title: string,
images: Record<string, JSONImage>
}

type JSONImage = {
name : string
}

@Common.ImageAjax(true)
export default class extends DecoratableMangaScraper {
private nextBuild = 'J5IvkmIUIIRR6fxzIaczb';
private readonly cdnURL = 'https://cdn.flamecomics.xyz';

public constructor() {
super('flamecomics', 'Flame Comics', 'https://flamecomics.xyz', Tags.Media.Manga, Tags.Media.Manhwa, Tags.Media.Manhua, Tags.Language.English, Tags.Source.Scanlator);
Expand All @@ -20,41 +52,35 @@ export default class extends DecoratableMangaScraper {
return icon;
}

public override async Initialize(): Promise<void> {
this.nextBuild = await FetchWindowScript(new Request(new URL(this.URI)), `__NEXT_DATA__.buildId`, 2500) ?? this.nextBuild;
}

public override ValidateMangaURL(url: string): boolean {
return new RegExpSafe(`^${this.URI.origin}/series/\\d+$`).test(url);
}

public override async FetchManga(provider: MangaPlugin, url: string): Promise<Manga> {
const mangaId = url.split('/').at(-1);
const { pageProps: { series } } = await FetchJSON<JSONMangaDetails>(new Request(new URL(`/_next/data/${this.nextBuild}/series/${mangaId}.json`, this.URI)));
return new Manga(this, provider, series.series_id.toString(), series.title);
}

public override async FetchMangas(provider: MangaPlugin): Promise<Manga[]> {
const { pageProps: { series } } = await FetchJSON<JSONMangas>(new Request(new URL(`/_next/data/${this.nextBuild}/browse.json`, this.URI)));
return series.map(serie => new Manga(this, provider, serie.series_id.toString(), serie.title));
}

public override async FetchChapters(manga: Manga): Promise<Chapter[]> {
const { pageProps: { chapters } } = await FetchJSON<JSONMangaDetails>(new Request(new URL(`/_next/data/${this.nextBuild}/series/${manga.Identifier}.json`, this.URI)));
return chapters.map(episode => new Chapter(this, manga, episode.token, ['Chapter', episode.chapter, episode.title].join(' ').trim()));
}

public override async FetchPages(chapter: Chapter): Promise<Page[]> {
const data = await FetchCSS<HTMLDivElement>(new Request(new URL(chapter.Identifier, this.URI)), 'div.composed_figure');
if (data.length == 0) return MangaStream.FetchPagesSinglePageCSS.call(this, chapter, [/readonflame[^.]+\.(gif|jpeg|jpg|png|avif)$/, /chevron\.png/]);

return data.map(page => {
const images = [...page.querySelectorAll('img')].map(image => image.getAttribute('src'));
return new Page(this, chapter, new URL(images[0]), { secondaryPic: images[1] });
});
}

public override async FetchImage(page: Page, priority: Priority, signal: AbortSignal): Promise<Blob> {
if (!page.Parameters?.secondaryPic) return Common.FetchImageAjax.call(this, page, priority, signal);

const promises: Promise<Blob>[] = [];
promises.push(this.FetchBlob(page.Link.href, priority, signal));
promises.push(this.FetchBlob((page.Parameters.secondaryPic) as string, priority, signal));
const [blobMainImage, blobSecondImage] = await Promise.all(promises);

const b1 = await createImageBitmap(blobMainImage);
const b2 = await createImageBitmap(blobSecondImage);
return DeScramble(new ImageData(b1.width + b2.width, b1.height), async (_, ctx) => {
ctx.drawImage(b1, 0, 0);
ctx.drawImage(b2, b1.width, 0);
});
}

private async FetchBlob(url: string, priority: Priority, signal: AbortSignal): Promise<Blob> {
return this.imageTaskPool.Add(async () => {
const response = await Fetch(new Request(url, {
signal,
headers: {
Referer: this.URI.href
}
}));
return response.blob();
}, priority, signal);
const exclude = [/readonflame[^.]+\.(gif|jpeg|jpg|png|avif)$/, /chevron\.png/];
const { pageProps: { chapter: { images } } } = await FetchJSON<JSONChapterDetails>(new Request(new URL(`/_next/data/${this.nextBuild}/series/${chapter.Parent.Identifier}/${chapter.Identifier}.json`, this.URI)));
return Object.values(images)
.filter(image => exclude.none(pattern => pattern.test(image.name)))
.map(image => new Page(this, chapter, new URL(`/series/${chapter.Parent.Identifier}/${chapter.Identifier}/${image.name}`, this.cdnURL)));
}
}
15 changes: 7 additions & 8 deletions web/src/engine/websites/FlameComics_e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,20 @@ const config: Config = {
plugin: {
id: 'flamecomics',
title: 'Flame Comics',
timeout: 30000
},
container: {
url: 'https://flamecomics.xyz/series/solo-necromancy/',
id: '/series/solo-necromancy/',
url: 'https://flamecomics.xyz/series/44',
id: '44',
title: 'Solo Necromancy'
},
child: {
id: '/solo-necromancy-chapter-1/',
title: 'Chapter 1'
id: '503e0f7071083096',
title: 'Chapter 1.0'
},
entry: {
index: 1,
size: 3_653_029,
type: 'image/png'
index: 2,
size: 947_020,
type: 'image/jpeg'
}
};

Expand Down

0 comments on commit 2ac5cf4

Please sign in to comment.