Browse Source

Merge branch 'feat-black-posts' into 中出し

Anonymous 2 years ago
parent
commit
c8cf1edc34
  1. 2
      main.meta.js
  2. 114
      main.user.js
  3. 22
      src/Embedding.svelte
  4. 4
      src/Embeddings.svelte
  5. 10
      src/gif.ts
  6. 29
      src/global.css
  7. 55
      src/main.ts
  8. 11
      src/png.ts
  9. 34
      src/thirdeye.ts
  10. 10
      src/webm.ts

2
main.meta.js

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

114
main.user.js

@ -1,7 +1,7 @@
// ==UserScript==
// @name PNGExtraEmbed
// @namespace https://coom.tech/
// @version 0.113
// @version 0.115
// @description uhh
// @author You
// @match https://boards.4channel.org/*
@ -11061,7 +11061,7 @@
});
// src/global.css
var global_default = ".pee-hidden {\r\n display: none;\r\n}\r\n\r\n.extractedImg {\r\n width: auto;\r\n height: auto;\r\n max-width: 125px;\r\n max-height: 125px;\r\n cursor: pointer;\r\n}\r\n\r\n#delform .postContainer>div.hasembed {\r\n border-right: 3px dashed deeppink !important;\r\n}\r\n\r\n.hasembed.catalog-post {\r\n border: 3px dashed deeppink !important;\r\n}\r\n\r\n#delform .postContainer>div.hasext {\r\n border-right: 3px dashed goldenrod !important;\r\n}\r\n\r\n#delform .postContainer>div.hasmultiple {\r\n border-right: 3px dashed cornflowerblue !important;\r\n}\r\n\r\n\r\n.hasext.catalog-post {\r\n border: 3px dashed goldenrod !important;\r\n}\r\n\r\n.expanded-image>.post>.file .fileThumb>img[data-md5] {\r\n display: none;\r\n}\r\n\r\n.expanded-image>.post>.file .fileThumb .full-image {\r\n display: inline;\r\n}\r\n\r\n.pee-settings {\r\n position: fixed;\r\n top: 0;\r\n width: 100%;\r\n height: 100%;\r\n pointer-events: none;\r\n}\r\n\r\ndiv.hasemb .catalog-host img {\r\n border: 1px solid deeppink;\r\n}\r\n\r\ndiv.hasext .catalog-host img {\r\n border: 1px solid goldenrod;\r\n}\r\n\r\ndiv.hasmultiple .catalog-host img {\r\n border: 1px solid cornflowerblue;\r\n}\r\n\r\n.catalog-host img {\r\n position: absolute;\r\n top: -5px;\r\n right: 0px;\r\n max-width: 80px;\r\n max-height: 80px;\r\n box-shadow: 0px 0px 4px 2px #00000090;\r\n}\r\n\r\n.fileThumb.filehost {\r\n margin-left: 0 !important;\r\n display: flex;\r\n gap: 20px;\r\n}\r\n";
var global_default = ".pee-hidden {\r\n display: none;\r\n}\r\n\r\n.extractedImg {\r\n width: auto;\r\n height: auto;\r\n max-width: 125px;\r\n max-height: 125px;\r\n cursor: pointer;\r\n}\r\n\r\n#delform .postContainer>div.hasblack {\r\n border-right: 3px dashed black !important;\r\n}\r\n.hasblack.catalog-post {\r\n border: 3px dashed black !important;\r\n}\r\n\r\n#delform .postContainer>div.hasembed {\r\n border-right: 3px dashed deeppink !important;\r\n}\r\n\r\n.hasembed.catalog-post {\r\n border: 3px dashed deeppink !important;\r\n}\r\n\r\n#delform .postContainer>div.hasext {\r\n border-right: 3px dashed goldenrod !important;\r\n}\r\n\r\n.hasext.catalog-post {\r\n border: 3px dashed goldenrod !important;\r\n}\r\n\r\n#delform .postContainer>div.hasmultiple {\r\n border-right: 3px dashed cornflowerblue !important;\r\n}\r\ndiv.hasmultiple.catalog-post {\r\n border-right: 3px dashed cornflowerblue !important;\r\n}\r\n\r\n.expanded-image>.post>.file .fileThumb>img[data-md5] {\r\n display: none;\r\n}\r\n\r\n.expanded-image>.post>.file .fileThumb .full-image {\r\n display: inline;\r\n}\r\n\r\n.pee-settings {\r\n position: fixed;\r\n top: 0;\r\n width: 100%;\r\n height: 100%;\r\n pointer-events: none;\r\n}\r\n\r\n#delform div.hasemb .catalog-host .place video,\r\n#delform div.hasemb .catalog-host .place img {\r\n border: 1px solid deeppink;\r\n}\r\n\r\n#delform div.hasext .catalog-host .place video,\r\n#delform div.hasext .catalog-host .place img {\r\n border: 1px solid goldenrod;\r\n}\r\n\r\n#delform .div.hasmultiple .catalog-host video,\r\n#delform .div.hasmultiple .catalog-host img {\r\n border: 1px solid cornflowerblue;\r\n}\r\n\r\n#delform .catalog-host .place video,\r\n#delform .catalog-host .place img {\r\n position: absolute;\r\n top: -5px;\r\n right: 0px;\r\n max-width: 80px;\r\n max-height: 80px;\r\n box-shadow: 0px 0px 4px 2px #00000090;\r\n}\r\n\r\n.fileThumb.filehost {\r\n margin-left: 0 !important;\r\n display: flex;\r\n gap: 20px;\r\n}\r\n";
// src/png.ts
init_esbuild_inject();
@ -11217,21 +11217,22 @@
case "tEXt":
buff = chunk;
if (buff.slice(4, 4 + CUM0.length).equals(CUM0)) {
return true;
return 0 /* HAS_PEE */;
}
break;
case "IDAT":
case "IEND":
return false;
return 4 /* NONE */;
default:
break;
}
}
} catch (e) {
return;
return 3 /* PEE_UNDEFINED */;
} finally {
reader.releaseLock();
}
return 3 /* PEE_UNDEFINED */;
};
var png_default = {
extract,
@ -11263,10 +11264,10 @@
const embed = chunks.findIndex((e) => e.name == "TagName" && e.type == "8" && e.value == "COOM");
const cl = chunks.find((e) => e.name == "Cluster");
if (cl && embed == -1)
return false;
return 4 /* NONE */;
if (embed == -1)
return;
return true;
return 3 /* PEE_UNDEFINED */;
return 0 /* HAS_PEE */;
};
var webm_default = {
extract: extract2,
@ -11386,12 +11387,12 @@
end += v;
}
} else {
return true;
return 0 /* HAS_PEE */;
}
}
if (end >= gif.byteLength)
return;
return false;
return 3 /* PEE_UNDEFINED */;
return 4 /* NONE */;
};
var gif_default = {
extract: extract3,
@ -11510,7 +11511,7 @@
return cache[b.domain][hex];
const res = await GM_fetch(`https://${b.domain}${b.endpoint}${hex}`);
const pres = await res.json();
const tran = b.quirks(pres).filter((e) => !e.tags.some((e2) => black.has(e2)));
const tran = b.quirks(pres);
if (!(b.domain in cache))
cache[b.domain] = {};
cache[b.domain][hex] = tran;
@ -11531,24 +11532,29 @@
break;
}
}
let cachedFile;
let cachedPrev;
let cachedFull;
const prev = result[0].preview_url;
const full = result[0].full_url;
return {
source: result[0].source,
page: { title: booru, url: result[0].page },
filename: fn.substring(0, 33) + result[0].ext,
thumbnail: await (await GM_fetch(prev || full)).arrayBuffer(),
thumbnail: async () => {
if (!cachedPrev)
cachedPrev = await (await GM_fetch(prev || full)).arrayBuffer();
return cachedPrev;
},
data: async (lsn) => {
if (!cachedFile)
cachedFile = await (await GM_fetch(full || prev, void 0, lsn)).arrayBuffer();
return cachedFile;
if (!cachedFull)
cachedFull = await (await GM_fetch(full || prev, void 0, lsn)).arrayBuffer();
return cachedFull;
}
};
};
var has_embed4 = async (b, fn) => {
if (Buffer2.from(fn, "hex").equals(b))
return false;
return 4 /* NONE */;
let result = void 0;
for (const e of Object.values(boorus)) {
if (e.disabled)
@ -11558,7 +11564,13 @@
if (result.length)
break;
}
return result && result.length != 0;
if (result && result.length !== 0) {
const filtered = result.filter((e) => !e.tags.some((e2) => black.has(e2)));
if (filtered.length > 0)
return 1 /* HAS_TE */;
return 2 /* TE_BLACKLISTED */;
}
return 4 /* NONE */;
};
var thirdeye_default = {
skip: true,
@ -14985,6 +14997,7 @@
attr(div0, "class", "place svelte-yvh28x");
toggle_class(div0, "contract", ctx[6]);
attr(div1, "class", "hoverer svelte-yvh28x");
attr(div1, "id", "ihover");
toggle_class(div1, "visible", ctx[7] && ctx[6]);
toggle_class(div1, "unzipping", ctx[16]);
},
@ -15342,7 +15355,7 @@
}
function create_fragment6(ctx) {
let if_block_anchor;
let if_block = (!ctx[18].eye || ctx[15]) && create_if_block5(ctx);
let if_block = !ctx[0].isBlacklisted && (!ctx[18].eye || ctx[15]) && create_if_block5(ctx);
return {
c() {
if (if_block)
@ -15355,7 +15368,7 @@
insert(target, if_block_anchor, anchor);
},
p(ctx2, dirty) {
if (!ctx2[18].eye || ctx2[15]) {
if (!ctx2[0].isBlacklisted && (!ctx2[18].eye || ctx2[15])) {
if (if_block) {
if_block.p(ctx2, dirty);
} else {
@ -15416,10 +15429,12 @@
return contracted;
}
beforeUpdate(async () => {
if (file?.isBlacklisted === true)
return;
if (settled)
return;
settled = true;
const thumb = file.thumbnail || file.data;
const thumb = file.thumbnail ? await file.thumbnail() : file.data;
const type = await fileTypeFromBuffer(thumb);
$$invalidate(5, url = URL.createObjectURL(new Blob([thumb], { type: type?.mime })));
if (!type)
@ -15450,7 +15465,7 @@
}
}, {
root: null,
rootMargin: "0px",
rootMargin: "1000px",
threshold: 0.01
});
obs.observe(place);
@ -15459,6 +15474,8 @@
let unzipping = false;
let progress = [0, 0];
async function unzip() {
if (file?.isBlacklisted === true)
return;
if (!file.thumbnail)
return;
if (unzipping)
@ -15486,6 +15503,8 @@
}
}
async function bepis(ev) {
if (file?.isBlacklisted === true)
return;
if ($appState.isCatalog)
return;
if (ev.button == 0) {
@ -15539,6 +15558,8 @@
$$invalidate(10, hoverElem.style.height = `${dims[1]}px`, hoverElem);
}
async function hoverStart(ev) {
if (file?.isBlacklisted === true)
return;
if ($settings.dh)
return;
if (file.thumbnail && !furl) {
@ -15899,7 +15920,7 @@
// src/EyeButton.svelte
init_esbuild_inject();
function add_css7(target) {
append_styles(target, "svelte-jnv079", ".clickable.svelte-jnv079{cursor:pointer;margin-left:2px;margin-right:2px;text-decoration:underline}.clickable.svelte-jnv079:hover{text-shadow:0 0 4px palevioletred}");
append_styles(target, "svelte-1bbaka0", ".clickable.svelte-1bbaka0{cursor:pointer;margin-left:2px;margin-right:2px}.clickable.svelte-1bbaka0:hover{text-shadow:0 0 4px palevioletred}");
}
function get_each_context4(ctx, list, i) {
const child_ctx = ctx.slice();
@ -15913,7 +15934,7 @@
return {
c() {
span = element("span");
attr(span, "class", "fa clickable svelte-jnv079");
attr(span, "class", "fa clickable svelte-1bbaka0");
toggle_class(span, "fa-eye", !ctx[3]);
toggle_class(span, "fa-eye-slash", ctx[3]);
},
@ -15950,7 +15971,7 @@
t = text("Source");
attr(a, "href", a_href_value = ctx[11].source);
attr(a, "target", "_blank");
attr(a, "class", "clickable svelte-jnv079");
attr(a, "class", "clickable svelte-1bbaka0");
},
m(target, anchor) {
insert(target, a, anchor);
@ -15978,7 +15999,7 @@
t = text(t_value);
attr(a, "href", a_href_value = ctx[11].page.url);
attr(a, "target", "_blank");
attr(a, "class", "clickable svelte-jnv079");
attr(a, "class", "clickable svelte-1bbaka0");
},
m(target, anchor) {
insert(target, a, anchor);
@ -16006,7 +16027,7 @@
a = element("a");
a.textContent = "[PEE contract]";
attr(a, "alt", "By clicking this you agree to stay hydrated");
attr(a, "class", "clickable svelte-jnv079");
attr(a, "class", "clickable svelte-1bbaka0");
},
m(target, anchor) {
insert(target, a, anchor);
@ -16053,7 +16074,7 @@
if_block2.c();
if_block2_anchor = empty();
attr(span, "title", span_title_value = ctx[11].filename);
attr(span, "class", "fa fa-download clickable svelte-jnv079");
attr(span, "class", "fa fa-download clickable svelte-1bbaka0");
},
m(target, anchor) {
insert(target, span, anchor);
@ -16293,6 +16314,14 @@
var EyeButton_default = EyeButton;
// src/main.ts
var EMBED_ENUM = /* @__PURE__ */ ((EMBED_ENUM2) => {
EMBED_ENUM2[EMBED_ENUM2["HAS_PEE"] = 0] = "HAS_PEE";
EMBED_ENUM2[EMBED_ENUM2["HAS_TE"] = 1] = "HAS_TE";
EMBED_ENUM2[EMBED_ENUM2["TE_BLACKLISTED"] = 2] = "TE_BLACKLISTED";
EMBED_ENUM2[EMBED_ENUM2["PEE_UNDEFINED"] = 3] = "PEE_UNDEFINED";
EMBED_ENUM2[EMBED_ENUM2["NONE"] = 4] = "NONE";
return EMBED_ENUM2;
})(EMBED_ENUM || {});
var csettings;
var processors = [thirdeye_default, png_default, webm_default, gif_default];
var cappState;
@ -16336,8 +16365,9 @@
return Promise.all(processors.filter((e) => e.match(fn)).map(async (proc) => {
if (proc.skip) {
const md5 = import_buffer4.Buffer.from(hex, "base64");
if (await proc.has_embed(md5, fn) === true)
return [await proc.extract(md5, fn), true];
const embed_enum2 = await proc.has_embed(md5, fn);
if (embed_enum2 === 1 /* HAS_TE */ || embed_enum2 === 2 /* TE_BLACKLISTED */)
return [await proc.extract(md5, fn), embed_enum2];
return;
}
const iter = streamRemote(src);
@ -16346,6 +16376,7 @@
let cumul = import_buffer4.Buffer.alloc(0);
let found;
let chunk = { done: true };
let embed_enum;
do {
const { value, done } = await iter.next(found === false);
if (done) {
@ -16355,13 +16386,14 @@
}
if (!done)
cumul = import_buffer4.Buffer.concat([cumul, value]);
found = await proc.has_embed(cumul);
embed_enum = await proc.has_embed(cumul);
found = embed_enum === 0 /* HAS_PEE */;
} while (found !== false && !chunk.done);
await iter.next(true);
if (found === false) {
return;
}
return [await proc.extract(cumul), false];
return [await proc.extract(cumul), embed_enum];
}));
};
var textToElement = (s) => document.createRange().createContextualFragment(s).children[0];
@ -16374,7 +16406,15 @@
res2 = res2?.filter((e) => e);
if (!res2 || res2.length == 0)
return;
processAttachments(post, res2?.filter((e) => e));
processAttachments(post, res2?.filter((e) => {
if (!e)
return;
if (e[1] === 2 /* TE_BLACKLISTED */) {
e[0].isBlacklisted = true;
post.querySelector(".reply")?.classList.add("hasblack");
}
return e;
}));
};
var startup = async () => {
if (typeof window["FCX"] != "undefined")
@ -16504,10 +16544,10 @@
document.documentElement.insertBefore(customStyles, null);
function processAttachments(post, ress) {
const replyBox = post.querySelector(".post");
const external = ress[0][1];
if (external)
const embed_enum = ress[0][1];
if (embed_enum === 1 /* HAS_TE */)
replyBox?.classList.add("hasext");
else
else if (embed_enum === 0 /* HAS_PEE */)
replyBox?.classList.add("hasembed");
if (ress.length > 1)
replyBox?.classList.add("hasmultiple");

22
src/Embedding.svelte

@ -2,12 +2,13 @@
import { fileTypeFromBuffer } from 'file-type'
import { settings, appState } from './stores'
import { beforeUpdate, tick } from 'svelte'
import type {EmbeddedFile} from './main';
import type { EmbeddedFile } from './main.js';
import { createEventDispatcher } from 'svelte';
export const dispatch = createEventDispatcher();
export let file: EmbeddedFile
// export let embed_enum: EMBED_ENUM = EMBED_ENUM.NONE;
let isVideo = false
let isImage = false
let isAudio = false
@ -28,7 +29,7 @@
let visible = false;
export const isNotChrome = !navigator.userAgent.includes("Chrome/");
export let id = '';
export let id = '';
document.addEventListener("reveal", (e: any) => {
if (e.detail.id == id)
visible = !visible;
@ -39,10 +40,11 @@
}
beforeUpdate(async () => {
if(file?.isBlacklisted === true) return
if (settled) return
settled = true
const thumb = file.thumbnail || file.data;
const thumb = file.thumbnail ? (await file.thumbnail()) : file.data;
const type = await fileTypeFromBuffer(thumb);
url = URL.createObjectURL(new Blob([thumb], { type: type?.mime }))
if (!type)
@ -68,11 +70,11 @@
if ($settings.prev) {
let obs = new IntersectionObserver((entries, obs) => {
for(const item of entries) {
if(!item.isIntersecting) continue
if(!item.isIntersecting) continue;
unzip();
obs.unobserve(place);
}
}, {root:null, rootMargin: '0px', threshold: 0.01});
}, {root:null, rootMargin: '1000px', threshold: 0.01});
obs.observe(place);
}
});
@ -80,6 +82,7 @@
let unzipping = false;
let progress = [0, 0]
async function unzip() {
if(file?.isBlacklisted === true) return
if (!file.thumbnail)
return;
if (unzipping)
@ -118,6 +121,7 @@
}
export async function bepis(ev: MouseEvent) {
if (file?.isBlacklisted === true) return
if ($appState.isCatalog) return;
if (ev.button == 0) {
@ -175,9 +179,10 @@
}
async function hoverStart(ev?: MouseEvent) {
if (file?.isBlacklisted === true) return
if ($settings.dh)return;
if (file.thumbnail && !furl) {
unzip();
unzip();
}
if (!isImage && !isVideo) return
@ -242,7 +247,7 @@
}
</script>
{#if !$settings.eye || visible}
{#if !file.isBlacklisted && (!$settings.eye || visible)}
<!-- svelte-ignore a11y-mouse-events-have-key-events -->
<div
class:contract={contracted}
@ -282,6 +287,7 @@
class:visible={hovering && contracted}
class:unzipping
class="hoverer"
id="ihover"
>
{#if unzipping}<span class="progress">[{progress[0]} / {progress[1]}]</span
>{/if}

4
src/Embeddings.svelte

@ -1,5 +1,5 @@
<script lang="ts">
import type { EmbeddedFile } from './main';
import type { EmbeddedFile } from './main.js';
import { createEventDispatcher } from 'svelte';
import Embedding from './Embedding.svelte';
@ -7,6 +7,7 @@
export let files: EmbeddedFile[]
export let id: string = '';
// export let embed_enums: EMBED_ENUM[] = [EMBED_ENUM.NONE, EMBED_ENUM.NONE];
let children: {[k in number]: Embedding}= {}
export async function bepis(ev: MouseEvent) {
@ -15,6 +16,7 @@
}
</script>
<!-- embed_enum={embed_enums[i]} -->
{#each files as file, i}
<Embedding on:fileinfo bind:this={children[i]} {id} {file} />
{/each}

10
src/gif.ts

@ -1,5 +1,5 @@
import { Buffer } from "buffer";
import type { EmbeddedFile, ImageProcessor } from "./main";
import { EmbeddedFile, EMBED_ENUM, ImageProcessor } from "./main";
import { BufferWriteStream } from "./png";
const netscape = Buffer.from("!\xFF\x0BNETSCAPE2.0", 'ascii');
@ -106,7 +106,7 @@ const inject = async (container: File, inj: File) => {
return extract();
};
const has_embed = (gif: Buffer) => {
const has_embed = (gif: Buffer) : EMBED_ENUM => {
const field = gif.readUInt8(10);
const gcte = !!(field & (1 << 7));
let end = 13;
@ -125,12 +125,12 @@ const has_embed = (gif: Buffer) => {
end += v;
}
} else {
return true;
return EMBED_ENUM.HAS_PEE;
}
}
if (end >= gif.byteLength)
return; // Don't know yet, need more to decide.
return false; // no more extension blocks, so definite no
return EMBED_ENUM.PEE_UNDEFINED; // Don't know yet, need more to decide.
return EMBED_ENUM.NONE; // no more extension blocks, so definite no
};
export default {

29
src/global.css

@ -10,6 +10,13 @@
cursor: pointer;
}
#delform .postContainer>div.hasblack {
border-right: 3px dashed black !important;
}
.hasblack.catalog-post {
border: 3px dashed black !important;
}
#delform .postContainer>div.hasembed {
border-right: 3px dashed deeppink !important;
}
@ -22,13 +29,15 @@
border-right: 3px dashed goldenrod !important;
}
.hasext.catalog-post {
border: 3px dashed goldenrod !important;
}
#delform .postContainer>div.hasmultiple {
border-right: 3px dashed cornflowerblue !important;
}
.hasext.catalog-post {
border: 3px dashed goldenrod !important;
div.hasmultiple.catalog-post {
border-right: 3px dashed cornflowerblue !important;
}
.expanded-image>.post>.file .fileThumb>img[data-md5] {
@ -47,19 +56,23 @@
pointer-events: none;
}
div.hasemb .catalog-host img {
#delform div.hasemb .catalog-host .place video,
#delform div.hasemb .catalog-host .place img {
border: 1px solid deeppink;
}
div.hasext .catalog-host img {
#delform div.hasext .catalog-host .place video,
#delform div.hasext .catalog-host .place img {
border: 1px solid goldenrod;
}
div.hasmultiple .catalog-host img {
#delform .div.hasmultiple .catalog-host video,
#delform .div.hasmultiple .catalog-host img {
border: 1px solid cornflowerblue;
}
.catalog-host img {
#delform .catalog-host .place video,
#delform .catalog-host .place img {
position: absolute;
top: -5px;
right: 0px;

55
src/main.ts

@ -16,10 +16,17 @@ import SettingsButton from './SettingsButton.svelte';
import Embeddings from './Embeddings.svelte';
import EyeButton from './EyeButton.svelte';
export enum EMBED_ENUM {
HAS_PEE,
HAS_TE,
TE_BLACKLISTED,
PEE_UNDEFINED,
NONE,
}
export interface ImageProcessor {
skip?: true;
match(fn: string): boolean;
has_embed(b: Buffer, fn?: string): boolean | Promise<boolean>;
has_embed(b: Buffer, fn?: string): EMBED_ENUM | Promise<EMBED_ENUM>;
extract(b: Buffer, fn?: string): EmbeddedFile | Promise<EmbeddedFile>;
inject?(b: File, c: File): Buffer | Promise<Buffer>;
}
@ -68,14 +75,17 @@ async function* streamRemote(url: string, chunkSize = 72 * 1024, fetchRestOnNonC
}
type EmbeddedFileWithPreview = {
isBlacklisted?: boolean; // third-eye only
page?: { title: string, url: string }; // can be a booru page
source?: string; // can be like a twitter post this was posted in originally
thumbnail: Buffer;
filename: string;
thumbnail: () => Promise<Buffer>;
data: (lisn?: EventTarget) => Promise<Buffer>;
};
type EmbeddedFileWithoutPreview = {
isBlacklisted: boolean; // third-eye only
page: undefined;
source: undefined;
thumbnail: undefined;
@ -85,22 +95,24 @@ type EmbeddedFileWithoutPreview = {
export type EmbeddedFile = EmbeddedFileWithPreview | EmbeddedFileWithoutPreview;
const processImage = async (src: string, fn: string, hex: string): Promise<([EmbeddedFile, boolean] | undefined)[]> => {
const processImage = async (src: string, fn: string, hex: string): Promise<([EmbeddedFile, Number] | undefined)[]> => {
return Promise.all(processors.filter(e => e.match(fn)).map(async proc => {
if (proc.skip) {
if (proc.skip) { /* third-eye */
// skip file downloading, file is referenced from the filename
// basically does things like filtering out blacklisted tags
const md5 = Buffer.from(hex, 'base64');
if (await proc.has_embed(md5, fn) === true)
return [await proc.extract(md5, fn), true] as [EmbeddedFile, boolean];
const embed_enum = await proc.has_embed(md5, fn)
if (embed_enum === EMBED_ENUM.HAS_TE || embed_enum === EMBED_ENUM.TE_BLACKLISTED)
return [await proc.extract(md5, fn), embed_enum] as [EmbeddedFile, Number];
return;
}
const iter = streamRemote(src);
const iter = streamRemote(src); /* media-embed */
if (!iter)
return;
let cumul = Buffer.alloc(0);
let found: boolean | undefined;
let chunk: ReadableStreamDefaultReadResult<Buffer> = { done: true };
let embed_enum: EMBED_ENUM;
do {
const { value, done } = await iter.next(found === false);
if (done) {
@ -110,14 +122,16 @@ const processImage = async (src: string, fn: string, hex: string): Promise<([Emb
}
if (!done)
cumul = Buffer.concat([cumul, value!]);
found = await proc.has_embed(cumul);
embed_enum = await proc.has_embed(cumul);
found = (embed_enum === EMBED_ENUM.HAS_PEE);
} while (found !== false && !chunk.done);
await iter.next(true);
if (found === false) {
//console.log(`Gave up on ${src} after downloading ${cumul.byteLength} bytes...`);
return;
}
return [await proc.extract(cumul), false] as [EmbeddedFile, boolean];
return [await proc.extract(cumul), embed_enum] as [EmbeddedFile, EMBED_ENUM];
// return [await proc.extract(cumul), EMBED_ENUM.HAS_PEE] as [EmbeddedFile, EMBED_ENUM];
}));
};
@ -135,7 +149,16 @@ const processPost = async (post: HTMLDivElement) => {
res2 = res2?.filter(e => e);
if (!res2 || res2.length == 0)
return;
processAttachments(post, res2?.filter(e => e) as [EmbeddedFile, boolean][]);
// processAttachments(post, res2?.filter(e => e) as [EmbeddedFile, boolean][]);
processAttachments(post, res2?.filter(e => {
if(!e) return;
if(e[1] === EMBED_ENUM.TE_BLACKLISTED){
e[0].isBlacklisted = true;
post.querySelector('.reply')?.classList.add('hasblack');
}
return e;
}) as [EmbeddedFile, EMBED_ENUM][]);
};
const startup = async () => {
@ -300,12 +323,12 @@ customStyles.appendChild(document.createTextNode(globalCss));
document.documentElement.insertBefore(customStyles, null);
function processAttachments(post: HTMLDivElement, ress: [EmbeddedFile, boolean][]) {
function processAttachments(post: HTMLDivElement, ress: [EmbeddedFile, EMBED_ENUM][]) {
const replyBox = post.querySelector('.post');
const external = ress[0][1];
if (external)
const embed_enum : EMBED_ENUM = ress[0][1];
if (embed_enum === EMBED_ENUM.HAS_TE)
replyBox?.classList.add('hasext');
else
else if (embed_enum === EMBED_ENUM.HAS_PEE)
replyBox?.classList.add('hasembed');
if (ress.length > 1)
replyBox?.classList.add('hasmultiple');
@ -343,6 +366,7 @@ function processAttachments(post: HTMLDivElement, ress: [EmbeddedFile, boolean][
target: imgcont,
props: {
files: ress.map(e => e[0]),
// embed_enum: ress.map(e => e[1]),
id: '' + id
}
});
@ -365,7 +389,8 @@ function processAttachments(post: HTMLDivElement, ress: [EmbeddedFile, boolean][
const emb = new Embeddings({
target: imgcont,
props: {
files: ress.map(e => e[0])
files: ress.map(e => e[0]),
// embed_enum: ress.map(e => e[1]),
}
});
if (!ahem)

11
src/png.ts

@ -1,6 +1,6 @@
import { buf } from "crc-32";
import { Buffer } from "buffer";
import type { ImageProcessor } from "./main";
import { EMBED_ENUM, ImageProcessor } from "./main";
type PNGChunk = [string, Buffer, number, number];
@ -168,7 +168,7 @@ const inject = async (container: File, inj: File) => {
return extract();
};
const has_embed = async (png: Buffer) => {
const has_embed = async (png: Buffer) : Promise<EMBED_ENUM> => {
const reader = BufferReadStream(png).getReader();
const sneed = new PNGDecoder(reader);
try {
@ -179,12 +179,12 @@ const has_embed = async (png: Buffer) => {
case 'tEXt':
buff = chunk;
if (buff.slice(4, 4 + CUM0.length).equals(CUM0)) {
return true;
return EMBED_ENUM.HAS_PEE;
} break;
case 'IDAT':
// eslint-disable-next-line no-fallthrough
case 'IEND':
return false; // Didn't find tExt Chunk; Definite no
return EMBED_ENUM.NONE; // Didn't find tExt Chunk; Definite no
// eslint-disable-next-line no-fallthrough
default:
break;
@ -192,10 +192,11 @@ const has_embed = async (png: Buffer) => {
}
// stream ended on chunk boundary, so no unexpected EOF was fired, need more data anyway
} catch (e) {
return; // possibly unexpected EOF, need more data to decide
return EMBED_ENUM.PEE_UNDEFINED; // possibly unexpected EOF, need more data to decide
} finally {
reader.releaseLock();
}
return EMBED_ENUM.PEE_UNDEFINED
};
export default {

34
src/thirdeye.ts

@ -1,4 +1,4 @@
import type { EmbeddedFile, ImageProcessor } from "./main";
import { EmbeddedFile, EMBED_ENUM, ImageProcessor } from "./main";
import { GM_fetch } from "./requests";
import { localLoad, settings } from "./stores";
@ -69,7 +69,7 @@ const findFileFrom = async (b: Booru, hex: string, abort?: EventTarget) => {
const res = await GM_fetch(`https://${b.domain}${b.endpoint}${hex}`);
// might throw because some endpoint respond with invalid json when an error occurs
const pres = await res.json();
const tran = b.quirks(pres).filter(e => !e.tags.some(e => black.has(e)));
const tran = b.quirks(pres) //.filter(e => !e.tags.some(e => black.has(e)));
if (!(b.domain in cache))
cache[b.domain] = {};
cache[b.domain][hex] = tran;
@ -91,29 +91,35 @@ const extract = async (b: Buffer, fn?: string) => {
break;
}
}
let cachedFile: ArrayBuffer;
let cachedPrev: ArrayBuffer;
let cachedFull: ArrayBuffer;
const prev = result[0].preview_url;
const full = result[0].full_url;
return {
source: result[0].source,
page: { title: booru, url: result[0].page },
filename: fn!.substring(0, 33) + result[0].ext,
thumbnail: (await (await GM_fetch(prev || full)).arrayBuffer()), // prefer preview
thumbnail: async () => {
if (!cachedPrev)
cachedPrev = (await (await GM_fetch(prev || full)).arrayBuffer()); // prefer preview
return cachedPrev;
},
data: async (lsn) => {
if (!cachedFile)
cachedFile = (await (await GM_fetch(full || prev, undefined, lsn)).arrayBuffer()); // prefer full
return cachedFile;
if (!cachedFull)
cachedFull = (await (await GM_fetch(full || prev, undefined, lsn)).arrayBuffer()); // prefer full
return cachedFull;
}
} as EmbeddedFile;
};
const has_embed = async (b: Buffer, fn?: string) => {
const has_embed = async (b: Buffer, fn?: string) : Promise<EMBED_ENUM> => {
// It's not worth to bother skipping images with filenames that match their md5 because
// 4chan reencodes jpegs, which is well over half the files posted
// ok fine you autists
if (Buffer.from(fn!, 'hex').equals(b))
return false;
return EMBED_ENUM.NONE;
let result: BooruMatch[] | undefined = undefined;
for (const e of Object.values(boorus)) {
@ -124,7 +130,15 @@ const has_embed = async (b: Buffer, fn?: string) => {
if (result.length)
break;
}
return result && result.length != 0;
// return result && result.length != 0;
if(result && result.length !== 0){
const filtered = result.filter(e => !e.tags.some(e => black.has(e)))
if(filtered.length > 0)
return EMBED_ENUM.HAS_TE;
return EMBED_ENUM.TE_BLACKLISTED;
}
return EMBED_ENUM.NONE;
};
export default {

10
src/webm.ts

@ -1,6 +1,6 @@
import { Buffer } from "buffer";
import * as ebml from "ts-ebml";
import type { ImageProcessor } from "./main";
import { EMBED_ENUM, ImageProcessor } from "./main";
// unused, but will in case 4chan does file sig checks
//const password = Buffer.from("NOA");
@ -126,17 +126,17 @@ const extract = (webm: Buffer) => {
const inject = async (container: File, inj: File): Promise<Buffer> =>
embed(Buffer.from(await container.arrayBuffer()), Buffer.from(await inj.arrayBuffer()));
const has_embed = (webm: Buffer) => {
const has_embed = (webm: Buffer) : EMBED_ENUM => {
const dec = new ebml.Decoder();
const chunks = dec.decode(webm);
const embed = chunks.findIndex(e => e.name == "TagName" && e.type == '8' && e.value == "COOM");
const cl = chunks.find(e => e.name == "Cluster");
if (cl && embed == -1)
return false; // Tags appear before Cluster, so if we have a Cluster and no coomtag, then it's a definite no
return EMBED_ENUM.NONE; // Tags appear before Cluster, so if we have a Cluster and no coomtag, then it's a definite no
if (embed == -1) // Found no coomtag, but no cluster, so it might be further
return;
return true;
return EMBED_ENUM.PEE_UNDEFINED;
return EMBED_ENUM.HAS_PEE;
};
export default {

Loading…
Cancel
Save