Skip to content

Commit

Permalink
support: akuma.moe #94;
Browse files Browse the repository at this point in the history
  • Loading branch information
MapoMagpie committed Sep 10, 2024
1 parent 2a3a6f6 commit 6e464c0
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 1 deletion.
2 changes: 2 additions & 0 deletions eh-view-enhance.meta.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
// @match https://e621.net/*
// @match https://arca.live/*
// @match https://*.artstation.com/*
// @match https://akuma.moe/*
// @require https://cdn.jsdelivr.net/npm/@zip.js/[email protected]/dist/zip-full.min.js
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/FileSaver.min.js
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/pica.min.js
Expand Down Expand Up @@ -84,6 +85,7 @@
// @connect e621.net
// @connect namu.la
// @connect artstation.com
// @connect akuma.moe
// @connect *
// @grant GM_getValue
// @grant GM_setValue
Expand Down
92 changes: 91 additions & 1 deletion eh-view-enhance.user.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
// @match https://e621.net/*
// @match https://arca.live/*
// @match https://*.artstation.com/*
// @match https://akuma.moe/*
// @require https://cdn.jsdelivr.net/npm/@zip.js/[email protected]/dist/zip-full.min.js
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/FileSaver.min.js
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/pica.min.js
Expand Down Expand Up @@ -84,6 +85,7 @@
// @connect e621.net
// @connect namu.la
// @connect artstation.com
// @connect akuma.moe
// @connect *
// @grant GM_getValue
// @grant GM_setValue
Expand Down Expand Up @@ -3211,6 +3213,93 @@ Report issues here: <a target="_blank" href="https://github.com/MapoMagpie/eh-vi
}
}

class AkumaMatcher extends BaseMatcher {
originImages;
index = 0;
meta;
name() {
return "Akuma.moe";
}
title() {
return this.galleryMeta(document).title;
}
galleryMeta(doc) {
if (!this.meta) {
this.meta = this.initGalleryMeta(doc);
}
return this.meta;
}
initGalleryMeta(doc) {
const title = doc.querySelector("header.entry-header > h1")?.textContent ?? doc.title;
const meta = new GalleryMeta(window.location.href, title);
meta.originTitle = doc.querySelector("header.entry-header > span")?.textContent || void 0;
meta.tags = Array.from(doc.querySelectorAll("ul.info-list > li.meta-data")).reduce((prev, curr) => {
const cat = curr.querySelector("span.data")?.textContent?.replace(":", "").toLowerCase().trim();
if (cat) {
prev[cat] = Array.from(curr.querySelectorAll("span.value")).map((v) => v.textContent?.trim()).filter(Boolean);
}
return prev;
}, {});
return meta;
}
async *fetchPagesSource() {
const csrf = document.querySelector("meta[name='csrf-token'][content]")?.content;
if (!csrf)
throw new Error("cannot get csrf token form this page");
this.originImages = await window.fetch(window.location.href, {
headers: { "X-CSRF-TOKEN": csrf, "X-Requested-With": "XMLHttpRequest", "Sec-Fetch-Dest": "empty" },
method: "POST"
}).then((res) => res.json());
const pagRaw = Array.from(document.querySelectorAll("body > script")).find((s) => s.textContent?.trimStart().startsWith("var ajx"))?.textContent?.match(/pag = (\{.*?\}),/s)?.[1];
if (!pagRaw)
throw new Error("cannot get page info");
const pag = JSON.parse(pagRaw.replaceAll(/(\w+) :/g, '"$1":'));
let idx = pag.idx;
yield document;
while (idx * pag.stp < pag.cnt) {
const res = await window.fetch(pag.act, {
headers: {
"X-CSRF-TOKEN": csrf,
"X-Requested-With": "XMLHttpRequest",
"Sec-Fetch-Dest": "empty",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"
},
method: "POST",
body: `index=${idx}`
});
if (!res.ok)
throw new Error(`fetch thumbnails failed, status: ${res.statusText}`);
idx++;
yield res.text().then((text) => new DOMParser().parseFromString(text, "text/html"));
}
}
async parseImgNodes(page) {
const doc = page;
const items = Array.from(doc.querySelectorAll("li > a.page-item"));
if (items.length === 0)
throw new Error("cannot find thumbnails");
let ret = [];
const digits = this.originImages.length.toString().length;
for (const item of items) {
const origin = this.originImages[this.index];
const href = item.href;
const thumb = item.firstElementChild.src;
const ext = origin.split(".").pop() ?? "jpg";
const originSrc = thumb.slice(0, thumb.indexOf("tbn")) + origin;
const title = (this.index + 1).toString().padStart(digits, "0");
ret.push(new ImageNode(thumb, href, `${title}.${ext}`, void 0, originSrc));
this.index++;
}
return ret;
}
async fetchOriginMeta(node) {
return { url: node.originSrc };
}
workURL() {
return /akuma.moe\/g\/\w+\/?$/;
}
}

