Browse Source

Optimize JPG load times. Give option to disable loading on catalog

pull/46/head
coomdev 2 years ago
parent
commit
aa18531282
  1. 2
      README.md
  2. 6321
      chrome/dist/main.js
  3. 2
      chrome/manifest.json
  4. 6323
      dist/main.js
  5. 6445
      firefox/dist/main.js
  6. 2
      firefox/manifest.json
  7. 2
      firefox_update.json
  8. 2
      main.meta.js
  9. 6325
      main.user.js
  10. BIN
      pngextraembedder-0.273.xpi
  11. 9
      src/Components/App.svelte
  12. 72
      src/jpg.ts
  13. 44
      src/main.ts
  14. 1
      src/stores.ts

2
README.md

@ -25,7 +25,7 @@ Please report any issue you have with those (only for mainstream browsers)
Also, use this if you plan to use b4k's archive. Also, use this if you plan to use b4k's archive.
- [Install 4chanX (recommended)](https://www.4chan-x.net/builds/4chan-X.user.js) - [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/fuckjannies/lolipiss/raw/branch/%E4%B8%AD%E5%87%BA%E3%81%97/pngextraembedder-0.271.xpi) or [Chrome-based](https://chrome.google.com/webstore/detail/pngextraembedder/bfhpobiikighljcapcfmfganodihbicj)) - Install the correct WebExtension for your Browser ([Firefox](https://git.coom.tech/fuckjannies/lolipiss/raw/branch/%E4%B8%AD%E5%87%BA%E3%81%97/pngextraembedder-0.273.xpi) or [Chrome-based](https://chrome.google.com/webstore/detail/pngextraembedder/bfhpobiikighljcapcfmfganodihbicj))
For FF users, the extension is signed so you can just drag and drop it on your about:addons tab. For FF users, the extension is signed so you can just drag and drop it on your about:addons tab.

6321
chrome/dist/main.js

File diff suppressed because it is too large

2
chrome/manifest.json

@ -2,7 +2,7 @@
"manifest_version": 3, "manifest_version": 3,
"name": "PngExtraEmbedder", "name": "PngExtraEmbedder",
"description": "Discover embedded files on 4chan and archives!", "description": "Discover embedded files on 4chan and archives!",
"version": "0.270", "version": "0.273",
"icons": { "icons": {
"64": "1449696017588.png" "64": "1449696017588.png"
}, },

6323
dist/main.js

File diff suppressed because it is too large

6445
firefox/dist/main.js

File diff suppressed because it is too large

2
firefox/manifest.json

@ -7,7 +7,7 @@
}, },
"name": "PngExtraEmbedder", "name": "PngExtraEmbedder",
"description": "Discover embedded files on 4chan and archives!", "description": "Discover embedded files on 4chan and archives!",
"version": "0.271", "version": "0.273",
"icons": { "icons": {
"64": "1449696017588.png" "64": "1449696017588.png"
}, },

2
firefox_update.json

@ -1 +1 @@
{"addons":{"{34ac4994-07f2-44d2-8599-682516a6c6a6}":{"updates":[{"version":"0.271","update_link":"https://git.coom.tech/fuckjannies/lolipiss/raw/branch/%E4%B8%AD%E5%87%BA%E3%81%97/pngextraembedder-0.271.xpi"}]}}} {"addons":{"{34ac4994-07f2-44d2-8599-682516a6c6a6}":{"updates":[{"version":"0.273","update_link":"https://git.coom.tech/fuckjannies/lolipiss/raw/branch/%E4%B8%AD%E5%87%BA%E3%81%97/pngextraembedder-0.273.xpi"}]}}}

2
main.meta.js

@ -1,7 +1,7 @@
// ==UserScript== // ==UserScript==
// @name PNGExtraEmbed // @name PNGExtraEmbed
// @namespace https://coom.tech/ // @namespace https://coom.tech/
// @version 0.270 // @version 0.273
// @description uhh // @description uhh
// @author You // @author You
// @match https://boards.4channel.org/* // @match https://boards.4channel.org/*

6325
main.user.js

File diff suppressed because it is too large

BIN
pngextraembedder-0.273.xpi

Binary file not shown.

9
src/Components/App.svelte

