diff --git a/PEE-chrome.crx b/PEE-chrome.crx index a472ff4..e4c0a22 100644 Binary files a/PEE-chrome.crx and b/PEE-chrome.crx differ diff --git a/README.md b/README.md index 9ce9125..201e7e1 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Note: 4chanX isn't a hard requirement, just recommended because it's overall a n ## The newer way (WIP) - [Install 4chanX (recommended)](https://www.4chan-x.net/builds/4chan-X.user.js) -- Install the correct WebExtension for your Browser ([Firefox](https://git.coom.tech/coomdev/PEE/raw/branch/%E4%B8%AD%E5%87%BA%E3%81%97/pngextraembedder-0.210-an+fx.xpi) or [Chrome-based](https://git.coom.tech/coomdev/PEE/raw/branch/%E4%B8%AD%E5%87%BA%E3%81%97/PEE-chrome.crx)) +- Install the correct WebExtension for your Browser ([Firefox](https://git.coom.tech/coomdev/PEE/raw/branch/%E4%B8%AD%E5%87%BA%E3%81%97/pngextraembedder-0.211-an+fx.xpi) or [Chrome-based](https://git.coom.tech/coomdev/PEE/raw/branch/%E4%B8%AD%E5%87%BA%E3%81%97/PEE-chrome.crx)) How to Build ============ diff --git a/build-chrome.js b/build-chrome.js index cf25a43..741ad92 100644 --- a/build-chrome.js +++ b/build-chrome.js @@ -133,7 +133,7 @@ const manif = { define: { global: 'window', execution_mode: '"chrome_api"', - isBackground: 'false', + isBackground: 'true', BUILD_VERSION: JSON.stringify([0, rev]) }, inject: ['./esbuild.inject.js'], diff --git a/build-ff.js b/build-ff.js index d2cce66..b625b7b 100644 --- a/build-ff.js +++ b/build-ff.js @@ -132,7 +132,7 @@ const manif = { define: { global: 'window', execution_mode: '"ff_api"', - isBackground: 'false', + isBackground: 'true', BUILD_VERSION: JSON.stringify([0, rev]) }, inject: ['./esbuild.inject.js'], diff --git a/chrome/manifest.json b/chrome/manifest.json index e31871a..2fe83be 100644 --- a/chrome/manifest.json +++ b/chrome/manifest.json @@ -3,7 +3,7 @@ "update_url": "https://git.coom.tech/fuckjannies/lolipiss/raw/branch/%E4%B8%AD%E5%87%BA%E3%81%97/chrome_update.xml", "name": "PngExtraEmbedder", "description": "Discover embedded files on 4chan and archives!", - "version": "0.210", + "version": "0.211", "icons": { "64": "1449696017588.png" }, diff --git a/chrome_update.xml b/chrome_update.xml index 06fe8c0..2e1cd47 100644 --- a/chrome_update.xml +++ b/chrome_update.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/firefox/manifest.json b/firefox/manifest.json index 74c8119..e9279bb 100644 --- a/firefox/manifest.json +++ b/firefox/manifest.json @@ -7,7 +7,7 @@ }, "name": "PngExtraEmbedder", "description": "Discover embedded files on 4chan and archives!", - "version": "0.210", + "version": "0.211", "icons": { "64": "1449696017588.png" }, diff --git a/firefox_update.json b/firefox_update.json index 1c065a0..815693b 100644 --- a/firefox_update.json +++ b/firefox_update.json @@ -1 +1 @@ -{"addons":{"{34ac4994-07f2-44d2-8599-682516a6c6a6}":{"updates":[{"version":"0.210","update_link":"https://git.coom.tech/fuckjannies/lolipiss/raw/branch/%E4%B8%AD%E5%87%BA%E3%81%97/pee-firefox.zip"}]}}} \ No newline at end of file +{"addons":{"{34ac4994-07f2-44d2-8599-682516a6c6a6}":{"updates":[{"version":"0.211","update_link":"https://git.coom.tech/fuckjannies/lolipiss/raw/branch/%E4%B8%AD%E5%87%BA%E3%81%97/pee-firefox.zip"}]}}} \ No newline at end of file diff --git a/pngextraembedder-0.210-an+fx.xpi b/pngextraembedder-0.210-an+fx.xpi deleted file mode 100644 index 4cb9e94..0000000 Binary files a/pngextraembedder-0.210-an+fx.xpi and /dev/null differ diff --git a/pngextraembedder-0.211-an+fx.xpi b/pngextraembedder-0.211-an+fx.xpi new file mode 100644 index 0000000..bc4e0ac Binary files /dev/null and b/pngextraembedder-0.211-an+fx.xpi differ diff --git a/src/Components/PostOptions.svelte b/src/Components/PostOptions.svelte index b400ee3..f0ec030 100644 --- a/src/Components/PostOptions.svelte +++ b/src/Components/PostOptions.svelte @@ -32,9 +32,10 @@ } // This is an event to signal a change in the container file + let inhibit = false; document.addEventListener("PEEFile", async (e) => { let file = (e as any).detail as File; - if (currentEmbed?.file != file) { + if (currentEmbed?.file != file && !inhibit) { original = file; if ($settings.auto_embed && $appState.client) { const tags = $settings.auto_tags @@ -49,6 +50,8 @@ const nlinks = await uploadFiles(files); links = [...links, ...nlinks]; } + inhibit = true; + setTimeout(() => inhibit = false, 500); // hack around 4chan(X)(?) inconsistent getFile embedContent(e); } }); diff --git a/src/background.ts b/src/background.ts index 3881418..3730709 100644 --- a/src/background.ts +++ b/src/background.ts @@ -102,6 +102,38 @@ obj.webRequest.onBeforeRequest.addListener((details) => { }, filts, ['blocking']); */ +async function deserialize(src: any): Promise { + switch (src.cls) { + case 'FormData': { + const ret = new FormData(); + for (const [key, items] of src.value) { + for (const item of items) { + ret.append(key, await deserialize(item)); + } + } + return ret; + } + case 'File': { + return new File([await (await fetch(src.value)).blob()], src.name, { + lastModified: src.lastModified, + type: src.type + }); + } + case 'Blob': { + return new Blob([await (await fetch(src.value)).blob()], { + type: src.type + }); + } + case 'Object': { + const ret = {} as any; + for (const prop in src.value) { + ret[prop] = await deserialize(src.value[prop]); + } + return ret; + } + } +} + const pendingFetches = new Map(); const bgCorsFetch = async (c: browser.runtime.Port, id: number, input: string, init?: RequestInit) => { @@ -113,6 +145,8 @@ const bgCorsFetch = async (c: browser.runtime.Port, id: number, input: string, i if (input.startsWith('//')) // wtf fireshit?? input = 'https:' + input; + if (init?.body && execution_mode == "chrome_api") + init.body = await deserialize(init.body); const k = await fetch(input, init); let headersStr = ''; const headerObj = {} as any; diff --git a/src/main.ts b/src/main.ts index e46bbb9..148f216 100644 --- a/src/main.ts +++ b/src/main.ts @@ -422,18 +422,20 @@ const startup = async (is4chanX = true) => { registerPlugin('quote', postQuote); if (!is4chanX && location.host.startsWith('boards.4chan')) { - (async () => { - while (!('QR' in window)) - await new Promise(_ => setTimeout(_, 250)); - const qr = QR; - const show = qr.show.bind(qr); - qr.show = (...args: any[]) => { - show(...args); - document.dispatchEvent(new CustomEvent("QRDialogCreation", { - detail: document.getElementById('quickReply') - })); - }; - })(); + const QRObs = new MutationObserver(rec => { + rec.forEach(m => { + m.addedNodes.forEach(no => { + if ((no as HTMLElement).id != "quickReply") { + return; + } + document.dispatchEvent(new CustomEvent("QRDialogCreation", { + detail: no + })); + }); + }); + }); + // only need immediate children of body + QRObs.observe(document.body, { childList: true }); document.addEventListener("QRGetFile", (e) => { const qr = document.getElementById('qrFile') as HTMLInputElement | null; diff --git a/src/platform.ts b/src/platform.ts index e3bf656..f9a4d12 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -69,6 +69,42 @@ const extrBlob = async (url: string) => { return new Uint8Array(ret); }; +async function serialize(src: any): Promise { + if (src instanceof FormData) { + const value = []; + for (const kv of src) + value.push([kv[0], await Promise.all(src.getAll(kv[0]).map(serialize))]); + return { + cls: 'FormData', value, + }; + } + if (src instanceof File) { + const { name, type, lastModified } = src; + const value = URL.createObjectURL(src); + return { + cls: 'File', + name, type, lastModified, value, + }; + } + if (src instanceof Blob) { + const { type } = src; + const value = URL.createObjectURL(src); + return { + cls: 'Blob', type, value, + }; + } + if (src === null || src === undefined || typeof src != "object") + return src; + const ret = { + cls: 'Object', + value: {} + } as any; + for (const prop in src) { + ret.value[prop] = await serialize(src[prop]); + } + return ret; +} + export const corsFetch = async (input: string, init?: RequestInit, lsn?: EventTarget) => { const id = gid++; @@ -82,6 +118,13 @@ export const corsFetch = async (input: string, init?: RequestInit, lsn?: EventTa } }*/ + if (init?.body) { + // Chrom* can't pass around FormData and File/Blobs between + // the content and bg scripts, so the data is passed through bloburls + if (execution_mode == "chrome_api") + init.body = await serialize(init.body); + } + const prom = new Promise>>((_, rej) => { let gcontroller: ReadableStreamController | undefined; let buffer: Uint8Array[] = []; diff --git a/src/utils.ts b/src/utils.ts index 3fe5112..afdce1a 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -63,7 +63,37 @@ const generateThumbnail = async (f: File): Promise => { return Buffer.from(await blob.arrayBuffer()); }; +export const buildPeeFileFF = async (f: File) => { + let thumbnail = new Uint8Array(); + const te = new TextEncoder(); + thumbnail = await generateThumbnail(f); + const namebuf = te.encode(f.name); + const ret = new Uint8Array(4 /* Magic */ + + 1 /* Flags */ + namebuf.byteLength + 1 + + (thumbnail.byteLength != 0 ? (4 + thumbnail.byteLength) : 0) /* TSize + Thumbnail */ + + f.size /*Teh file*/); + const ret32 = new DataView(ret.buffer); + let ptr = 0; + ret.set(te.encode('PEE\0'), 0); + ptr += 4; + ret[ptr++] = 1 | ((+(thumbnail.length != 0)) << 2); + ret.set(namebuf, ptr); + ptr += namebuf.byteLength; + ret[ptr++] = 0; + if (thumbnail.length > 0) { + ret32.setUint32(ptr, thumbnail.byteLength, true); + ptr += 4; + ret.set(thumbnail, ptr); + ptr += thumbnail.byteLength; + } + const content = await f.arrayBuffer(); + ret.set(new Uint8Array(content), ptr); + return new Blob([ret]); +}; + export const buildPeeFile = async (f: File) => { + if (execution_mode == "ff_api") + return buildPeeFileFF(f); //const isMemeBrowser = navigator.userAgent.indexOf("Chrome") == -1; let thumbnail = Buffer.alloc(0); thumbnail = await generateThumbnail(f); @@ -85,7 +115,8 @@ export const buildPeeFile = async (f: File) => { thumbnail.copy(ret, ptr); ptr += thumbnail.byteLength; } - Buffer.from(await f.arrayBuffer()).copy(ret, ptr); + const content = await f.arrayBuffer(); + Buffer.from(content).copy(ret, ptr); return new Blob([ret]); }; @@ -200,7 +231,8 @@ export const uploadFiles = async (injs: File[]) => { let total = 0; fireNotification('info', `Uploading ${injs.length} files...`); return await Promise.all(injs.map(async inj => { - const ret = await filehosts[csettings.fhost || 0].uploadFile(await buildPeeFile(inj)); + const peefile = await buildPeeFile(inj); + const ret = await filehosts[csettings.fhost || 0].uploadFile(peefile); fireNotification('info', `Uploaded files [${++total}/${injs.length}] ${ret}`); return ret; })); diff --git a/update.xml b/update.xml deleted file mode 100644 index fea80b6..0000000 --- a/update.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file