class ArcaMatcher extends BaseMatcher {
name() {
return "Arcalive";
Expand Down Expand Up @@ -6009,7 +6098,8 @@ before contentType: ${contentType}, after contentType: ${blob.type}
new MangaCopyMatcher(),
new E621Matcher(),
new ArcaMatcher(),
new ArtStationMatcher()
new ArtStationMatcher(),
new AkumaMatcher()
];
}
function adaptMatcher(url) {
Expand Down
2 changes: 2 additions & 0 deletions src/platform/adapt.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { conf } from "../config";
import { Comic18Matcher } from "./18comic";
import { AkumaMatcher } from "./akuma";
import { ArcaMatcher } from "./arca";
import { ArtStationMatcher } from "./artstation";
import { DanbooruDonmaiMatcher, E621Matcher, GelBooruMatcher, KonachanMatcher, Rule34Matcher, YandereMatcher } from "./danbooru";
Expand Down Expand Up @@ -43,6 +44,7 @@ export function getMatchers(): Matcher[] {
new E621Matcher(),
new ArcaMatcher(),
new ArtStationMatcher(),
new AkumaMatcher(),
];
}

Expand Down
92 changes: 92 additions & 0 deletions src/platform/akuma.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { GalleryMeta } from "../download/gallery-meta";
import ImageNode from "../img-node";
import { PagesSource } from "../page-fetcher";
import { BaseMatcher, OriginMeta } from "./platform";

export class AkumaMatcher extends BaseMatcher {
originImages?: string[];
index: number = 0;
meta?: GalleryMeta;
name(): string {
return "Akuma.moe"
}
title(): string {
return this.galleryMeta(document).title!;
}
galleryMeta(doc: Document): GalleryMeta {
if (!this.meta) {
this.meta = this.initGalleryMeta(doc);
}
return this.meta!;
}
initGalleryMeta(doc: Document): GalleryMeta {
const title = doc.querySelector("header.entry-header > h1")?.textContent ?? doc.title;
const meta = new GalleryMeta(window.location.href, title);
meta.originTitle = doc.querySelector("header.entry-header > span")?.textContent || undefined;
meta.tags = Array.from(doc.querySelectorAll("ul.info-list > li.meta-data"))
.reduce<Record<string, string[]>>((prev, curr) => {
const cat = curr.querySelector("span.data")?.textContent?.replace(":", "").toLowerCase().trim();
if (cat) {
prev[cat] = Array.from(curr.querySelectorAll("span.value")).map(v => v.textContent?.trim()).filter(Boolean) as string[];
}
return prev;
}, {});
return meta;
}
async *fetchPagesSource(): AsyncGenerator<PagesSource> {
// fetch origin images;
const csrf = document.querySelector<HTMLMetaElement>("meta[name='csrf-token'][content]")?.content;
if (!csrf) throw new Error("cannot get csrf token form this page");
this.originImages = await window.fetch(window.location.href, {
headers: { "X-CSRF-TOKEN": csrf, "X-Requested-With": "XMLHttpRequest", "Sec-Fetch-Dest": "empty", },
method: "POST",
}).then(res => res.json()) as string[];
// find page info
const pagRaw = Array.from(document.querySelectorAll<HTMLScriptElement>("body > script"))
.find(s => s.textContent?.trimStart().startsWith("var ajx"))
?.textContent?.match(/pag = (\{.*?\}),/s)?.[1];
if (!pagRaw) throw new Error("cannot get page info");
const pag = JSON.parse(pagRaw.replaceAll(/(\w+) :/g, "\"$1\":")) as { act: string, cnt: number, idx: number, stp: number };
let idx = pag.idx;
yield document;
while (idx * pag.stp < pag.cnt) {
const res = await window.fetch(pag.act, {
headers: {
"X-CSRF-TOKEN": csrf,
"X-Requested-With": "XMLHttpRequest",
"Sec-Fetch-Dest": "empty",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
},
method: "POST",
body: `index=${idx}`
})
if (!res.ok) throw new Error(`fetch thumbnails failed, status: ${res.statusText}`);
idx++;
yield res.text().then(text => new DOMParser().parseFromString(text, "text/html"));
}
}
async parseImgNodes(page: PagesSource): Promise<ImageNode[]> {
const doc = page as Document;
const items = Array.from(doc.querySelectorAll<HTMLAnchorElement>("li > a.page-item"));
if (items.length === 0) throw new Error("cannot find thumbnails");
let ret: ImageNode[] = [];
const digits = this.originImages!.length.toString().length;
for (const item of items) {
const origin = this.originImages![this.index];
const href = item.href;
const thumb = (item.firstElementChild as HTMLImageElement).src;
const ext = origin.split(".").pop() ?? "jpg";
const originSrc = thumb.slice(0, thumb.indexOf("tbn")) + origin;
const title = (this.index + 1).toString().padStart(digits, "0");
ret.push(new ImageNode(thumb, href, `${title}.${ext}`, undefined, originSrc));
this.index++;
}
return ret;
}
async fetchOriginMeta(node: ImageNode): Promise<OriginMeta> {
return { url: node.originSrc! };
}
workURL(): RegExp {
return /akuma.moe\/g\/\w+\/?$/;
}
}
2 changes: 2 additions & 0 deletions vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export default defineConfig(({ command }) => {
'https://e621.net/*',
'https://arca.live/*',
'https://*.artstation.com/*',
'https://akuma.moe/*',
// '*://*/*',
],
name: {
Expand Down Expand Up @@ -120,6 +121,7 @@ export default defineConfig(({ command }) => {
'e621.net',
'namu.la',
'artstation.com',
'akuma.moe',
'*',
],
grant: [
Expand Down

0 comments on commit 6e464c0

Please sign in to comment.