Browse Source

- Split the EMBED enum and rely more on EmbeddedFile Instead.

- Added colors indicator to EyeButton and border of media
Anonymous 2 years ago
parent
commit
bc41574655
  1. 2
      extheader.js
  2. 4
      main.meta.js
  3. 131
      main.user.js
  4. 16
      src/Embedding.svelte
  5. 17
      src/EyeButton.svelte
  6. 10
      src/gif.ts
  7. 19
      src/global.css
  8. 54
      src/main.ts
  9. 12
      src/png.ts
  10. 12
      src/thirdeye.ts
  11. 10
      src/webm.ts

2
extheader.js

@ -4,7 +4,7 @@ let res = spawnSync("git", ["rev-list", "--count", "HEAD"]);
let rev = +res.stdout;
export const extheader = `// ==UserScript==
// @name PNGExtraEmbed
// @name PNGExtraEmbed2
// @namespace https://coom.tech/
// @version 0.${rev}
// @description uhh

4
main.meta.js

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

131
main.user.js

@ -1,7 +1,7 @@
// ==UserScript==
// @name PNGExtraEmbed
// @name PNGExtraEmbed2
// @namespace https://coom.tech/
// @version 0.113
// @version 0.114
// @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.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";
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\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/* .hasblack overrides the .hasembed (and .hasext) */\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\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.hasembed .catalog-host .place video,\r\n#delform div.hasembed .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,22 +11217,22 @@
case "tEXt":
buff = chunk;
if (buff.slice(4, 4 + CUM0.length).equals(CUM0)) {
return 0 /* HAS_PEE */;
return 0 /* SUCCESS */;
}
break;
case "IDAT":
case "IEND":
return 4 /* NONE */;
return 3 /* NONE */;
default:
break;
}
}
} catch (e) {
return 3 /* PEE_UNDEFINED */;
return 2 /* PEE_UNDEFINED */;
} finally {
reader.releaseLock();
}
return 3 /* PEE_UNDEFINED */;
return 2 /* PEE_UNDEFINED */;
};
var png_default = {
extract,
@ -11264,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 4 /* NONE */;
return 3 /* NONE */;
if (embed == -1)
return 3 /* PEE_UNDEFINED */;
return 0 /* HAS_PEE */;
return 2 /* PEE_UNDEFINED */;
return 0 /* SUCCESS */;
};
var webm_default = {
extract: extract2,
@ -11387,12 +11387,12 @@
end += v;
}
} else {
return 0 /* HAS_PEE */;
return 0 /* SUCCESS */;
}
}
if (end >= gif.byteLength)
return 3 /* PEE_UNDEFINED */;
return 4 /* NONE */;
return 2 /* PEE_UNDEFINED */;
return 3 /* NONE */;
};
var gif_default = {
extract: extract3,
@ -11554,7 +11554,7 @@
};
var has_embed4 = async (b, fn) => {
if (Buffer2.from(fn, "hex").equals(b))
return 4 /* NONE */;
return 3 /* NONE */;
let result = void 0;
for (const e of Object.values(boorus)) {
if (e.disabled)
@ -11567,10 +11567,10 @@
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 0 /* SUCCESS */;
return 1 /* TE_BLACKLISTED */;
}
return 4 /* NONE */;
return 3 /* NONE */;
};
var thirdeye_default = {
skip: true,
@ -14955,7 +14955,7 @@
// src/Embedding.svelte
init_esbuild_inject();
function add_css6(target) {
append_styles(target, "svelte-yvh28x", ".place.svelte-yvh28x.svelte-yvh28x{cursor:pointer;max-width:100vw;max-height:100vh}.unzipping.svelte-yvh28x>img.svelte-yvh28x{filter:brightness(0.5) blur(10px)}.progress.svelte-yvh28x.svelte-yvh28x{color:black;-webkit-text-stroke:0.7px white;font-weight:bold;left:50%;top:50%;font-size:larger;display:inline-block;position:absolute;z-index:10}.hoverer.svelte-yvh28x.svelte-yvh28x{display:none;position:fixed;pointer-events:none}.visible.svelte-yvh28x.svelte-yvh28x{display:block;z-index:9}.contract.svelte-yvh28x img.svelte-yvh28x,.contract.svelte-yvh28x video.svelte-yvh28x{max-width:125px !important;max-height:125px !important;width:auto;height:auto}.place.svelte-yvh28x:not(.contract) video.svelte-yvh28x,.place.svelte-yvh28x:not(.contract) img.svelte-yvh28x,.hoverer.svelte-yvh28x>video.svelte-yvh28x,.hoverer.svelte-yvh28x>img.svelte-yvh28x{max-width:100vw;max-height:100vh}");
append_styles(target, "svelte-16retph", ".place.svelte-16retph.svelte-16retph{cursor:pointer;max-width:100vw;max-height:100vh}.unzipping.svelte-16retph>img.svelte-16retph{filter:brightness(0.5) blur(10px)}.progress.svelte-16retph.svelte-16retph{color:black;-webkit-text-stroke:0.7px white;font-weight:bold;left:50%;top:50%;font-size:larger;display:inline-block;position:absolute;z-index:10}.hoverer.svelte-16retph.svelte-16retph{display:none;position:fixed;pointer-events:none}.visible.svelte-16retph.svelte-16retph{display:block;z-index:9}.contract.svelte-16retph img.svelte-16retph,.contract.svelte-16retph video.svelte-16retph{max-width:125px !important;max-height:125px !important;width:auto;height:auto}.place.svelte-16retph:not(.contract) video.svelte-16retph,.place.svelte-16retph:not(.contract) img.svelte-16retph,.hoverer.svelte-16retph>video.svelte-16retph,.hoverer.svelte-16retph>img.svelte-16retph{max-width:100vw;max-height:100vh}.place.hasembed.svelte-16retph video.svelte-16retph,.place.hasembed.svelte-16retph img.svelte-16retph{border:solid 1px deeppink\r\n }.place.hasext.svelte-16retph video.svelte-16retph,.place.hasext.svelte-16retph img.svelte-16retph{border:solid 1px goldenrod\r\n }");
}
function create_if_block5(ctx) {
let div0;
@ -14994,9 +14994,11 @@
t4 = space();
if (if_block5)
if_block5.c();
attr(div0, "class", "place svelte-yvh28x");
attr(div0, "class", "place svelte-16retph");
toggle_class(div0, "contract", ctx[6]);
attr(div1, "class", "hoverer svelte-yvh28x");
toggle_class(div0, "hasembed", ctx[0]?.embed_type === 0 /* THIRD_EYE */);
toggle_class(div0, "hasext", ctx[0]?.embed_type === 1 /* MEDIA_EMBED */);
attr(div1, "class", "hoverer svelte-16retph");
attr(div1, "id", "ihover");
toggle_class(div1, "visible", ctx[7] && ctx[6]);
toggle_class(div1, "unzipping", ctx[16]);
@ -15076,6 +15078,12 @@
if (dirty[0] & 64) {
toggle_class(div0, "contract", ctx2[6]);
}
if (dirty[0] & 1) {
toggle_class(div0, "hasembed", ctx2[0]?.embed_type === 0 /* THIRD_EYE */);
}
if (dirty[0] & 1) {
toggle_class(div0, "hasext", ctx2[0]?.embed_type === 1 /* MEDIA_EMBED */);
}
if (ctx2[16]) {
if (if_block3) {
if_block3.p(ctx2, dirty);
@ -15155,7 +15163,7 @@
attr(img, "alt", img_alt_value = ctx[0].filename);
if (!src_url_equal(img.src, img_src_value = ctx[14] || ctx[5]))
attr(img, "src", img_src_value);
attr(img, "class", "svelte-yvh28x");
attr(img, "class", "svelte-16retph");
},
m(target, anchor) {
insert(target, img, anchor);
@ -15233,7 +15241,7 @@
video.loop = video_loop_value = ctx[18].loop;
if (!src_url_equal(video.src, video_src_value = ctx[14] || ctx[5]))
attr(video, "src", video_src_value);
attr(video, "class", "svelte-yvh28x");
attr(video, "class", "svelte-16retph");
},
m(target, anchor) {
insert(target, video, anchor);
@ -15271,7 +15279,7 @@
t2 = text(" / ");
t3 = text(t3_value);
t4 = text("]");
attr(span, "class", "progress svelte-yvh28x");
attr(span, "class", "progress svelte-16retph");
},
m(target, anchor) {
insert(target, span, anchor);
@ -15303,7 +15311,7 @@
attr(img, "alt", img_alt_value = ctx[0].filename);
if (!src_url_equal(img.src, img_src_value = ctx[14] || ctx[5]))
attr(img, "src", img_src_value);
attr(img, "class", "svelte-yvh28x");
attr(img, "class", "svelte-16retph");
},
m(target, anchor) {
insert(target, img, anchor);
@ -15332,7 +15340,7 @@
video.loop = video_loop_value = ctx[18].loop;
if (!src_url_equal(video.src, video_src_value = ctx[14] || ctx[5]))
attr(video, "src", video_src_value);
attr(video, "class", "svelte-yvh28x");
attr(video, "class", "svelte-16retph");
},
m(target, anchor) {
insert(target, video, anchor);
@ -15920,7 +15928,7 @@
// src/EyeButton.svelte
init_esbuild_inject();
function add_css7(target) {
append_styles(target, "svelte-64lw6s", ".clickable.svelte-64lw6s{cursor:pointer;margin-left:5px}.clickable.svelte-64lw6s:hover{text-shadow:0 0 4px palevioletred}");
append_styles(target, "svelte-96kv3k", ".clickable.svelte-96kv3k{cursor:pointer;margin-left:5px}.clickable.svelte-96kv3k:hover{text-shadow:0 0 4px palevioletred}.clickable.hasembed.svelte-96kv3k{color:deeppink}.clickable.hasext.svelte-96kv3k{color:goldenrod}.clickable.hasblack.svelte-96kv3k{color:black}");
}
function get_each_context4(ctx, list, i) {
const child_ctx = ctx.slice();
@ -15934,7 +15942,7 @@
return {
c() {
span = element("span");
attr(span, "class", "fa clickable svelte-64lw6s");
attr(span, "class", "fa clickable svelte-96kv3k");
toggle_class(span, "fa-eye", !ctx[3]);
toggle_class(span, "fa-eye-slash", ctx[3]);
},
@ -15971,7 +15979,7 @@
t = text("Source");
attr(a, "href", a_href_value = ctx[11].source);
attr(a, "target", "_blank");
attr(a, "class", "clickable svelte-64lw6s");
attr(a, "class", "clickable svelte-96kv3k");
},
m(target, anchor) {
insert(target, a, anchor);
@ -15999,7 +16007,7 @@
t = text(t_value);
attr(a, "href", a_href_value = ctx[11].page.url);
attr(a, "target", "_blank");
attr(a, "class", "clickable svelte-64lw6s");
attr(a, "class", "clickable svelte-96kv3k");
},
m(target, anchor) {
insert(target, a, anchor);
@ -16027,7 +16035,7 @@
a = element("a");
a.textContent = "[PEE contract]";
attr(a, "alt", "By clicking this you agree to stay hydrated");
attr(a, "class", "clickable svelte-64lw6s");
attr(a, "class", "clickable svelte-96kv3k");
},
m(target, anchor) {
insert(target, a, anchor);
@ -16074,7 +16082,10 @@
if_block2.c();
if_block2_anchor = empty();
attr(span, "title", span_title_value = ctx[11].filename);
attr(span, "class", "fa fa-download clickable svelte-64lw6s");
attr(span, "class", "fa fa-download clickable svelte-96kv3k");
toggle_class(span, "hasembed", ctx[11]?.embed_type === 0 /* THIRD_EYE */);
toggle_class(span, "hasext", ctx[11]?.embed_type === 1 /* MEDIA_EMBED */);
toggle_class(span, "hasblack", ctx[11]?.isBlacklisted === true);
},
m(target, anchor) {
insert(target, span, anchor);
@ -16098,6 +16109,15 @@
if (dirty & 1 && span_title_value !== (span_title_value = ctx[11].filename)) {
attr(span, "title", span_title_value);
}
if (dirty & 1) {
toggle_class(span, "hasembed", ctx[11]?.embed_type === 0 /* THIRD_EYE */);
}
if (dirty & 1) {
toggle_class(span, "hasext", ctx[11]?.embed_type === 1 /* MEDIA_EMBED */);
}
if (dirty & 1) {
toggle_class(span, "hasblack", ctx[11]?.isBlacklisted === true);
}
if (ctx[11].source) {
if (if_block0) {
if_block0.p(ctx, dirty);
@ -16314,14 +16334,18 @@
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 EMBED_TYPES = /* @__PURE__ */ ((EMBED_TYPES2) => {
EMBED_TYPES2[EMBED_TYPES2["THIRD_EYE"] = 0] = "THIRD_EYE";
EMBED_TYPES2[EMBED_TYPES2["MEDIA_EMBED"] = 1] = "MEDIA_EMBED";
return EMBED_TYPES2;
})(EMBED_TYPES || {});
var EMBED_STATUS = /* @__PURE__ */ ((EMBED_STATUS2) => {
EMBED_STATUS2[EMBED_STATUS2["SUCCESS"] = 0] = "SUCCESS";
EMBED_STATUS2[EMBED_STATUS2["TE_BLACKLISTED"] = 1] = "TE_BLACKLISTED";
EMBED_STATUS2[EMBED_STATUS2["PEE_UNDEFINED"] = 2] = "PEE_UNDEFINED";
EMBED_STATUS2[EMBED_STATUS2["NONE"] = 3] = "NONE";
return EMBED_STATUS2;
})(EMBED_STATUS || {});
var csettings;
var processors = [thirdeye_default, png_default, webm_default, gif_default];
var cappState;
@ -16365,9 +16389,14 @@
return Promise.all(processors.filter((e) => e.match(fn)).map(async (proc) => {
if (proc.skip) {
const md5 = import_buffer4.Buffer.from(hex, "base64");
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];
const embed_status = await proc.has_embed(md5, fn);
if (embed_status === 0 /* SUCCESS */ || embed_status === 1 /* TE_BLACKLISTED */) {
const file2 = await proc.extract(md5, fn);
file2.embed_type = 0 /* THIRD_EYE */;
if (embed_status === 1 /* TE_BLACKLISTED */)
file2.isBlacklisted = true;
return [file2, 0 /* THIRD_EYE */];
}
return;
}
const iter = streamRemote(src);
@ -16376,7 +16405,6 @@
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) {
@ -16386,14 +16414,16 @@
}
if (!done)
cumul = import_buffer4.Buffer.concat([cumul, value]);
embed_enum = await proc.has_embed(cumul);
found = embed_enum === 0 /* HAS_PEE */;
const embed_status = await proc.has_embed(cumul);
found = embed_status === 0 /* SUCCESS */;
} while (found !== false && !chunk.done);
await iter.next(true);
if (found === false) {
return;
}
return [await proc.extract(cumul), embed_enum];
const file = await proc.extract(cumul);
file.embed_type = 1 /* MEDIA_EMBED */;
return [file, 1 /* MEDIA_EMBED */];
}));
};
var textToElement = (s) => document.createRange().createContextualFragment(s).children[0];
@ -16409,8 +16439,7 @@
processAttachments(post, res2?.filter((e) => {
if (!e)
return;
if (e[1] === 2 /* TE_BLACKLISTED */) {
e[0].isBlacklisted = true;
if (e[0]?.isBlacklisted === true) {
post.querySelector(".reply")?.classList.add("hasblack");
}
return e;
@ -16544,10 +16573,10 @@
document.documentElement.insertBefore(customStyles, null);
function processAttachments(post, ress) {
const replyBox = post.querySelector(".post");
const embed_enum = ress[0][1];
if (embed_enum === 1 /* HAS_TE */)
const embed_status = ress[0][1];
if (embed_status === 0 /* THIRD_EYE */)
replyBox?.classList.add("hasext");
else if (embed_enum === 0 /* HAS_PEE */)
else if (embed_status === 1 /* MEDIA_EMBED */)
replyBox?.classList.add("hasembed");
if (ress.length > 1)
replyBox?.classList.add("hasmultiple");

16
src/Embedding.svelte

@ -2,7 +2,7 @@
import { fileTypeFromBuffer } from 'file-type'
import { settings, appState } from './stores'
import { beforeUpdate, tick } from 'svelte'
import type { EmbeddedFile } from './main.js';
import { EmbeddedFile, EMBED_TYPES } from './main.js';
import { createEventDispatcher } from 'svelte';
export const dispatch = createEventDispatcher();
@ -249,9 +249,12 @@
{#if !file.isBlacklisted && (!$settings.eye || visible)}
<!-- svelte-ignore a11y-mouse-events-have-key-events -->
<div
<!-- class:hasblack={file?.isBlacklisted === true} -->
<div
class:contract={contracted}
class="place"
class:hasembed={file?.embed_type === EMBED_TYPES.THIRD_EYE}
class:hasext={file?.embed_type === EMBED_TYPES.MEDIA_EMBED}
on:click={e => e.preventDefault()}
on:auxclick={e => e.preventDefault()}
on:mousedown={bepis}
@ -352,4 +355,13 @@
max-width: 100vw;
max-height: 100vh;
}
.place.hasembed video,
.place.hasembed img {
border: solid 1px deeppink
}
.place.hasext video,
.place.hasext img {
border: solid 1px goldenrod
}
</style>

17
src/EyeButton.svelte

@ -3,7 +3,7 @@ import { fileTypeFromBuffer } from 'file-type';
import type Embedding from './Embedding.svelte';
import type Embeddings from './Embeddings.svelte';
import type { EmbeddedFile } from './main';
import { EmbeddedFile, EMBED_TYPES } from './main';
import { settings } from './stores'
@ -51,7 +51,11 @@ import type { EmbeddedFile } from './main';
title={file.filename}
on:click={() => downloadFile(file)}
class="fa fa-download clickable"
/>
class:hasembed={file?.embed_type === EMBED_TYPES.THIRD_EYE}
class:hasext={file?.embed_type === EMBED_TYPES.MEDIA_EMBED}
class:hasblack={file?.isBlacklisted === true}
/> <!-- hasblack is last for it to have greater weight than hasext, to override it -->
{#if file.source}
<!-- svelte-ignore a11y-missing-content -->
<a
@ -88,4 +92,13 @@ import type { EmbeddedFile } from './main';
.clickable:hover {
text-shadow: 0 0 4px palevioletred;
}
.clickable.hasembed {
color: deeppink;
}
.clickable.hasext {
color: goldenrod;
}
.clickable.hasblack {
color: black;
}
</style>

10
src/gif.ts

@ -1,5 +1,5 @@
import { Buffer } from "buffer";
import { EmbeddedFile, EMBED_ENUM, ImageProcessor } from "./main";
import { EmbeddedFile, EMBED_STATUS, 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) : EMBED_ENUM => {
const has_embed = (gif: Buffer) : EMBED_STATUS => {
const field = gif.readUInt8(10);
const gcte = !!(field & (1 << 7));
let end = 13;
@ -125,12 +125,12 @@ const has_embed = (gif: Buffer) : EMBED_ENUM => {
end += v;
}
} else {
return EMBED_ENUM.HAS_PEE;
return EMBED_STATUS.SUCCESS;
}
}
if (end >= gif.byteLength)
return EMBED_ENUM.PEE_UNDEFINED; // Don't know yet, need more to decide.
return EMBED_ENUM.NONE; // no more extension blocks, so definite no
return EMBED_STATUS.PEE_UNDEFINED; // Don't know yet, need more to decide.
return EMBED_STATUS.NONE; // no more extension blocks, so definite no
};
export default {

19
src/global.css

@ -10,12 +10,6 @@
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;
@ -33,6 +27,15 @@
border: 3px dashed goldenrod !important;
}
/* .hasblack overrides the .hasembed (and .hasext) */
#delform .postContainer>div.hasblack {
border-right: 3px dashed black !important;
}
.hasblack.catalog-post {
border: 3px dashed black !important;
}
#delform .postContainer>div.hasmultiple {
border-right: 3px dashed cornflowerblue !important;
}
@ -56,8 +59,8 @@ div.hasmultiple.catalog-post {
pointer-events: none;
}
#delform div.hasemb .catalog-host .place video,
#delform div.hasemb .catalog-host .place img {
#delform div.hasembed .catalog-host .place video,
#delform div.hasembed .catalog-host .place img {
border: 1px solid deeppink;
}

54
src/main.ts

@ -16,9 +16,12 @@ import SettingsButton from './SettingsButton.svelte';
import Embeddings from './Embeddings.svelte';
import EyeButton from './EyeButton.svelte';
export enum EMBED_ENUM {
HAS_PEE,
HAS_TE,
export enum EMBED_TYPES {
THIRD_EYE,
MEDIA_EMBED,
}
export enum EMBED_STATUS {
SUCCESS,
TE_BLACKLISTED,
PEE_UNDEFINED,
NONE,
@ -26,7 +29,7 @@ export enum EMBED_ENUM {
export interface ImageProcessor {
skip?: true;
match(fn: string): boolean;
has_embed(b: Buffer, fn?: string): EMBED_ENUM | Promise<EMBED_ENUM>;
has_embed(b: Buffer, fn?: string): EMBED_STATUS | Promise<EMBED_STATUS>;
extract(b: Buffer, fn?: string): EmbeddedFile | Promise<EmbeddedFile>;
inject?(b: File, c: File): Buffer | Promise<Buffer>;
}
@ -75,6 +78,7 @@ async function* streamRemote(url: string, chunkSize = 72 * 1024, fetchRestOnNonC
}
type EmbeddedFileWithPreview = {
embed_type: EMBED_TYPES;
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
@ -85,6 +89,7 @@ type EmbeddedFileWithPreview = {
type EmbeddedFileWithoutPreview = {
embed_type: EMBED_TYPES;
isBlacklisted: boolean; // third-eye only
page: undefined;
source: undefined;
@ -95,15 +100,20 @@ type EmbeddedFileWithoutPreview = {
export type EmbeddedFile = EmbeddedFileWithPreview | EmbeddedFileWithoutPreview;
const processImage = async (src: string, fn: string, hex: string): Promise<([EmbeddedFile, Number] | undefined)[]> => {
const processImage = async (src: string, fn: string, hex: string): Promise<([EmbeddedFile, EMBED_TYPES] | undefined)[]> => {
return Promise.all(processors.filter(e => e.match(fn)).map(async proc => {
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');
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];
const embed_status = await proc.has_embed(md5, fn)
if (embed_status === EMBED_STATUS.SUCCESS || embed_status === EMBED_STATUS.TE_BLACKLISTED){
const file = await proc.extract(md5, fn);
file.embed_type = EMBED_TYPES.THIRD_EYE;
if(embed_status === EMBED_STATUS.TE_BLACKLISTED)
file.isBlacklisted = true
return [file, EMBED_TYPES.THIRD_EYE] as [EmbeddedFile, EMBED_TYPES];
}
return;
}
const iter = streamRemote(src); /* media-embed */
@ -112,7 +122,6 @@ const processImage = async (src: string, fn: string, hex: string): Promise<([Emb
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) {
@ -122,16 +131,18 @@ const processImage = async (src: string, fn: string, hex: string): Promise<([Emb
}
if (!done)
cumul = Buffer.concat([cumul, value!]);
embed_enum = await proc.has_embed(cumul);
found = (embed_enum === EMBED_ENUM.HAS_PEE);
const embed_status = await proc.has_embed(cumul);
found = (embed_status === EMBED_STATUS.SUCCESS);
} 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), embed_enum] as [EmbeddedFile, EMBED_ENUM];
// return [await proc.extract(cumul), EMBED_ENUM.HAS_PEE] as [EmbeddedFile, EMBED_ENUM];
const file = await proc.extract(cumul);
file.embed_type = EMBED_TYPES.MEDIA_EMBED;
return [file, EMBED_TYPES.MEDIA_EMBED] as [EmbeddedFile, EMBED_TYPES];
// return [await proc.extract(cumul), EMBED_TYPES.HAS_PEE] as [EmbeddedFile, EMBED_TYPES];
}));
};
@ -153,12 +164,11 @@ const processPost = async (post: HTMLDivElement) => {
processAttachments(post, res2?.filter(e => {
if(!e) return;
if(e[1] === EMBED_ENUM.TE_BLACKLISTED){
e[0].isBlacklisted = true;
if(e[0]?.isBlacklisted === true){
post.querySelector('.reply')?.classList.add('hasblack');
}
return e;
}) as [EmbeddedFile, EMBED_ENUM][]);
}) as [EmbeddedFile, EMBED_TYPES][]);
};
const startup = async () => {
@ -323,12 +333,12 @@ customStyles.appendChild(document.createTextNode(globalCss));
document.documentElement.insertBefore(customStyles, null);
function processAttachments(post: HTMLDivElement, ress: [EmbeddedFile, EMBED_ENUM][]) {
function processAttachments(post: HTMLDivElement, ress: [EmbeddedFile, EMBED_TYPES][]) {
const replyBox = post.querySelector('.post');
const embed_enum : EMBED_ENUM = ress[0][1];
if (embed_enum === EMBED_ENUM.HAS_TE)
const embed_status = ress[0][1];
if (embed_status === EMBED_TYPES.THIRD_EYE)
replyBox?.classList.add('hasext');
else if (embed_enum === EMBED_ENUM.HAS_PEE)
else if (embed_status === EMBED_TYPES.MEDIA_EMBED)
replyBox?.classList.add('hasembed');
if (ress.length > 1)
replyBox?.classList.add('hasmultiple');
@ -366,7 +376,7 @@ function processAttachments(post: HTMLDivElement, ress: [EmbeddedFile, EMBED_ENU
target: imgcont,
props: {
files: ress.map(e => e[0]),
// embed_enum: ress.map(e => e[1]),
// EMBED_TYPES: ress.map(e => e[1]),
id: '' + id
}
});
@ -390,7 +400,7 @@ function processAttachments(post: HTMLDivElement, ress: [EmbeddedFile, EMBED_ENU
target: imgcont,
props: {
files: ress.map(e => e[0]),
// embed_enum: ress.map(e => e[1]),
// EMBED_TYPES: ress.map(e => e[1]),
}
});
if (!ahem)

12
src/png.ts

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

12
src/thirdeye.ts

@ -1,4 +1,4 @@
import { EmbeddedFile, EMBED_ENUM, ImageProcessor } from "./main";
import { EmbeddedFile, EMBED_STATUS, ImageProcessor } from "./main";
import { GM_fetch } from "./requests";
import { localLoad, settings } from "./stores";
@ -113,13 +113,13 @@ const extract = async (b: Buffer, fn?: string) => {
} as EmbeddedFile;
};
const has_embed = async (b: Buffer, fn?: string) : Promise<EMBED_ENUM> => {
const has_embed = async (b: Buffer, fn?: string) : Promise<EMBED_STATUS> => {
// 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 EMBED_ENUM.NONE;
return EMBED_STATUS.NONE;
let result: BooruMatch[] | undefined = undefined;
for (const e of Object.values(boorus)) {
@ -135,10 +135,10 @@ const has_embed = async (b: Buffer, fn?: string) : Promise<EMBED_ENUM> => {
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_STATUS.SUCCESS;
return EMBED_STATUS.TE_BLACKLISTED;
}
return EMBED_ENUM.NONE;
return EMBED_STATUS.NONE;
};
export default {

10
src/webm.ts

@ -1,6 +1,6 @@
import { Buffer } from "buffer";
import * as ebml from "ts-ebml";
import { EMBED_ENUM, ImageProcessor } from "./main";
import { EMBED_STATUS, 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) : EMBED_ENUM => {
const has_embed = (webm: Buffer) : EMBED_STATUS => {
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 EMBED_ENUM.NONE; // Tags appear before Cluster, so if we have a Cluster and no coomtag, then it's a definite no
return EMBED_STATUS.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 EMBED_ENUM.PEE_UNDEFINED;
return EMBED_ENUM.HAS_PEE;
return EMBED_STATUS.PEE_UNDEFINED;
return EMBED_STATUS.SUCCESS;
};
export default {

Loading…
Cancel
Save