@ -125,12 +125,8 @@
</TabList> </TabList>
<TabPanel> <TabPanel>
<label> <label>
<input type="checkbox" bind:checked={$settings.jpeg} /> <input type="checkbox" bind:checked={$settings.notcata} />
Enable JPGs support (JPG embed and extract) Disable loading on catalog
<a
title="JPG embed detection is relatively slow, heavy, so you might want to also enable server cache loading"
>?</a
>
</label> </label>
<label> <label>
<input type="checkbox" bind:checked={$cached} /> <input type="checkbox" bind:checked={$cached} />
@ -446,6 +442,7 @@
} }
h4 { h4 {
text-align: center;
margin: 0; margin: 0;
} }

72
src/jpg.ts

@ -1,7 +1,6 @@
import { Buffer } from "buffer"; import { Buffer } from "buffer";
import type { ImageProcessor } from "./main"; import type { ImageProcessor } from "./main";
import pngv3 from "./pngv3"; import {f5stego} from './f5stego';
import f5 from 'f5stegojs';
import { settings } from "./stores"; import { settings } from "./stores";
import { decodeCoom3Payload } from "./utils"; import { decodeCoom3Payload } from "./utils";
@ -11,89 +10,34 @@ settings.subscribe(b => {
csettings = b; csettings = b;
}); });
export const convertToPng = async (f: File): Promise<Blob | undefined> => {
const can = document.createElement("canvas");
const url = URL.createObjectURL(f);
try {
let dims: [number, number];
let source: CanvasImageSource;
if (f.type.startsWith("image")) {
const imgElem = document.createElement('img');
imgElem.src = url;
await new Promise(_ => imgElem.onload = _);
dims = [imgElem.naturalWidth, imgElem.naturalHeight];
source = imgElem;
} else if (f.type.startsWith("video")) {
const vidElem = document.createElement('video');
vidElem.src = url;
await new Promise(_ => vidElem.onloadedmetadata = _);
vidElem.currentTime = 0;
await new Promise(_ => vidElem.onloadeddata = _);
await new Promise(requestAnimationFrame);
await new Promise(requestAnimationFrame);
await new Promise(requestAnimationFrame);
dims = [vidElem.videoWidth, vidElem.videoHeight];
source = vidElem;
} else
return;
can.width = dims[0];
can.height = dims[1];
const ctx = can.getContext("2d");
if (!ctx)
return;
ctx.drawImage(source, 0, 0, dims[0], dims[1]);
const blob = await new Promise<Blob | null>(_ => can.toBlob(_, "image/png"));
if (!blob)
return;
return blob;
} finally {
URL.revokeObjectURL(url);
}
};
const key = Buffer.from("CUNNYCUNNYCUNNY"); const key = Buffer.from("CUNNYCUNNYCUNNY");
const f5inst = new f5(key); const f5inst = new f5stego(key);
const injectTrue = async (b: File, links: string[]) => { const inject = async (b: File, links: string[]) => {
// TODO: maybe do a lossless crop/embed/concat? // TODO: maybe do a lossless crop/embed/concat?
if (b.size / 20 < links.join(' ').length) if (b.size / 20 < links.join(' ').length)
throw "Image too small to embed."; throw "Image too small to embed.";
const arr = new Uint8Array(new Uint8Array(await b.arrayBuffer())); const arr = new Uint8Array(new Uint8Array(await b.arrayBuffer()));
const buff = f5inst.embed(arr, new TextEncoder().encode(links.join(' '))); const buff = f5inst.embed(arr, new TextEncoder().encode(links.join(' ')), 1);
return Buffer.from(buff); return Buffer.from(buff);
}; };
const inject = async (b: File, links: string[]) => {
if (csettings.jpeg)
return injectTrue(b, links);
const pngfile = await convertToPng(b);
if (!pngfile || pngfile.size > 3000 * 1024) {
throw new Error("Couldn't convert file to PNG: resulting filesize too big.");
}
return pngv3.inject!(new File([pngfile], b.name), links);
};
// unfortunately, because of the way f5 work, we can't determine // unfortunately, because of the way f5 work, we can't determine
// if there's an embedded message until we have the complete file // if there's an embedded message until we have the complete file
// but the way PEE was designed forces us to just try to extract something until it works // but the way PEE was designed forces us to just try to extract something until it works
const has_embed = (b: Buffer) => { const has_embed = (b: Buffer) => {
if (!csettings.jpeg)
return false;
try { try {
const res = f5inst.extract(b); const res = f5inst.extract(b);
if (!res) if (!res)
return; // unsure return false; // unsure
if (res.length > 1024) // probably garbage, allows for ~20 links from take-me-to.space, should be enough if (res.length > 1024) // probably garbage, allows for ~20 links from take-me-to.space, should be enough
return; // unsure return false; // unsure
const str = Buffer.from(res).toString(); const str = Buffer.from(res).toString();
if (!str.match(/^[a-zA-Z0-9:/.\-_ ]+$/)) if (!str.match(/^[a-zA-Z0-9:/.\-_ ]+$/))
return; // unsure return false; // unsure
return str; // sure return str; // sure
} catch { } catch {
return; // unsure return false; // unsure
} }
}; };

