diff --git a/main.meta.js b/main.meta.js index eec4aee..165bd6c 100644 --- a/main.meta.js +++ b/main.meta.js @@ -1,7 +1,7 @@ // ==UserScript== // @name PNGExtraEmbed // @namespace https://coom.tech/ -// @version 0.205 +// @version 0.206 // @description uhh // @author You // @match https://boards.4channel.org/* diff --git a/main.user.js b/main.user.js index 92d4d22..61c0340 100644 --- a/main.user.js +++ b/main.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name PNGExtraEmbed // @namespace https://coom.tech/ -// @version 0.205 +// @version 0.206 // @description uhh // @author You // @match https://boards.4channel.org/* @@ -88,7 +88,7 @@ const _DOMParser = DOMParser; var define_BUILD_VERSION_default; var init_define_BUILD_VERSION = __esm({ ""() { - define_BUILD_VERSION_default = [0, 205]; + define_BUILD_VERSION_default = [0, 206]; } }); @@ -22180,7 +22180,9 @@ const _DOMParser = DOMParser; postsWithFiles: (h) => [...(h || document).querySelectorAll(".file")].map((e) => e.closest(".postContainer")), settingsHost: () => document.getElementById("navtopright"), catalogControlHost: () => document.getElementById("settings"), - getImageLink: (post) => post.querySelector('a[target="_blank"]')?.getAttribute("href") || "", + getImageLink: async function* (post) { + yield post.querySelector('a[target="_blank"]')?.getAttribute("href") || ""; + }, getFilename: (post) => { const a = post.querySelector('a[target="_blank"]'); if (a && a.title) @@ -22199,7 +22201,9 @@ const _DOMParser = DOMParser; postsWithFiles: (h) => [...(h || document).querySelectorAll('.postContainer:not([class*="noFile"])')], settingsHost: () => document.getElementById("shortcuts"), catalogControlHost: () => document.getElementById("index-options"), - getImageLink: (post) => post.querySelector('a[target="_blank"]')?.getAttribute("href") || "", + getImageLink: async function* (post) { + yield post.querySelector('a[target="_blank"]')?.getAttribute("href") || ""; + }, getFilename: (post) => { const a = post.querySelector('a[target="_blank"]'); const origlink = post.querySelector('.file-info > a[target*="_blank"]'); @@ -22217,7 +22221,16 @@ const _DOMParser = DOMParser; postsWithFiles: (h) => [...(h || document).querySelectorAll('article[class*="has_image"]')], settingsHost: () => document.querySelector(".letters"), catalogControlHost: () => document.getElementById("index-options"), - getImageLink: (post) => post.querySelector("a[rel]")?.getAttribute("href") || "", + getImageLink: async function* (post) { + yield post.querySelector("a[rel]")?.getAttribute("href") || ""; + if (location.host == "arch.b4k.co") { + const pid = post.id.match(/\d+/)[0]; + const board = location.pathname.match(/\/(..?.?)\//)[1]; + const res = await GM_fetch(`https://archive.wakarimasen.moe/_/api/chan/post/?board=${board}&num=${pid}`); + const data = await res.json(); + yield data.media.media_link; + } + }, getFilename: (post) => { const opfn = post.querySelector("a.post_file_filename")?.textContent; if (opfn) @@ -23249,7 +23262,7 @@ const _DOMParser = DOMParser; appState.subscribe((v) => { cappState = v; }); - var processImage = async (src, fn, hex, prevurl, onfound) => { + var processImage = async (srcs, fn, hex, prevurl, onfound) => { return Promise.all(processors.filter((e) => e.match(fn)).map(async (proc) => { if (proc.skip) { const md5 = import_buffer11.Buffer.from(hex, "base64"); @@ -23259,28 +23272,39 @@ const _DOMParser = DOMParser; } return; } - const iter = streamRemote(src); - if (!iter) - return; - let cumul = import_buffer11.Buffer.alloc(0); - let found; - let chunk = { done: true }; + let succ = false; + let cumul; do { - const { value, done } = await iter.next(typeof found === "boolean"); - if (done) { - chunk = { done: true }; - } else { - chunk = { done: false, value }; - cumul = import_buffer11.Buffer.concat([cumul, value]); - found = await proc.has_embed(cumul); + try { + const n = await srcs.next(); + if (n.done) + return; + const iter = streamRemote(n.value); + if (!iter) + return; + cumul = import_buffer11.Buffer.alloc(0); + let found; + let chunk = { done: true }; + do { + const { value, done } = await iter.next(typeof found === "boolean"); + if (done) { + chunk = { done: true }; + } else { + chunk = { done: false, value }; + cumul = import_buffer11.Buffer.concat([cumul, value]); + found = await proc.has_embed(cumul); + } + } while (found !== false && !chunk.done); + succ = true; + await iter.next(true); + if (found === false) { + return; + } + onfound(); + return [await proc.extract(cumul), false]; + } catch { } - } while (found !== false && !chunk.done); - await iter.next(true); - if (found === false) { - return; - } - onfound(); - return [await proc.extract(cumul), false]; + } while (!succ); })); }; var textToElement = (s) => document.createRange().createContextualFragment(s).children[0]; diff --git a/src/main.ts b/src/main.ts index 7cf9131..ee4f7c3 100644 --- a/src/main.ts +++ b/src/main.ts @@ -87,7 +87,7 @@ type EmbeddedFileWithoutPreview = { export type EmbeddedFile = EmbeddedFileWithPreview | EmbeddedFileWithoutPreview; -const processImage = async (src: string, fn: string, hex: string, prevurl: string, onfound: () => void): Promise<([EmbeddedFile[], boolean] | undefined)[]> => { +const processImage = async (srcs: AsyncGenerator, fn: string, hex: string, prevurl: string, onfound: () => void): Promise<([EmbeddedFile[], boolean] | undefined)[]> => { return Promise.all(processors.filter(e => e.match(fn)).map(async proc => { if (proc.skip) { // skip file downloading, file is referenced from the filename @@ -98,30 +98,41 @@ const processImage = async (src: string, fn: string, hex: string, prevurl: strin return [await proc.extract(md5, fn), true] as [EmbeddedFile[], boolean]; } return; } - // TODO: Move this outside the loop? - const iter = streamRemote(src); - if (!iter) - return; - let cumul = Buffer.alloc(0); - let found: boolean | undefined; - let chunk: ReadableStreamDefaultReadResult = { done: true }; + let succ = false; + let cumul: Buffer; do { - const { value, done } = await iter.next(typeof found === "boolean"); - if (done) { - chunk = { done: true } as ReadableStreamDefaultReadDoneResult; - } else { - chunk = { done: false, value } as ReadableStreamDefaultReadValueResult; - cumul = Buffer.concat([cumul, value!]); - found = await proc.has_embed(cumul); + try { + const n = await srcs.next(); + if (n.done) + return; // no more links to try + const iter = streamRemote(n.value); + if (!iter) + return; + cumul = Buffer.alloc(0); + let found: boolean | undefined; + let chunk: ReadableStreamDefaultReadResult = { done: true }; + do { + const { value, done } = await iter.next(typeof found === "boolean"); + if (done) { + chunk = { done: true } as ReadableStreamDefaultReadDoneResult; + } else { + chunk = { done: false, value } as ReadableStreamDefaultReadValueResult; + cumul = Buffer.concat([cumul, value!]); + found = await proc.has_embed(cumul); + } + } while (found !== false && !chunk.done /* Because we only embed links now, it's safe to assume we get everything we need in the first chunk */); + succ = true; + await iter.next(true); + if (found === false) { + //console.log(`Gave up on ${src} after downloading ${cumul.byteLength} bytes...`); + return; + } + onfound(); + return [await proc.extract(cumul), false] as [EmbeddedFile[], boolean]; + } catch { + // ignore error and retry with another link } - } while (found !== false && !chunk.done /* Because we only embed links now, it's safe to assume we get everything we need in the first chunk */); - await iter.next(true); - if (found === false) { - //console.log(`Gave up on ${src} after downloading ${cumul.byteLength} bytes...`); - return; - } - onfound(); - return [await proc.extract(cumul), false] as [EmbeddedFile[], boolean]; + } while (!succ); })); }; diff --git a/src/websites/index.ts b/src/websites/index.ts index 834201f..24a5ca6 100644 --- a/src/websites/index.ts +++ b/src/websites/index.ts @@ -1,10 +1,12 @@ +import { GM_fetch } from "../requests"; + export type QueryProcessor = { getPost: (post: HTMLElement) => HTMLElement; getFileThumbnail: (post: HTMLElement) => HTMLElement; postsWithFiles: (host?: HTMLElement) => HTMLElement[]; settingsHost: () => HTMLSpanElement; catalogControlHost: () => HTMLDivElement; - getImageLink: (post: HTMLElement) => string; + getImageLink:(post: HTMLElement) => AsyncGenerator; getThumbnailLink: (post: HTMLElement) => string; getFilename: (post: HTMLElement) => string; getMD5: (post: HTMLElement) => string; @@ -19,7 +21,11 @@ export const V4chan: QueryProcessor = { postsWithFiles: (h) => [...(h || document).querySelectorAll('.file')].map(e => e.closest('.postContainer')) as any, settingsHost: () => document.getElementById("navtopright") as any, catalogControlHost: () => document.getElementById("settings") as HTMLDivElement, - getImageLink: (post: HTMLElement) => post.querySelector('a[target="_blank"]')?.getAttribute('href') || '', + getImageLink: async function *(post: HTMLElement) { + yield post.querySelector('a[target="_blank"]')?.getAttribute('href') || ''; + }, + + //(post: HTMLElement) => post.querySelector('a[target="_blank"]')?.getAttribute('href') || '', getFilename: (post: HTMLElement) => { const a = post.querySelector('a[target="_blank"]') as (HTMLAnchorElement | null); if (a && a.title) @@ -39,7 +45,9 @@ export const X4chan: QueryProcessor = { postsWithFiles: (h) => [...(h || document).querySelectorAll('.postContainer:not([class*="noFile"])')] as HTMLElement[], settingsHost: () => document.getElementById("shortcuts") as any, catalogControlHost: () => document.getElementById("index-options") as HTMLDivElement, - getImageLink: (post: HTMLElement) => post.querySelector('a[target="_blank"]')?.getAttribute('href') || '', + getImageLink: async function *(post: HTMLElement) { + yield post.querySelector('a[target="_blank"]')?.getAttribute('href') || ''; + }, getFilename: (post: HTMLElement) => { const a = post.querySelector('a[target="_blank"]') as (HTMLAnchorElement | null); const origlink = post.querySelector('.file-info > a[target*="_blank"]') as HTMLAnchorElement; @@ -58,7 +66,16 @@ export const FoolFuuka: QueryProcessor = { postsWithFiles: (h) => [...(h || document).querySelectorAll('article[class*="has_image"]')] as HTMLElement[], settingsHost: () => document.querySelector(".letters") as any, catalogControlHost: () => document.getElementById("index-options") as HTMLDivElement, - getImageLink: (post: HTMLElement) => post.querySelector('a[rel]')?.getAttribute('href') || '', + getImageLink: async function *(post: HTMLElement) { + yield post.querySelector('a[rel]')?.getAttribute('href') || ''; + if (location.host == "arch.b4k.co") { //get fucked + const pid = post.id.match(/\d+/)![0]; + const board = location.pathname.match(/\/(..?.?)\//)![1]; + const res = await GM_fetch(`https://archive.wakarimasen.moe/_/api/chan/post/?board=${board}&num=${pid}`); + const data = await res.json(); + yield data.media.media_link; + } + }, getFilename: (post: HTMLElement) => { const opfn = post.querySelector('a.post_file_filename')?.textContent; if (opfn)