44
src/main.ts

@ -18,7 +18,7 @@ import Embeddings from './Components/Embeddings.svelte';
import EyeButton from './Components/EyeButton.svelte'; import EyeButton from './Components/EyeButton.svelte';
import NotificationsHandler from './Components/NotificationsHandler.svelte'; import NotificationsHandler from './Components/NotificationsHandler.svelte';
import { decodeCoom3Payload, fireNotification, getEmbedsFromCache, getSelectedFile } from "./utils"; import { fireNotification, getEmbedsFromCache, getSelectedFile } from "./utils";
import { getQueryProcessor, QueryProcessor } from "./websites"; import { getQueryProcessor, QueryProcessor } from "./websites";
import { ifetch, Platform, streamRemote, supportedAltDomain, supportedMainDomain } from "./platform"; import { ifetch, Platform, streamRemote, supportedAltDomain, supportedMainDomain } from "./platform";
import TextEmbeddingsSvelte from "./Components/TextEmbeddings.svelte"; import TextEmbeddingsSvelte from "./Components/TextEmbeddings.svelte";
@ -506,25 +506,29 @@ const startup = async (is4chanX = true) => {
//await Promise.all([...document.querySelectorAll('.postContainer')].filter(e => e.textContent?.includes("191 KB")).map(e => processPost(e as any))); //await Promise.all([...document.querySelectorAll('.postContainer')].filter(e => e.textContent?.includes("191 KB")).map(e => processPost(e as any)));
// keep this to handle posts getting inlined // keep this to handle posts getting inlined
const mo = new MutationObserver(reco => { if (!cappState.isCatalog) {
for (const rec of reco) const mo = new MutationObserver(reco => {
if (rec.type == "childList") for (const rec of reco)
rec.addedNodes.forEach(e => { if (rec.type == "childList")
if (!(e instanceof HTMLElement)) rec.addedNodes.forEach(e => {
return; if (!(e instanceof HTMLElement))
// apparently querySelector cannot select the root element if it matches return;
let el = qp.postsWithFiles(e); if (cappState.isCatalog && csettings.notcata)
if (!el && e.classList.contains('postContainer')) return;
el = [e]; // apparently querySelector cannot select the root element if it matches
if (el) let el = qp.postsWithFiles(e);
[...el].map(el => processPost(el as any)); if (!el && e.classList.contains('postContainer'))
}); el = [e];
}); if (el)
[...el].map(el => processPost(el as any));
document.querySelectorAll('.board').forEach(e => { });
mo.observe(e!, { childList: true, subtree: true }); });
});
document.querySelectorAll('.board').forEach(e => {
mo.observe(e!, { childList: true, subtree: true });
});
}
if (!document.body) { if (!document.body) {
let bodyRes: any; let bodyRes: any;
const bodyInit = new Promise(r => bodyRes = r); const bodyInit = new Promise(r => bodyRes = r);
@ -582,6 +586,8 @@ const startup = async (is4chanX = true) => {
button.onclick = () => scrapeBoard(button); button.onclick = () => scrapeBoard(button);
opts.insertAdjacentElement("beforebegin", button); opts.insertAdjacentElement("beforebegin", button);
} }
if (csettings.notcata)
return;
} }
const n = 7; const n = 7;

1
src/stores.ts

@ -17,6 +17,7 @@ export const initial_settings = localLoad('settingsv2', {
xpv: false, xpv: false,
xpi: false, xpi: false,
hyd: false, hyd: false,
notcata: false,
ak: '', ak: '',
auto_embed: 0, auto_embed: 0,
auto_tags: '', auto_tags: '',

Loading…
Cancel
Save