mirror of
https://git.coom.tech/fuckjannies/lolipiss.git
synced 2024-06-24 11:42:36 +00:00
Merge remote-tracking branch 'coomdev/中出し' into feat-black-posts
This commit is contained in:
commit
c9b871cd4f
15
README.md
15
README.md
|
@ -1,3 +1,14 @@
|
||||||
|
Fork of PNG Extra Embedder (PEE)
|
||||||
|
========================
|
||||||
|
|
||||||
|
This is a very minor fork of https://git.coom.tech/coomdev/PEE.git.
|
||||||
|
|
||||||
|
There are two branches:
|
||||||
|
- feat-black-posts: which maintains an extra feature to highlight blacklisted posts and a general 'Tags' button.
|
||||||
|
- main branch: merges feat-black-posts and includes an extra IntersectionObserver to delay initial processing of
|
||||||
|
posts until they are in the users viewport.
|
||||||
|
|
||||||
|
|
||||||
PNG Extra Embedder (PEE)
|
PNG Extra Embedder (PEE)
|
||||||
========================
|
========================
|
||||||
|
|
||||||
|
@ -10,7 +21,7 @@ How to Install
|
||||||
Ok retard, listen up, just click on these things out of your grasp:
|
Ok retard, listen up, just click on these things out of your grasp:
|
||||||
- [Install ViolentMonkey](https://violentmonkey.github.io/get-it/) (it is preferable to TamperMonkey(closed source) and GreaseMonkey(abandonned shit))
|
- [Install ViolentMonkey](https://violentmonkey.github.io/get-it/) (it is preferable to TamperMonkey(closed source) and GreaseMonkey(abandonned shit))
|
||||||
- [Install 4chanX](https://www.4chan-x.net/builds/4chan-X.user.js)
|
- [Install 4chanX](https://www.4chan-x.net/builds/4chan-X.user.js)
|
||||||
- Use the prebuilt [main.user.js](https://shoujo.coom.tech/main.user.js)
|
- Use the prebuilt [main.user.js](https://git.coom.tech/coomdev/PEE/raw/branch/%E4%B8%AD%E5%87%BA%E3%81%97/main.user.js)
|
||||||
|
|
||||||
How to Build
|
How to Build
|
||||||
============
|
============
|
||||||
|
@ -111,4 +122,4 @@ Bugs
|
||||||
====
|
====
|
||||||
|
|
||||||
- more to come
|
- more to come
|
||||||
- not having a feature isn't a bug you retard
|
- not having a feature isn't a bug you retard
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name PNGExtraEmbed2
|
// @name PNGExtraEmbed2
|
||||||
// @namespace https://coom.tech/
|
// @namespace https://coom.tech/
|
||||||
// @version 0.120
|
// @version 0.126
|
||||||
// @description uhh
|
// @description uhh
|
||||||
// @author You
|
// @author You
|
||||||
// @match https://boards.4channel.org/*
|
// @match https://boards.4channel.org/*
|
||||||
|
|
314
main.user.js
314
main.user.js
|
@ -1,7 +1,7 @@
|
||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name PNGExtraEmbed2
|
// @name PNGExtraEmbed2
|
||||||
// @namespace https://coom.tech/
|
// @namespace https://coom.tech/
|
||||||
// @version 0.120
|
// @version 0.126
|
||||||
// @description uhh
|
// @description uhh
|
||||||
// @author You
|
// @author You
|
||||||
// @match https://boards.4channel.org/*
|
// @match https://boards.4channel.org/*
|
||||||
|
@ -11005,6 +11005,7 @@
|
||||||
sh: false,
|
sh: false,
|
||||||
ep: false,
|
ep: false,
|
||||||
expte: false,
|
expte: false,
|
||||||
|
hotlink: false,
|
||||||
conc: 8,
|
conc: 8,
|
||||||
ho: false,
|
ho: false,
|
||||||
blacklist: ["guro", "scat", "ryona", "gore"],
|
blacklist: ["guro", "scat", "ryona", "gore"],
|
||||||
|
@ -11105,25 +11106,6 @@
|
||||||
async dtor() {
|
async dtor() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
var PNGEncoder = class {
|
|
||||||
constructor(bytes) {
|
|
||||||
this.writer = bytes.getWriter();
|
|
||||||
this.writer.write(import_buffer.Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]));
|
|
||||||
}
|
|
||||||
async insertchunk(chunk) {
|
|
||||||
const b = import_buffer.Buffer.alloc(4);
|
|
||||||
b.writeInt32BE(chunk[1].length - 4, 0);
|
|
||||||
await this.writer.write(b);
|
|
||||||
const buff = chunk[1];
|
|
||||||
await this.writer.write(buff);
|
|
||||||
b.writeInt32BE((0, import_crc_32.buf)(buff), 0);
|
|
||||||
await this.writer.write(b);
|
|
||||||
}
|
|
||||||
async dtor() {
|
|
||||||
this.writer.releaseLock();
|
|
||||||
await this.writer.close();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var CUM0 = import_buffer.Buffer.from("CUM\x000");
|
var CUM0 = import_buffer.Buffer.from("CUM\x000");
|
||||||
var BufferReadStream = (b) => {
|
var BufferReadStream = (b) => {
|
||||||
const ret = new ReadableStream({
|
const ret = new ReadableStream({
|
||||||
|
@ -11173,43 +11155,6 @@
|
||||||
reader.releaseLock();
|
reader.releaseLock();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
var buildChunk = (tag, data) => {
|
|
||||||
const ret = import_buffer.Buffer.alloc(data.byteLength + 4);
|
|
||||||
ret.write(tag.slice(0, 4), 0);
|
|
||||||
data.copy(ret, 4);
|
|
||||||
return ret;
|
|
||||||
};
|
|
||||||
var BufferWriteStream = () => {
|
|
||||||
let b = import_buffer.Buffer.from([]);
|
|
||||||
const ret = new WritableStream({
|
|
||||||
write(chunk) {
|
|
||||||
b = import_buffer.Buffer.concat([b, chunk]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return [ret, () => b];
|
|
||||||
};
|
|
||||||
var inject = async (container, inj) => {
|
|
||||||
const [writestream, extract6] = BufferWriteStream();
|
|
||||||
const encoder = new PNGEncoder(writestream);
|
|
||||||
const decoder = new PNGDecoder(container.stream().getReader());
|
|
||||||
let magic2 = false;
|
|
||||||
for await (const [name, chunk, crc, offset] of decoder.chunks()) {
|
|
||||||
if (magic2 && name != "IDAT")
|
|
||||||
break;
|
|
||||||
if (!magic2 && name == "IDAT") {
|
|
||||||
await encoder.insertchunk(["tEXt", buildChunk("tEXt", CUM0), 0, 0]);
|
|
||||||
magic2 = true;
|
|
||||||
}
|
|
||||||
await encoder.insertchunk([name, chunk, crc, offset]);
|
|
||||||
}
|
|
||||||
const injb = import_buffer.Buffer.alloc(4 + inj.name.length + inj.size);
|
|
||||||
injb.writeInt32LE(inj.name.length, 0);
|
|
||||||
injb.write(inj.name, 4);
|
|
||||||
import_buffer.Buffer.from(await inj.arrayBuffer()).copy(injb, 4 + inj.name.length);
|
|
||||||
await encoder.insertchunk(["IDAT", buildChunk("IDAT", injb), 0, 0]);
|
|
||||||
await encoder.insertchunk(["IEND", buildChunk("IEND", import_buffer.Buffer.from([])), 0, 0]);
|
|
||||||
return extract6();
|
|
||||||
};
|
|
||||||
var has_embed = async (png) => {
|
var has_embed = async (png) => {
|
||||||
const reader = BufferReadStream(png).getReader();
|
const reader = BufferReadStream(png).getReader();
|
||||||
const sneed = new PNGDecoder(reader);
|
const sneed = new PNGDecoder(reader);
|
||||||
|
@ -11240,7 +11185,6 @@
|
||||||
var png_default = {
|
var png_default = {
|
||||||
extract,
|
extract,
|
||||||
has_embed,
|
has_embed,
|
||||||
inject,
|
|
||||||
match: (fn) => !!fn.match(/\.png$/)
|
match: (fn) => !!fn.match(/\.png$/)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -11332,7 +11276,7 @@
|
||||||
if (chk.type == "b" && chk.name == "TagBinary")
|
if (chk.type == "b" && chk.name == "TagBinary")
|
||||||
return [{ embed_type: 1 /* MEDIA_EMBED */, filename: "string", data: chk.data }];
|
return [{ embed_type: 1 /* MEDIA_EMBED */, filename: "string", data: chk.data }];
|
||||||
};
|
};
|
||||||
var inject2 = async (container, inj) => embed(import_buffer2.Buffer.from(await container.arrayBuffer()), import_buffer2.Buffer.from(await inj.arrayBuffer()));
|
var inject = async (container, [inj]) => embed(import_buffer2.Buffer.from(await container.arrayBuffer()), import_buffer2.Buffer.from(await inj.arrayBuffer()));
|
||||||
var has_embed2 = (webm) => {
|
var has_embed2 = (webm) => {
|
||||||
const dec = new ebml.Decoder();
|
const dec = new ebml.Decoder();
|
||||||
const chunks = dec.decode(webm);
|
const chunks = dec.decode(webm);
|
||||||
|
@ -11347,7 +11291,7 @@
|
||||||
var webm_default = {
|
var webm_default = {
|
||||||
extract: extract2,
|
extract: extract2,
|
||||||
has_embed: has_embed2,
|
has_embed: has_embed2,
|
||||||
inject: inject2,
|
inject,
|
||||||
match: (fn) => !!fn.match(/\.webm$/)
|
match: (fn) => !!fn.match(/\.webm$/)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -11399,53 +11343,6 @@
|
||||||
throw "Shouldn't happen";
|
throw "Shouldn't happen";
|
||||||
};
|
};
|
||||||
var extract3 = extractBuff;
|
var extract3 = extractBuff;
|
||||||
var write_data = async (writer, inj) => {
|
|
||||||
await writer.write(magic);
|
|
||||||
const byte = import_buffer3.Buffer.from([0]);
|
|
||||||
let size = inj.byteLength;
|
|
||||||
let ws;
|
|
||||||
let offset = 0;
|
|
||||||
while (size != 0) {
|
|
||||||
ws = size >= 255 ? 255 : size;
|
|
||||||
byte.writeUInt8(ws, 0);
|
|
||||||
await writer.write(byte);
|
|
||||||
await writer.write(inj.slice(offset, offset + ws));
|
|
||||||
size -= ws;
|
|
||||||
offset += ws;
|
|
||||||
}
|
|
||||||
byte.writeUInt8(0, 0);
|
|
||||||
await writer.write(byte);
|
|
||||||
};
|
|
||||||
var write_embedding = async (writer, inj) => {
|
|
||||||
const b = import_buffer3.Buffer.alloc(4);
|
|
||||||
b.writeInt32LE(inj.byteLength, 0);
|
|
||||||
await write_data(writer, b);
|
|
||||||
let size = inj.byteLength;
|
|
||||||
let offset = 0;
|
|
||||||
while (size != 0) {
|
|
||||||
const ws = size >= 3 << 13 ? 3 << 13 : size;
|
|
||||||
await write_data(writer, inj.slice(offset, offset + ws));
|
|
||||||
offset += ws;
|
|
||||||
size -= ws;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var inject3 = async (container, inj) => {
|
|
||||||
const [writestream, extract6] = BufferWriteStream();
|
|
||||||
const writer = writestream.getWriter();
|
|
||||||
const contbuff = import_buffer3.Buffer.from(await container.arrayBuffer());
|
|
||||||
debugger;
|
|
||||||
const field = contbuff.readUInt8(10);
|
|
||||||
const gcte = !!(field & 1 << 7);
|
|
||||||
let endo = 13;
|
|
||||||
if (gcte)
|
|
||||||
endo += 3 * (1 << (field & 7) + 1);
|
|
||||||
if (netscape.compare(contbuff, endo, endo + netscape.byteLength) == 0)
|
|
||||||
endo += 19;
|
|
||||||
await writer.write(contbuff.slice(0, endo));
|
|
||||||
await write_embedding(writer, import_buffer3.Buffer.from(await inj.arrayBuffer()));
|
|
||||||
await writer.write(contbuff.slice(endo));
|
|
||||||
return extract6();
|
|
||||||
};
|
|
||||||
var has_embed3 = (gif) => {
|
var has_embed3 = (gif) => {
|
||||||
const field = gif.readUInt8(10);
|
const field = gif.readUInt8(10);
|
||||||
const gcte = !!(field & 1 << 7);
|
const gcte = !!(field & 1 << 7);
|
||||||
|
@ -11473,7 +11370,6 @@
|
||||||
var gif_default = {
|
var gif_default = {
|
||||||
extract: extract3,
|
extract: extract3,
|
||||||
has_embed: has_embed3,
|
has_embed: has_embed3,
|
||||||
inject: inject3,
|
|
||||||
match: (fn) => !!fn.match(/\.gif$/)
|
match: (fn) => !!fn.match(/\.gif$/)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -11584,6 +11480,10 @@
|
||||||
|
|
||||||
// src/thirdeye.ts
|
// src/thirdeye.ts
|
||||||
var import_buffer4 = __toESM(require_buffer(), 1);
|
var import_buffer4 = __toESM(require_buffer(), 1);
|
||||||
|
var csettings2;
|
||||||
|
settings.subscribe((b) => {
|
||||||
|
csettings2 = b;
|
||||||
|
});
|
||||||
var gelquirk = (prefix) => (a) => (a.post || a).map((e) => ({
|
var gelquirk = (prefix) => (a) => (a.post || a).map((e) => ({
|
||||||
full_url: e.file_url,
|
full_url: e.file_url,
|
||||||
preview_url: e.preview_url || e.preview_url,
|
preview_url: e.preview_url || e.preview_url,
|
||||||
|
@ -11698,7 +11598,7 @@
|
||||||
cachedPrev = await (await GM_fetch(prev || full)).arrayBuffer();
|
cachedPrev = await (await GM_fetch(prev || full)).arrayBuffer();
|
||||||
return cachedPrev;
|
return cachedPrev;
|
||||||
},
|
},
|
||||||
data: async (lsn) => {
|
data: csettings2.hotlink ? full || prev : async (lsn) => {
|
||||||
if (!cachedFull)
|
if (!cachedFull)
|
||||||
cachedFull = await (await GM_fetch(full || prev, void 0, lsn)).arrayBuffer();
|
cachedFull = await (await GM_fetch(full || prev, void 0, lsn)).arrayBuffer();
|
||||||
return cachedFull;
|
return cachedFull;
|
||||||
|
@ -11739,6 +11639,10 @@
|
||||||
{ host: "Litter", prefix: "https://litter.catbox.moe/" },
|
{ host: "Litter", prefix: "https://litter.catbox.moe/" },
|
||||||
{ host: "Pomf", prefix: "https://a.pomf.cat/" }
|
{ host: "Pomf", prefix: "https://a.pomf.cat/" }
|
||||||
];
|
];
|
||||||
|
var csettings3;
|
||||||
|
settings.subscribe((b) => {
|
||||||
|
csettings3 = b;
|
||||||
|
});
|
||||||
var getExt = (fn) => {
|
var getExt = (fn) => {
|
||||||
const isDum = fn.match(/^([a-z0-9]{6}\.(?:jpe?g|png|webm|gif))/gi);
|
const isDum = fn.match(/^([a-z0-9]{6}\.(?:jpe?g|png|webm|gif))/gi);
|
||||||
const isB64 = fn.match(/^((?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=))?\.(gif|jpe?g|png|webm)/);
|
const isB64 = fn.match(/^((?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=))?\.(gif|jpe?g|png|webm)/);
|
||||||
|
@ -11767,7 +11671,7 @@
|
||||||
return [{
|
return [{
|
||||||
embed_type: 1 /* MEDIA_EMBED */,
|
embed_type: 1 /* MEDIA_EMBED */,
|
||||||
filename: ext,
|
filename: ext,
|
||||||
data: async (lsn) => {
|
data: csettings3.hotlink ? rsource : async (lsn) => {
|
||||||
try {
|
try {
|
||||||
return (await GM_fetch(rsource, void 0, lsn)).arrayBuffer();
|
return (await GM_fetch(rsource, void 0, lsn)).arrayBuffer();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -12097,14 +12001,14 @@
|
||||||
}
|
}
|
||||||
function get_each_context(ctx, list, i) {
|
function get_each_context(ctx, list, i) {
|
||||||
const child_ctx = ctx.slice();
|
const child_ctx = ctx.slice();
|
||||||
child_ctx[33] = list[i];
|
child_ctx[34] = list[i];
|
||||||
child_ctx[35] = i;
|
child_ctx[36] = i;
|
||||||
return child_ctx;
|
return child_ctx;
|
||||||
}
|
}
|
||||||
function get_each_context_1(ctx, list, i) {
|
function get_each_context_1(ctx, list, i) {
|
||||||
const child_ctx = ctx.slice();
|
const child_ctx = ctx.slice();
|
||||||
child_ctx[36] = list[i];
|
child_ctx[37] = list[i];
|
||||||
child_ctx[35] = i;
|
child_ctx[36] = i;
|
||||||
return child_ctx;
|
return child_ctx;
|
||||||
}
|
}
|
||||||
function create_if_block_1(ctx) {
|
function create_if_block_1(ctx) {
|
||||||
|
@ -12180,7 +12084,7 @@
|
||||||
$$scope: { ctx }
|
$$scope: { ctx }
|
||||||
};
|
};
|
||||||
dialog = new Dialog_default({ props: dialog_props });
|
dialog = new Dialog_default({ props: dialog_props });
|
||||||
ctx[29](dialog);
|
ctx[30](dialog);
|
||||||
let each_value = ctx[3].blacklist;
|
let each_value = ctx[3].blacklist;
|
||||||
let each_blocks = [];
|
let each_blocks = [];
|
||||||
for (let i = 0; i < each_value.length; i += 1) {
|
for (let i = 0; i < each_value.length; i += 1) {
|
||||||
|
@ -12259,9 +12163,9 @@
|
||||||
current = true;
|
current = true;
|
||||||
if (!mounted) {
|
if (!mounted) {
|
||||||
dispose = [
|
dispose = [
|
||||||
listen(input0, "change", ctx[20]),
|
listen(input0, "change", ctx[21]),
|
||||||
listen(button, "click", ctx[24]),
|
listen(button, "click", ctx[25]),
|
||||||
listen(input1, "keydown", ctx[31])
|
listen(input1, "keydown", ctx[32])
|
||||||
];
|
];
|
||||||
mounted = true;
|
mounted = true;
|
||||||
}
|
}
|
||||||
|
@ -12292,7 +12196,7 @@
|
||||||
check_outros();
|
check_outros();
|
||||||
}
|
}
|
||||||
const dialog_changes = {};
|
const dialog_changes = {};
|
||||||
if (dirty[0] & 1 | dirty[1] & 128) {
|
if (dirty[0] & 1 | dirty[1] & 256) {
|
||||||
dialog_changes.$$scope = { dirty, ctx: ctx2 };
|
dialog_changes.$$scope = { dirty, ctx: ctx2 };
|
||||||
}
|
}
|
||||||
dialog.$set(dialog_changes);
|
dialog.$set(dialog_changes);
|
||||||
|
@ -12360,7 +12264,7 @@
|
||||||
detach(button);
|
detach(button);
|
||||||
if (detaching)
|
if (detaching)
|
||||||
detach(t7);
|
detach(t7);
|
||||||
ctx[29](null);
|
ctx[30](null);
|
||||||
destroy_component(dialog, detaching);
|
destroy_component(dialog, detaching);
|
||||||
if (detaching)
|
if (detaching)
|
||||||
detach(t8);
|
detach(t8);
|
||||||
|
@ -12388,17 +12292,17 @@
|
||||||
let tag;
|
let tag;
|
||||||
let current;
|
let current;
|
||||||
function func(...args) {
|
function func(...args) {
|
||||||
return ctx[21](ctx[36], ...args);
|
return ctx[22](ctx[37], ...args);
|
||||||
}
|
}
|
||||||
function remove_handler() {
|
function remove_handler() {
|
||||||
return ctx[22](ctx[36]);
|
return ctx[23](ctx[37]);
|
||||||
}
|
}
|
||||||
function toggle_handler() {
|
function toggle_handler() {
|
||||||
return ctx[23](ctx[36]);
|
return ctx[24](ctx[37]);
|
||||||
}
|
}
|
||||||
tag = new Tag_default({
|
tag = new Tag_default({
|
||||||
props: {
|
props: {
|
||||||
tag: ctx[36].name,
|
tag: ctx[37].name,
|
||||||
toggleable: true,
|
toggleable: true,
|
||||||
toggled: !ctx[3].rsources.find(func)?.disabled
|
toggled: !ctx[3].rsources.find(func)?.disabled
|
||||||
}
|
}
|
||||||
|
@ -12417,7 +12321,7 @@
|
||||||
ctx = new_ctx;
|
ctx = new_ctx;
|
||||||
const tag_changes = {};
|
const tag_changes = {};
|
||||||
if (dirty[0] & 8)
|
if (dirty[0] & 8)
|
||||||
tag_changes.tag = ctx[36].name;
|
tag_changes.tag = ctx[37].name;
|
||||||
if (dirty[0] & 8)
|
if (dirty[0] & 8)
|
||||||
tag_changes.toggled = !ctx[3].rsources.find(func)?.disabled;
|
tag_changes.toggled = !ctx[3].rsources.find(func)?.disabled;
|
||||||
tag.$set(tag_changes);
|
tag.$set(tag_changes);
|
||||||
|
@ -12518,10 +12422,10 @@
|
||||||
append(div, button);
|
append(div, button);
|
||||||
if (!mounted) {
|
if (!mounted) {
|
||||||
dispose = [
|
dispose = [
|
||||||
listen(input0, "input", ctx[25]),
|
listen(input0, "input", ctx[26]),
|
||||||
listen(input1, "input", ctx[26]),
|
listen(input1, "input", ctx[27]),
|
||||||
listen(input2, "input", ctx[27]),
|
listen(input2, "input", ctx[28]),
|
||||||
listen(input3, "input", ctx[28]),
|
listen(input3, "input", ctx[29]),
|
||||||
listen(button, "click", ctx[4])
|
listen(button, "click", ctx[4])
|
||||||
];
|
];
|
||||||
mounted = true;
|
mounted = true;
|
||||||
|
@ -12553,9 +12457,9 @@
|
||||||
let tag;
|
let tag;
|
||||||
let current;
|
let current;
|
||||||
function toggle_handler_1() {
|
function toggle_handler_1() {
|
||||||
return ctx[30](ctx[33]);
|
return ctx[31](ctx[34]);
|
||||||
}
|
}
|
||||||
tag = new Tag_default({ props: { tag: ctx[33] } });
|
tag = new Tag_default({ props: { tag: ctx[34] } });
|
||||||
tag.$on("toggle", toggle_handler_1);
|
tag.$on("toggle", toggle_handler_1);
|
||||||
return {
|
return {
|
||||||
c() {
|
c() {
|
||||||
|
@ -12569,7 +12473,7 @@
|
||||||
ctx = new_ctx;
|
ctx = new_ctx;
|
||||||
const tag_changes = {};
|
const tag_changes = {};
|
||||||
if (dirty[0] & 8)
|
if (dirty[0] & 8)
|
||||||
tag_changes.tag = ctx[33];
|
tag_changes.tag = ctx[34];
|
||||||
tag.$set(tag_changes);
|
tag.$set(tag_changes);
|
||||||
},
|
},
|
||||||
i(local) {
|
i(local) {
|
||||||
|
@ -12634,12 +12538,16 @@
|
||||||
let label9;
|
let label9;
|
||||||
let input9;
|
let input9;
|
||||||
let t22;
|
let t22;
|
||||||
let a;
|
let t23;
|
||||||
let t24;
|
|
||||||
let label10;
|
let label10;
|
||||||
let input10;
|
let input10;
|
||||||
let t25;
|
let t24;
|
||||||
|
let a;
|
||||||
let t26;
|
let t26;
|
||||||
|
let label11;
|
||||||
|
let input11;
|
||||||
|
let t27;
|
||||||
|
let t28;
|
||||||
let current;
|
let current;
|
||||||
let mounted;
|
let mounted;
|
||||||
let dispose;
|
let dispose;
|
||||||
|
@ -12687,22 +12595,26 @@
|
||||||
t17 = space();
|
t17 = space();
|
||||||
label7 = element("label");
|
label7 = element("label");
|
||||||
input7 = element("input");
|
input7 = element("input");
|
||||||
t18 = text("\r\n Control audio on videos with mouse wheel.");
|
t18 = text("\r\n Hotlink content.");
|
||||||
t19 = space();
|
t19 = space();
|
||||||
label8 = element("label");
|
label8 = element("label");
|
||||||
input8 = element("input");
|
input8 = element("input");
|
||||||
t20 = text("\r\n Show Minimap");
|
t20 = text("\r\n Control audio on videos with mouse wheel.");
|
||||||
t21 = space();
|
t21 = space();
|
||||||
label9 = element("label");
|
label9 = element("label");
|
||||||
input9 = element("input");
|
input9 = element("input");
|
||||||
t22 = text("\r\n \r\n Disable embedded file preloading");
|
t22 = text("\r\n Show Minimap");
|
||||||
a = element("a");
|
t23 = space();
|
||||||
a.textContent = "?";
|
|
||||||
t24 = space();
|
|
||||||
label10 = element("label");
|
label10 = element("label");
|
||||||
input10 = element("input");
|
input10 = element("input");
|
||||||
t25 = text("\r\n Disable third-eye.");
|
t24 = text("\r\n \r\n Disable embedded file preloading");
|
||||||
|
a = element("a");
|
||||||
|
a.textContent = "?";
|
||||||
t26 = space();
|
t26 = space();
|
||||||
|
label11 = element("label");
|
||||||
|
input11 = element("input");
|
||||||
|
t27 = text("\r\n Disable third-eye.");
|
||||||
|
t28 = space();
|
||||||
if (if_block1)
|
if (if_block1)
|
||||||
if_block1.c();
|
if_block1.c();
|
||||||
attr(h1, "class", "svelte-14cwalg");
|
attr(h1, "class", "svelte-14cwalg");
|
||||||
|
@ -12717,8 +12629,9 @@
|
||||||
attr(input7, "type", "checkbox");
|
attr(input7, "type", "checkbox");
|
||||||
attr(input8, "type", "checkbox");
|
attr(input8, "type", "checkbox");
|
||||||
attr(input9, "type", "checkbox");
|
attr(input9, "type", "checkbox");
|
||||||
attr(a, "title", "You might still want to enable 'preload external files'");
|
|
||||||
attr(input10, "type", "checkbox");
|
attr(input10, "type", "checkbox");
|
||||||
|
attr(a, "title", "You might still want to enable 'preload external files'");
|
||||||
|
attr(input11, "type", "checkbox");
|
||||||
attr(div0, "class", "content svelte-14cwalg");
|
attr(div0, "class", "content svelte-14cwalg");
|
||||||
attr(div1, "class", "backpanel svelte-14cwalg");
|
attr(div1, "class", "backpanel svelte-14cwalg");
|
||||||
toggle_class(div1, "enabled", ctx[2]);
|
toggle_class(div1, "enabled", ctx[2]);
|
||||||
|
@ -12771,25 +12684,30 @@
|
||||||
append(div0, t17);
|
append(div0, t17);
|
||||||
append(div0, label7);
|
append(div0, label7);
|
||||||
append(label7, input7);
|
append(label7, input7);
|
||||||
input7.checked = ctx[3].ca;
|
input7.checked = ctx[3].hotlink;
|
||||||
append(label7, t18);
|
append(label7, t18);
|
||||||
append(div0, t19);
|
append(div0, t19);
|
||||||
append(div0, label8);
|
append(div0, label8);
|
||||||
append(label8, input8);
|
append(label8, input8);
|
||||||
input8.checked = ctx[3].sh;
|
input8.checked = ctx[3].ca;
|
||||||
append(label8, t20);
|
append(label8, t20);
|
||||||
append(div0, t21);
|
append(div0, t21);
|
||||||
append(div0, label9);
|
append(div0, label9);
|
||||||
append(label9, input9);
|
append(label9, input9);
|
||||||
input9.checked = ctx[3].ep;
|
input9.checked = ctx[3].sh;
|
||||||
append(label9, t22);
|
append(label9, t22);
|
||||||
append(label9, a);
|
append(div0, t23);
|
||||||
append(div0, t24);
|
|
||||||
append(div0, label10);
|
append(div0, label10);
|
||||||
append(label10, input10);
|
append(label10, input10);
|
||||||
input10.checked = ctx[3].te;
|
input10.checked = ctx[3].ep;
|
||||||
append(label10, t25);
|
append(label10, t24);
|
||||||
|
append(label10, a);
|
||||||
append(div0, t26);
|
append(div0, t26);
|
||||||
|
append(div0, label11);
|
||||||
|
append(label11, input11);
|
||||||
|
input11.checked = ctx[3].te;
|
||||||
|
append(label11, t27);
|
||||||
|
append(div0, t28);
|
||||||
if (if_block1)
|
if (if_block1)
|
||||||
if_block1.m(div0, null);
|
if_block1.m(div0, null);
|
||||||
current = true;
|
current = true;
|
||||||
|
@ -12805,7 +12723,8 @@
|
||||||
listen(input7, "change", ctx[16]),
|
listen(input7, "change", ctx[16]),
|
||||||
listen(input8, "change", ctx[17]),
|
listen(input8, "change", ctx[17]),
|
||||||
listen(input9, "change", ctx[18]),
|
listen(input9, "change", ctx[18]),
|
||||||
listen(input10, "change", ctx[19])
|
listen(input10, "change", ctx[19]),
|
||||||
|
listen(input11, "change", ctx[20])
|
||||||
];
|
];
|
||||||
mounted = true;
|
mounted = true;
|
||||||
}
|
}
|
||||||
|
@ -12845,16 +12764,19 @@
|
||||||
input6.checked = ctx2[3].prev;
|
input6.checked = ctx2[3].prev;
|
||||||
}
|
}
|
||||||
if (dirty[0] & 8) {
|
if (dirty[0] & 8) {
|
||||||
input7.checked = ctx2[3].ca;
|
input7.checked = ctx2[3].hotlink;
|
||||||
}
|
}
|
||||||
if (dirty[0] & 8) {
|
if (dirty[0] & 8) {
|
||||||
input8.checked = ctx2[3].sh;
|
input8.checked = ctx2[3].ca;
|
||||||
}
|
}
|
||||||
if (dirty[0] & 8) {
|
if (dirty[0] & 8) {
|
||||||
input9.checked = ctx2[3].ep;
|
input9.checked = ctx2[3].sh;
|
||||||
}
|
}
|
||||||
if (dirty[0] & 8) {
|
if (dirty[0] & 8) {
|
||||||
input10.checked = ctx2[3].te;
|
input10.checked = ctx2[3].ep;
|
||||||
|
}
|
||||||
|
if (dirty[0] & 8) {
|
||||||
|
input11.checked = ctx2[3].te;
|
||||||
}
|
}
|
||||||
if (!ctx2[3].te) {
|
if (!ctx2[3].te) {
|
||||||
if (if_block1) {
|
if (if_block1) {
|
||||||
|
@ -12975,18 +12897,22 @@
|
||||||
settings.set($settings);
|
settings.set($settings);
|
||||||
}
|
}
|
||||||
function input7_change_handler() {
|
function input7_change_handler() {
|
||||||
$settings.ca = this.checked;
|
$settings.hotlink = this.checked;
|
||||||
settings.set($settings);
|
settings.set($settings);
|
||||||
}
|
}
|
||||||
function input8_change_handler() {
|
function input8_change_handler() {
|
||||||
$settings.sh = this.checked;
|
$settings.ca = this.checked;
|
||||||
settings.set($settings);
|
settings.set($settings);
|
||||||
}
|
}
|
||||||
function input9_change_handler() {
|
function input9_change_handler() {
|
||||||
$settings.ep = this.checked;
|
$settings.sh = this.checked;
|
||||||
settings.set($settings);
|
settings.set($settings);
|
||||||
}
|
}
|
||||||
function input10_change_handler() {
|
function input10_change_handler() {
|
||||||
|
$settings.ep = this.checked;
|
||||||
|
settings.set($settings);
|
||||||
|
}
|
||||||
|
function input11_change_handler() {
|
||||||
$settings.te = this.checked;
|
$settings.te = this.checked;
|
||||||
settings.set($settings);
|
settings.set($settings);
|
||||||
}
|
}
|
||||||
|
@ -13051,6 +12977,7 @@
|
||||||
input8_change_handler,
|
input8_change_handler,
|
||||||
input9_change_handler,
|
input9_change_handler,
|
||||||
input10_change_handler,
|
input10_change_handler,
|
||||||
|
input11_change_handler,
|
||||||
input0_change_handler_1,
|
input0_change_handler_1,
|
||||||
func,
|
func,
|
||||||
remove_handler,
|
remove_handler,
|
||||||
|
@ -15408,6 +15335,7 @@
|
||||||
return {
|
return {
|
||||||
c() {
|
c() {
|
||||||
img = element("img");
|
img = element("img");
|
||||||
|
attr(img, "referrerpolicy", "no-referrer");
|
||||||
attr(img, "alt", img_alt_value = ctx[0].filename);
|
attr(img, "alt", img_alt_value = ctx[0].filename);
|
||||||
if (!src_url_equal(img.src, img_src_value = ctx[14] || ctx[5]))
|
if (!src_url_equal(img.src, img_src_value = ctx[14] || ctx[5]))
|
||||||
attr(img, "src", img_src_value);
|
attr(img, "src", img_src_value);
|
||||||
|
@ -15446,6 +15374,7 @@
|
||||||
if (!src_url_equal(source.src, source_src_value = ctx[14] || ctx[5]))
|
if (!src_url_equal(source.src, source_src_value = ctx[14] || ctx[5]))
|
||||||
attr(source, "src", source_src_value);
|
attr(source, "src", source_src_value);
|
||||||
attr(source, "type", ctx[8]);
|
attr(source, "type", ctx[8]);
|
||||||
|
attr(audio, "referrerpolicy", "no-referrer");
|
||||||
audio.controls = true;
|
audio.controls = true;
|
||||||
if (!src_url_equal(audio.src, audio_src_value = ctx[14] || ctx[5]))
|
if (!src_url_equal(audio.src, audio_src_value = ctx[14] || ctx[5]))
|
||||||
attr(audio, "src", audio_src_value);
|
attr(audio, "src", audio_src_value);
|
||||||
|
@ -15486,6 +15415,7 @@
|
||||||
return {
|
return {
|
||||||
c() {
|
c() {
|
||||||
video = element("video");
|
video = element("video");
|
||||||
|
attr(video, "referrerpolicy", "no-referrer");
|
||||||
video.loop = video_loop_value = ctx[18].loop;
|
video.loop = video_loop_value = ctx[18].loop;
|
||||||
if (!src_url_equal(video.src, video_src_value = ctx[14] || ctx[5]))
|
if (!src_url_equal(video.src, video_src_value = ctx[14] || ctx[5]))
|
||||||
attr(video, "src", video_src_value);
|
attr(video, "src", video_src_value);
|
||||||
|
@ -15556,6 +15486,7 @@
|
||||||
return {
|
return {
|
||||||
c() {
|
c() {
|
||||||
img = element("img");
|
img = element("img");
|
||||||
|
attr(img, "referrerpolicy", "no-referrer");
|
||||||
attr(img, "alt", img_alt_value = ctx[0].filename);
|
attr(img, "alt", img_alt_value = ctx[0].filename);
|
||||||
if (!src_url_equal(img.src, img_src_value = ctx[14] || ctx[5]))
|
if (!src_url_equal(img.src, img_src_value = ctx[14] || ctx[5]))
|
||||||
attr(img, "src", img_src_value);
|
attr(img, "src", img_src_value);
|
||||||
|
@ -15585,6 +15516,7 @@
|
||||||
return {
|
return {
|
||||||
c() {
|
c() {
|
||||||
video = element("video");
|
video = element("video");
|
||||||
|
attr(video, "referrerpolicy", "no-referrer");
|
||||||
video.loop = video_loop_value = ctx[18].loop;
|
video.loop = video_loop_value = ctx[18].loop;
|
||||||
if (!src_url_equal(video.src, video_src_value = ctx[14] || ctx[5]))
|
if (!src_url_equal(video.src, video_src_value = ctx[14] || ctx[5]))
|
||||||
attr(video, "src", video_src_value);
|
attr(video, "src", video_src_value);
|
||||||
|
@ -15690,9 +15622,20 @@
|
||||||
if (settled)
|
if (settled)
|
||||||
return;
|
return;
|
||||||
settled = true;
|
settled = true;
|
||||||
const thumb = file.thumbnail ? await file.thumbnail() : file.data;
|
if (file.thumbnail == null)
|
||||||
const type = await fileTypeFromBuffer(thumb);
|
return;
|
||||||
$$invalidate(5, url = URL.createObjectURL(new Blob([thumb], { type: type?.mime })));
|
const thumb = typeof file.thumbnail === "function" ? await file.thumbnail() : file.data;
|
||||||
|
let type;
|
||||||
|
if (thumb instanceof Buffer2) {
|
||||||
|
type = await fileTypeFromBuffer(thumb);
|
||||||
|
$$invalidate(5, url = URL.createObjectURL(new Blob([thumb], { type: type?.mime })));
|
||||||
|
} else if (typeof thumb === "string") {
|
||||||
|
let head = headerStringToObject(await GM_head(thumb, void 0));
|
||||||
|
type = {
|
||||||
|
ext: "",
|
||||||
|
mime: head["content-type"].split(";")[0].trim()
|
||||||
|
};
|
||||||
|
}
|
||||||
if (!type)
|
if (!type)
|
||||||
return;
|
return;
|
||||||
$$invalidate(8, ftype = type.mime);
|
$$invalidate(8, ftype = type.mime);
|
||||||
|
@ -15736,25 +15679,38 @@
|
||||||
return;
|
return;
|
||||||
if (unzipping)
|
if (unzipping)
|
||||||
return;
|
return;
|
||||||
$$invalidate(16, unzipping = true);
|
let type;
|
||||||
let lisn = new EventTarget();
|
if (typeof file.data === "function") {
|
||||||
lisn.addEventListener("progress", (e) => {
|
$$invalidate(16, unzipping = true);
|
||||||
$$invalidate(17, progress = e.detail);
|
let lisn = new EventTarget();
|
||||||
});
|
lisn.addEventListener("progress", (e) => {
|
||||||
let full = await file.data(lisn);
|
$$invalidate(17, progress = e.detail);
|
||||||
const type = await fileTypeFromBuffer(full);
|
});
|
||||||
$$invalidate(14, furl = URL.createObjectURL(new Blob([full], { type: type?.mime })));
|
let full = await file.data(lisn);
|
||||||
$$invalidate(16, unzipping = false);
|
const type2 = await fileTypeFromBuffer(full);
|
||||||
|
$$invalidate(14, furl = URL.createObjectURL(new Blob([full], { type: type2?.mime })));
|
||||||
|
} else if (typeof file.data === "string") {
|
||||||
|
$$invalidate(5, url = file.data);
|
||||||
|
$$invalidate(14, furl = file.data);
|
||||||
|
let head = headerStringToObject(await GM_head(file.data, void 0));
|
||||||
|
type = {
|
||||||
|
ext: "",
|
||||||
|
mime: head["content-type"].split(";")[0].trim()
|
||||||
|
};
|
||||||
|
}
|
||||||
if (!type)
|
if (!type)
|
||||||
return;
|
return;
|
||||||
$$invalidate(2, isVideo = type.mime.startsWith("video/"));
|
$$invalidate(2, isVideo = type.mime.startsWith("video/"));
|
||||||
$$invalidate(4, isAudio = type.mime.startsWith("audio/"));
|
$$invalidate(4, isAudio = type.mime.startsWith("audio/"));
|
||||||
$$invalidate(3, isImage = type.mime.startsWith("image/"));
|
$$invalidate(3, isImage = type.mime.startsWith("image/"));
|
||||||
|
$$invalidate(16, unzipping = false);
|
||||||
dispatch("fileinfo", { type });
|
dispatch("fileinfo", { type });
|
||||||
if (hovering) {
|
if (hovering) {
|
||||||
setTimeout(() => {
|
setTimeout(async () => {
|
||||||
recompute();
|
while (dims[0] == 0 && dims[1] == 0) {
|
||||||
hoverUpdate();
|
hoverUpdate();
|
||||||
|
await new Promise((_) => setTimeout(_, 20));
|
||||||
|
}
|
||||||
}, 20);
|
}, 20);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15851,6 +15807,7 @@
|
||||||
return;
|
return;
|
||||||
if (!contracted)
|
if (!contracted)
|
||||||
return;
|
return;
|
||||||
|
recompute();
|
||||||
const [sw, sh] = [visualViewport.width, visualViewport.height];
|
const [sw, sh] = [visualViewport.width, visualViewport.height];
|
||||||
if (dims[0] == 0 && dims[1] == 0)
|
if (dims[0] == 0 && dims[1] == 0)
|
||||||
recompute();
|
recompute();
|
||||||
|
@ -16774,13 +16731,13 @@
|
||||||
var EyeButton_default = EyeButton;
|
var EyeButton_default = EyeButton;
|
||||||
|
|
||||||
// src/main.ts
|
// src/main.ts
|
||||||
var csettings2;
|
var csettings4;
|
||||||
var processors = [thirdeye_default, pomf_default, png_default, webm_default, gif_default];
|
var processors = [thirdeye_default, pomf_default, png_default, webm_default, gif_default];
|
||||||
var cappState;
|
var cappState;
|
||||||
settings.subscribe((b) => {
|
settings.subscribe((b) => {
|
||||||
csettings2 = b;
|
csettings4 = b;
|
||||||
processors = [
|
processors = [
|
||||||
...!csettings2.te ? [thirdeye_default] : [],
|
...!csettings4.te ? [thirdeye_default] : [],
|
||||||
png_default,
|
png_default,
|
||||||
pomf_default,
|
pomf_default,
|
||||||
webm_default,
|
webm_default,
|
||||||
|
@ -16977,20 +16934,21 @@
|
||||||
const input = document.createElement("input");
|
const input = document.createElement("input");
|
||||||
input.setAttribute("type", "file");
|
input.setAttribute("type", "file");
|
||||||
const type = file.type;
|
const type = file.type;
|
||||||
|
input.multiple = true;
|
||||||
input.onchange = async (ev) => {
|
input.onchange = async (ev) => {
|
||||||
if (input.files) {
|
if (input.files) {
|
||||||
try {
|
try {
|
||||||
const proc = processors.filter((e3) => e3.inject).find((e3) => e3.match(file.name));
|
const proc = processors.filter((e3) => e3.inject).find((e3) => e3.match(file.name));
|
||||||
if (!proc)
|
if (!proc)
|
||||||
throw new Error("Container filetype not supported");
|
throw new Error("Container filetype not supported");
|
||||||
const buff = await proc.inject(file, input.files[0]);
|
const buff = await proc.inject(file, [...input.files]);
|
||||||
document.dispatchEvent(new CustomEvent("QRSetFile", {
|
document.dispatchEvent(new CustomEvent("QRSetFile", {
|
||||||
detail: { file: new Blob([buff], { type }), name: file.name }
|
detail: { file: new Blob([buff], { type }), name: file.name }
|
||||||
}));
|
}));
|
||||||
document.dispatchEvent(new CustomEvent("CreateNotification", {
|
document.dispatchEvent(new CustomEvent("CreateNotification", {
|
||||||
detail: {
|
detail: {
|
||||||
type: "success",
|
type: "success",
|
||||||
content: "File successfully embedded!",
|
content: `File${input.files.length > 1 ? "s" : ""} successfully embedded!`,
|
||||||
lifetime: 3
|
lifetime: 3
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -85,6 +85,10 @@
|
||||||
<input type="checkbox" bind:checked={$settings.prev} />
|
<input type="checkbox" bind:checked={$settings.prev} />
|
||||||
Preload external files when they are in view.
|
Preload external files when they are in view.
|
||||||
</label>
|
</label>
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" bind:checked={$settings.hotlink} />
|
||||||
|
Hotlink content.
|
||||||
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" bind:checked={$settings.ca} />
|
<input type="checkbox" bind:checked={$settings.ca} />
|
||||||
Control audio on videos with mouse wheel.
|
Control audio on videos with mouse wheel.
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { fileTypeFromBuffer } from 'file-type'
|
import { fileTypeFromBuffer, FileTypeResult } from 'file-type'
|
||||||
import { settings, appState } from './stores'
|
import { settings, appState } from './stores'
|
||||||
import { beforeUpdate, tick } from 'svelte'
|
import { beforeUpdate, tick } from 'svelte'
|
||||||
import { EmbeddedFile, EMBED_TYPES } from './main.js';
|
import { EmbeddedFile, EMBED_TYPES } from './main.js';
|
||||||
import { createEventDispatcher } from 'svelte';
|
import { createEventDispatcher } from 'svelte';
|
||||||
|
import { GM_head, headerStringToObject } from './requests.js';
|
||||||
|
|
||||||
export const dispatch = createEventDispatcher();
|
export const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
export let file: EmbeddedFile
|
export let file: EmbeddedFile
|
||||||
// export let embed_enum: EMBED_ENUM = EMBED_ENUM.NONE;
|
// export let embed_enum: EMBED_ENUM = EMBED_ENUM.NONE;
|
||||||
|
@ -14,9 +15,9 @@
|
||||||
let isAudio = false
|
let isAudio = false
|
||||||
let url = ''
|
let url = ''
|
||||||
let settled = false
|
let settled = false
|
||||||
let contracted = true;
|
let contracted = true
|
||||||
let hovering = false
|
let hovering = false
|
||||||
let ftype = '';
|
let ftype = ''
|
||||||
|
|
||||||
let place: HTMLDivElement
|
let place: HTMLDivElement
|
||||||
let hoverElem: HTMLDivElement
|
let hoverElem: HTMLDivElement
|
||||||
|
@ -24,19 +25,18 @@
|
||||||
let videoElem: HTMLVideoElement
|
let videoElem: HTMLVideoElement
|
||||||
let hoverVideo: HTMLVideoElement
|
let hoverVideo: HTMLVideoElement
|
||||||
let dims: [number, number] = [0, 0]
|
let dims: [number, number] = [0, 0]
|
||||||
let furl: string | undefined = undefined;
|
let furl: string | undefined = undefined
|
||||||
|
|
||||||
let visible = false;
|
let visible = false
|
||||||
export const isNotChrome = !navigator.userAgent.includes("Chrome/");
|
export const isNotChrome = !navigator.userAgent.includes('Chrome/')
|
||||||
|
|
||||||
export let id = '';
|
export let id = ''
|
||||||
document.addEventListener("reveal", (e: any) => {
|
document.addEventListener('reveal', (e: any) => {
|
||||||
if (e.detail.id == id)
|
if (e.detail.id == id) visible = !visible
|
||||||
visible = !visible;
|
})
|
||||||
});
|
|
||||||
|
|
||||||
export function isContracted() {
|
export function isContracted() {
|
||||||
return contracted;
|
return contracted
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeUpdate(async () => {
|
beforeUpdate(async () => {
|
||||||
|
@ -44,85 +44,109 @@
|
||||||
if (settled) return
|
if (settled) return
|
||||||
settled = true
|
settled = true
|
||||||
|
|
||||||
const thumb = file.thumbnail ? (await file.thumbnail()) : file.data;
|
if(file.thumbnail == null) return
|
||||||
const type = await fileTypeFromBuffer(thumb);
|
const thumb = typeof file.thumbnail === 'function' ? (await file.thumbnail()) : file.data;
|
||||||
url = URL.createObjectURL(new Blob([thumb], { type: type?.mime }))
|
// const thumb = file.thumbnail || file.data
|
||||||
if (!type)
|
let type: FileTypeResult | undefined
|
||||||
return;
|
if (thumb instanceof Buffer) {
|
||||||
|
type = await fileTypeFromBuffer(thumb);
|
||||||
|
url = URL.createObjectURL(new Blob([thumb], { type: type?.mime }))
|
||||||
|
// if (!type) return; // extracted out below for TypeScript
|
||||||
|
} else if (typeof thumb === 'string'){
|
||||||
|
let head = headerStringToObject(await GM_head(thumb, undefined))
|
||||||
|
// TODO write a proper getExt() and getMime()
|
||||||
|
type = { ext: '' as any, mime: head['content-type'].split(';')[0].trim() }
|
||||||
|
}
|
||||||
|
if (!type) return
|
||||||
ftype = type.mime;
|
ftype = type.mime;
|
||||||
isVideo = type.mime.startsWith('video/')
|
isVideo = type.mime.startsWith('video/')
|
||||||
isAudio = type.mime.startsWith('audio/')
|
isAudio = type.mime.startsWith('audio/')
|
||||||
isImage = type.mime.startsWith('image/')
|
isImage = type.mime.startsWith('image/')
|
||||||
dispatch("fileinfo", {type})
|
dispatch('fileinfo', { type })
|
||||||
|
|
||||||
if (isImage) {
|
if (isImage) {
|
||||||
contracted = !$settings.xpi;
|
contracted = !$settings.xpi
|
||||||
}
|
}
|
||||||
if (isVideo) {
|
if (isVideo) {
|
||||||
contracted = !$settings.xpv && !$appState.isCatalog
|
contracted = !$settings.xpv && !$appState.isCatalog
|
||||||
}
|
}
|
||||||
if ($appState.isCatalog)
|
if ($appState.isCatalog) contracted = true
|
||||||
contracted = true;
|
|
||||||
if ($settings.pre) {
|
if ($settings.pre) {
|
||||||
unzip(); // not awaiting on purpose
|
unzip() // not awaiting on purpose
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($settings.prev) {
|
if ($settings.prev) {
|
||||||
let obs = new IntersectionObserver((entries, obs) => {
|
let obs = new IntersectionObserver(
|
||||||
for(const item of entries) {
|
(entries, obs) => {
|
||||||
if(!item.isIntersecting) continue;
|
for (const item of entries) {
|
||||||
unzip();
|
if (!item.isIntersecting) continue
|
||||||
obs.unobserve(place);
|
unzip()
|
||||||
}
|
obs.unobserve(place)
|
||||||
}, {root:null, rootMargin: '1000px', threshold: 0.01});
|
}
|
||||||
obs.observe(place);
|
},
|
||||||
|
{ root: null, rootMargin: '1000px', threshold: 0.01 },
|
||||||
|
)
|
||||||
|
obs.observe(place)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
let unzipping = false;
|
let unzipping = false
|
||||||
let progress = [0, 0]
|
let progress = [0, 0]
|
||||||
async function unzip() {
|
async function unzip() {
|
||||||
if(file?.isBlacklisted === true) return
|
if(file?.isBlacklisted === true) return
|
||||||
if (!file.thumbnail)
|
|
||||||
return;
|
if (!file.thumbnail) return
|
||||||
if (unzipping)
|
if (unzipping) return
|
||||||
return;
|
|
||||||
unzipping = true;
|
let type: FileTypeResult | undefined
|
||||||
let lisn = new EventTarget();
|
if (typeof file.data === 'function') {
|
||||||
lisn.addEventListener("progress", (e: any) => {
|
unzipping = true;
|
||||||
progress = e.detail
|
let lisn = new EventTarget();
|
||||||
});
|
lisn.addEventListener("progress", (e: any) => {
|
||||||
let full = await file.data(lisn);
|
progress = e.detail
|
||||||
const type = await fileTypeFromBuffer(full);
|
});
|
||||||
furl = URL.createObjectURL(new Blob([full], { type: type?.mime }));
|
let full = await file.data(lisn);
|
||||||
unzipping = false;
|
const type = await fileTypeFromBuffer(full);
|
||||||
if (!type)
|
furl = URL.createObjectURL(new Blob([full], { type: type?.mime }));
|
||||||
return;
|
} else if (typeof file.data === 'string') {
|
||||||
|
url = file.data
|
||||||
|
furl = file.data
|
||||||
|
let head = headerStringToObject(await GM_head(file.data, undefined))
|
||||||
|
// TODO write a proper getExt() and getMime()
|
||||||
|
type = { ext: '' as any, mime: head['content-type'].split(';')[0].trim() } as FileTypeResult
|
||||||
|
}
|
||||||
|
// unzipping = false;
|
||||||
|
if (!type) return
|
||||||
isVideo = type.mime.startsWith('video/')
|
isVideo = type.mime.startsWith('video/')
|
||||||
isAudio = type.mime.startsWith('audio/')
|
isAudio = type.mime.startsWith('audio/')
|
||||||
isImage = type.mime.startsWith('image/')
|
isImage = type.mime.startsWith('image/')
|
||||||
dispatch("fileinfo", {type})
|
unzipping = false
|
||||||
|
|
||||||
|
dispatch('fileinfo', { type })
|
||||||
|
|
||||||
if (hovering) {
|
if (hovering) {
|
||||||
// reset hovering to recompute proper image coordinates
|
// reset hovering to recompute proper image coordinates
|
||||||
setTimeout(() => {
|
setTimeout(async () => {
|
||||||
recompute();
|
while (dims[0] == 0 && dims[1] == 0) {
|
||||||
hoverUpdate();
|
hoverUpdate()
|
||||||
}, 20);
|
await new Promise(_ => setTimeout(_, 20));
|
||||||
|
}
|
||||||
|
}, 20)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasAudio(video: any) {
|
function hasAudio(video: any) {
|
||||||
return (
|
return (
|
||||||
video.mozHasAudio ||
|
video.mozHasAudio ||
|
||||||
!!(video.webkitAudioDecodedByteCount) ||
|
!!video.webkitAudioDecodedByteCount ||
|
||||||
!!(video.audioTracks && video.audioTracks.length)
|
!!(video.audioTracks && video.audioTracks.length)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function bepis(ev: MouseEvent) {
|
export async function bepis(ev: MouseEvent) {
|
||||||
if (file?.isBlacklisted === true) return
|
if (file?.isBlacklisted === true) return
|
||||||
if ($appState.isCatalog) return;
|
|
||||||
|
if ($appState.isCatalog) return
|
||||||
|
|
||||||
if (ev.button == 0) {
|
if (ev.button == 0) {
|
||||||
contracted = !contracted
|
contracted = !contracted
|
||||||
|
@ -135,7 +159,7 @@
|
||||||
videoElem.controls = true
|
videoElem.controls = true
|
||||||
// has to be delayed
|
// has to be delayed
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
videoElem.currentTime = hoverVideo.currentTime || 0;
|
videoElem.currentTime = hoverVideo.currentTime || 0
|
||||||
await videoElem.play()
|
await videoElem.play()
|
||||||
}, 10)
|
}, 10)
|
||||||
}
|
}
|
||||||
|
@ -143,27 +167,33 @@
|
||||||
// don't know how you managed to click before hovering but oh well
|
// don't know how you managed to click before hovering but oh well
|
||||||
unzip()
|
unzip()
|
||||||
}
|
}
|
||||||
ev.preventDefault();
|
ev.preventDefault()
|
||||||
} else if (ev.button == 1) { // middle click
|
} else if (ev.button == 1) {
|
||||||
let src = furl || url;
|
// middle click
|
||||||
|
let src = furl || url
|
||||||
if (ev.altKey && file.source) {
|
if (ev.altKey && file.source) {
|
||||||
src = file.source;
|
src = file.source
|
||||||
}
|
}
|
||||||
if (ev.shiftKey && file.page) {
|
if (ev.shiftKey && file.page) {
|
||||||
src = file.page.url;
|
src = file.page.url
|
||||||
}
|
}
|
||||||
ev.preventDefault();
|
ev.preventDefault()
|
||||||
if (isNotChrome) {
|
if (isNotChrome) {
|
||||||
window.open(src, '_blank');
|
window.open(src, '_blank')
|
||||||
} else
|
} else await GM.openInTab(src, { active: false, insert: true })
|
||||||
await GM.openInTab(src, {active: false, insert: true});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getViewport = () => (typeof visualViewport != "undefined" ? () => [visualViewport.width, visualViewport.height] : () => [document.documentElement.clientWidth, document.documentElement.clientHeight])();
|
const getViewport = () =>
|
||||||
|
(typeof visualViewport != 'undefined'
|
||||||
|
? () => [visualViewport.width, visualViewport.height]
|
||||||
|
: () => [
|
||||||
|
document.documentElement.clientWidth,
|
||||||
|
document.documentElement.clientHeight,
|
||||||
|
])()
|
||||||
|
|
||||||
function recompute() {
|
function recompute() {
|
||||||
const [sw, sh] = getViewport();
|
const [sw, sh] = getViewport()
|
||||||
|
|
||||||
let [iw, ih] = [0, 0]
|
let [iw, ih] = [0, 0]
|
||||||
if (isImage) {
|
if (isImage) {
|
||||||
|
@ -179,48 +209,49 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
async function hoverStart(ev?: MouseEvent) {
|
async function hoverStart(ev?: MouseEvent) {
|
||||||
if (file?.isBlacklisted === true) return
|
if (file?.isBlacklisted === true) return;
|
||||||
if ($settings.dh)return;
|
|
||||||
|
if ($settings.dh) return
|
||||||
if (file.thumbnail && !furl) {
|
if (file.thumbnail && !furl) {
|
||||||
unzip();
|
unzip()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isImage && !isVideo) return
|
if (!isImage && !isVideo) return
|
||||||
if (!contracted) return
|
if (!contracted) return
|
||||||
|
|
||||||
recompute();
|
recompute()
|
||||||
hovering = true
|
hovering = true
|
||||||
|
|
||||||
if (isVideo){
|
if (isVideo) {
|
||||||
try {
|
try {
|
||||||
await hoverVideo.play()
|
await hoverVideo.play()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// probably didn't interact with document error, mute the video and try again?
|
// probably didn't interact with document error, mute the video and try again?
|
||||||
hoverVideo.muted = true;
|
hoverVideo.muted = true
|
||||||
hoverVideo.volume = 0;
|
hoverVideo.volume = 0
|
||||||
await hoverVideo.play()
|
await hoverVideo.play()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function hoverStop(ev?: MouseEvent) {
|
function hoverStop(ev?: MouseEvent) {
|
||||||
if ($settings.dh) return;
|
if ($settings.dh) return
|
||||||
hovering = false
|
hovering = false
|
||||||
if (isVideo) hoverVideo.pause()
|
if (isVideo) hoverVideo.pause()
|
||||||
}
|
}
|
||||||
|
|
||||||
let lastev: MouseEvent | undefined;
|
let lastev: MouseEvent | undefined
|
||||||
function hoverUpdate(ev?: MouseEvent) {
|
function hoverUpdate(ev?: MouseEvent) {
|
||||||
lastev = lastev || ev;
|
lastev = lastev || ev
|
||||||
if ($settings.dh) return;
|
if ($settings.dh) return
|
||||||
if (!contracted) return
|
if (!contracted) return
|
||||||
|
recompute(); // yeah I gave up
|
||||||
const [sw, sh] = [visualViewport.width, visualViewport.height]
|
const [sw, sh] = [visualViewport.width, visualViewport.height]
|
||||||
// shamelessly stolen from 4chanX
|
// shamelessly stolen from 4chanX
|
||||||
if (dims[0] == 0 && dims[1] == 0)
|
if (dims[0] == 0 && dims[1] == 0) recompute()
|
||||||
recompute();
|
|
||||||
let width = dims[0]
|
let width = dims[0]
|
||||||
let height = dims[1] + 25
|
let height = dims[1] + 25
|
||||||
let { clientX, clientY } = (ev || lastev!)
|
let { clientX, clientY } = ev || lastev!
|
||||||
let top = Math.max(0, (clientY * (sh - height)) / sh)
|
let top = Math.max(0, (clientY * (sh - height)) / sh)
|
||||||
let threshold = sw / 2
|
let threshold = sw / 2
|
||||||
let marginX: number | string =
|
let marginX: number | string =
|
||||||
|
@ -238,13 +269,12 @@
|
||||||
if (!$settings.ca) return
|
if (!$settings.ca) return
|
||||||
if (!isVideo) return
|
if (!isVideo) return
|
||||||
if ($settings.dh && contracted) return
|
if ($settings.dh && contracted) return
|
||||||
if (!hasAudio(videoElem))
|
if (!hasAudio(videoElem)) return
|
||||||
return;
|
let vol = videoElem.volume * (ev.deltaY > 0 ? 0.9 : 1.1)
|
||||||
let vol = videoElem.volume * (ev.deltaY > 0 ? 0.9 : 1.1);
|
vol = Math.max(0, Math.min(1, vol))
|
||||||
vol = Math.max(0, Math.min(1, vol));
|
videoElem.volume = vol
|
||||||
videoElem.volume = vol;
|
hoverVideo.volume = videoElem.volume
|
||||||
hoverVideo.volume = videoElem.volume;
|
hoverVideo.muted = vol < 0
|
||||||
hoverVideo.muted = vol < 0;
|
|
||||||
ev.preventDefault()
|
ev.preventDefault()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -257,8 +287,8 @@
|
||||||
class="place"
|
class="place"
|
||||||
class:hasembed={file?.embed_type === EMBED_TYPES.MEDIA_EMBED}
|
class:hasembed={file?.embed_type === EMBED_TYPES.MEDIA_EMBED}
|
||||||
class:hasext={file?.embed_type === EMBED_TYPES.THIRD_EYE}
|
class:hasext={file?.embed_type === EMBED_TYPES.THIRD_EYE}
|
||||||
on:click={e => e.preventDefault()}
|
on:click={(e) => e.preventDefault()}
|
||||||
on:auxclick={e => e.preventDefault()}
|
on:auxclick={(e) => e.preventDefault()}
|
||||||
on:mousedown={bepis}
|
on:mousedown={bepis}
|
||||||
on:mouseover={hoverStart}
|
on:mouseover={hoverStart}
|
||||||
on:mouseout={hoverStop}
|
on:mouseout={hoverStop}
|
||||||
|
@ -268,10 +298,16 @@
|
||||||
>
|
>
|
||||||
{#if isImage}
|
{#if isImage}
|
||||||
<!-- svelte-ignore a11y-missing-attribute -->
|
<!-- svelte-ignore a11y-missing-attribute -->
|
||||||
<img bind:this={imgElem} alt={file.filename} src={furl || url} />
|
<img
|
||||||
|
referrerpolicy="no-referrer"
|
||||||
|
bind:this={imgElem}
|
||||||
|
alt={file.filename}
|
||||||
|
src={furl || url}
|
||||||
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
{#if isAudio}
|
{#if isAudio}
|
||||||
<audio
|
<audio
|
||||||
|
referrerpolicy="no-referrer"
|
||||||
controls
|
controls
|
||||||
src={furl || url}
|
src={furl || url}
|
||||||
loop={$settings.loop}
|
loop={$settings.loop}
|
||||||
|
@ -283,7 +319,12 @@
|
||||||
{#if isVideo}
|
{#if isVideo}
|
||||||
<!-- svelte-ignore a11y-media-has-caption -->
|
<!-- svelte-ignore a11y-media-has-caption -->
|
||||||
<!-- svelte-ignore a11y-missing-attribute -->
|
<!-- svelte-ignore a11y-missing-attribute -->
|
||||||
<video loop={$settings.loop} bind:this={videoElem} src={furl || url} />
|
<video
|
||||||
|
referrerpolicy="no-referrer"
|
||||||
|
loop={$settings.loop}
|
||||||
|
bind:this={videoElem}
|
||||||
|
src={furl || url}
|
||||||
|
/>
|
||||||
<!-- assoom videos will never be loaded from thumbnails -->
|
<!-- assoom videos will never be loaded from thumbnails -->
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
@ -298,11 +339,16 @@
|
||||||
>{/if}
|
>{/if}
|
||||||
|
|
||||||
{#if isImage}
|
{#if isImage}
|
||||||
<img alt={file.filename} src={furl || url} />
|
<img referrerpolicy="no-referrer" alt={file.filename} src={furl || url} />
|
||||||
{/if}
|
{/if}
|
||||||
{#if isVideo}
|
{#if isVideo}
|
||||||
<!-- svelte-ignore a11y-media-has-caption -->
|
<!-- svelte-ignore a11y-media-has-caption -->
|
||||||
<video loop={$settings.loop} bind:this={hoverVideo} src={furl || url} />
|
<video
|
||||||
|
referrerpolicy="no-referrer"
|
||||||
|
loop={$settings.loop}
|
||||||
|
bind:this={hoverVideo}
|
||||||
|
src={furl || url}
|
||||||
|
/>
|
||||||
<!-- assoom videos will never be loaded from thumbnails -->
|
<!-- assoom videos will never be loaded from thumbnails -->
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -85,7 +85,7 @@ const write_embedding = async (writer: WritableStreamDefaultWriter<Buffer>, inj:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const inject = async (container: File, inj: File) => {
|
const inject = async (container: File, [inj]: File[]) => {
|
||||||
const [writestream, extract] = BufferWriteStream();
|
const [writestream, extract] = BufferWriteStream();
|
||||||
const writer = writestream.getWriter();
|
const writer = writestream.getWriter();
|
||||||
|
|
||||||
|
@ -136,6 +136,5 @@ const has_embed = (gif: Buffer) : EMBED_STATUS => {
|
||||||
export default {
|
export default {
|
||||||
extract,
|
extract,
|
||||||
has_embed,
|
has_embed,
|
||||||
inject,
|
|
||||||
match: fn => !!fn.match(/\.gif$/)
|
match: fn => !!fn.match(/\.gif$/)
|
||||||
} as ImageProcessor;
|
} as ImageProcessor;
|
||||||
|
|
15
src/main.ts
15
src/main.ts
|
@ -21,8 +21,8 @@ export interface ImageProcessor {
|
||||||
skip?: true;
|
skip?: true;
|
||||||
match(fn: string): boolean;
|
match(fn: string): boolean;
|
||||||
has_embed(b: Buffer, fn?: string): EMBED_STATUS | Promise<EMBED_STATUS>;
|
has_embed(b: Buffer, fn?: string): EMBED_STATUS | Promise<EMBED_STATUS>;
|
||||||
extract(b: Buffer, fn?: string): EmbeddedFile[] | Promise<EmbeddedFile[] | undefined>;
|
extract(b: Buffer, fn?: string): EmbeddedFile[] | Promise<EmbeddedFile[]>;
|
||||||
inject?(b: File, c: File): Buffer | Promise<Buffer>;
|
inject?(b: File, c: File[]): Buffer | Promise<Buffer>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export let csettings: Parameters<typeof settings['set']>[0];
|
export let csettings: Parameters<typeof settings['set']>[0];
|
||||||
|
@ -85,8 +85,8 @@ type EmbeddedFileWithPreview = {
|
||||||
page?: { title: string, url: string }; // can be a booru page
|
page?: { title: string, url: string }; // can be a booru page
|
||||||
source?: string; // can be like a twitter post this was posted in originally
|
source?: string; // can be like a twitter post this was posted in originally
|
||||||
filename: string;
|
filename: string;
|
||||||
thumbnail: () => Promise<Buffer>;
|
thumbnail: undefined | Buffer | (() => Promise<Buffer>);
|
||||||
data: (lisn?: EventTarget) => Promise<Buffer>;
|
data: string | ((lisn?: EventTarget) => Promise<Buffer>);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ type EmbeddedFileWithoutPreview = {
|
||||||
source: undefined;
|
source: undefined;
|
||||||
thumbnail: undefined;
|
thumbnail: undefined;
|
||||||
filename: string;
|
filename: string;
|
||||||
data: Buffer;
|
data: string | Buffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type EmbeddedFile = EmbeddedFileWithPreview | EmbeddedFileWithoutPreview;
|
export type EmbeddedFile = EmbeddedFileWithPreview | EmbeddedFileWithoutPreview;
|
||||||
|
@ -299,13 +299,14 @@ document.addEventListener('QRDialogCreation', <any>((e: CustomEvent<HTMLElement>
|
||||||
const input = document.createElement('input') as HTMLInputElement;
|
const input = document.createElement('input') as HTMLInputElement;
|
||||||
input.setAttribute("type", "file");
|
input.setAttribute("type", "file");
|
||||||
const type = file.type;
|
const type = file.type;
|
||||||
|
input.multiple = true;
|
||||||
input.onchange = (async ev => {
|
input.onchange = (async ev => {
|
||||||
if (input.files) {
|
if (input.files) {
|
||||||
try {
|
try {
|
||||||
const proc = processors.filter(e => e.inject).find(e => e.match(file.name));
|
const proc = processors.filter(e => e.inject).find(e => e.match(file.name));
|
||||||
if (!proc)
|
if (!proc)
|
||||||
throw new Error("Container filetype not supported");
|
throw new Error("Container filetype not supported");
|
||||||
const buff = await proc.inject!(file, input.files[0]);
|
const buff = await proc.inject!(file, [...input.files]);
|
||||||
document.dispatchEvent(new CustomEvent('QRSetFile', {
|
document.dispatchEvent(new CustomEvent('QRSetFile', {
|
||||||
//detail: { file: new Blob([buff]), name: file.name, type: file.type }
|
//detail: { file: new Blob([buff]), name: file.name, type: file.type }
|
||||||
detail: { file: new Blob([buff], { type }), name: file.name }
|
detail: { file: new Blob([buff], { type }), name: file.name }
|
||||||
|
@ -313,7 +314,7 @@ document.addEventListener('QRDialogCreation', <any>((e: CustomEvent<HTMLElement>
|
||||||
document.dispatchEvent(new CustomEvent("CreateNotification", {
|
document.dispatchEvent(new CustomEvent("CreateNotification", {
|
||||||
detail: {
|
detail: {
|
||||||
type: 'success',
|
type: 'success',
|
||||||
content: 'File successfully embedded!',
|
content: `File${input.files.length > 1 ? 's' : ''} successfully embedded!`,
|
||||||
lifetime: 3
|
lifetime: 3
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -144,7 +144,7 @@ export const BufferWriteStream = () => {
|
||||||
return [ret, () => b] as [WritableStream<Buffer>, () => Buffer];
|
return [ret, () => b] as [WritableStream<Buffer>, () => Buffer];
|
||||||
};
|
};
|
||||||
|
|
||||||
const inject = async (container: File, inj: File) => {
|
const inject = async (container: File, [inj]: File[]) => {
|
||||||
const [writestream, extract] = BufferWriteStream();
|
const [writestream, extract] = BufferWriteStream();
|
||||||
const encoder = new PNGEncoder(writestream);
|
const encoder = new PNGEncoder(writestream);
|
||||||
const decoder = new PNGDecoder(container.stream().getReader());
|
const decoder = new PNGDecoder(container.stream().getReader());
|
||||||
|
@ -202,6 +202,5 @@ const has_embed = async (png: Buffer) : Promise<EMBED_STATUS> => {
|
||||||
export default {
|
export default {
|
||||||
extract,
|
extract,
|
||||||
has_embed,
|
has_embed,
|
||||||
inject,
|
|
||||||
match: fn => !!fn.match(/\.png$/)
|
match: fn => !!fn.match(/\.png$/)
|
||||||
} as ImageProcessor;
|
} as ImageProcessor;
|
||||||
|
|
33
src/pngv3.ts
33
src/pngv3.ts
|
@ -4,7 +4,6 @@ import { EMBED_TYPES, ImageProcessor, EmbeddedFile, EMBED_STATUS } from './main'
|
||||||
import { PNGDecoder, PNGEncoder } from "./png";
|
import { PNGDecoder, PNGEncoder } from "./png";
|
||||||
import { decodeCoom3Payload } from "./utils";
|
import { decodeCoom3Payload } from "./utils";
|
||||||
|
|
||||||
const CUM0 = Buffer.from("CUM\0" + "0");
|
|
||||||
const CUM3 = Buffer.from("CUM\0" + "3");
|
const CUM3 = Buffer.from("CUM\0" + "3");
|
||||||
|
|
||||||
const BufferReadStream = (b: Buffer) => {
|
const BufferReadStream = (b: Buffer) => {
|
||||||
|
@ -19,7 +18,6 @@ const BufferReadStream = (b: Buffer) => {
|
||||||
|
|
||||||
const extract = async (png: Buffer): Promise<EmbeddedFile[] | undefined> => {
|
const extract = async (png: Buffer): Promise<EmbeddedFile[] | undefined> => {
|
||||||
let magic = false;
|
let magic = false;
|
||||||
let coom3 = false;
|
|
||||||
const reader = BufferReadStream(png).getReader();
|
const reader = BufferReadStream(png).getReader();
|
||||||
const sneed = new PNGDecoder(reader);
|
const sneed = new PNGDecoder(reader);
|
||||||
try {
|
try {
|
||||||
|
@ -30,10 +28,7 @@ const extract = async (png: Buffer): Promise<EmbeddedFile[] | undefined> => {
|
||||||
// should exist at the beginning of file to signal decoders if the file indeed has an embedded chunk
|
// should exist at the beginning of file to signal decoders if the file indeed has an embedded chunk
|
||||||
case 'tEXt':
|
case 'tEXt':
|
||||||
buff = chunk;
|
buff = chunk;
|
||||||
if (buff.slice(4, 4 + CUM0.length).equals(CUM0))
|
if (buff.slice(4, 4 + CUM3.length).equals(CUM3)) {
|
||||||
magic = true;
|
|
||||||
if (buff.slice(4, 4 + CUM0.length).equals(CUM3)) {
|
|
||||||
coom3 = true;
|
|
||||||
magic = true;
|
magic = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -52,16 +47,8 @@ const extract = async (png: Buffer): Promise<EmbeddedFile[] | undefined> => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (lastIDAT) {
|
if (lastIDAT) {
|
||||||
let data = (lastIDAT as Buffer).slice(4);
|
const data = (lastIDAT as Buffer).slice(4);
|
||||||
if (coom3) {
|
return await decodeCoom3Payload(data);
|
||||||
let file = decodeCoom3Payload(data)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const fnsize = data.readUInt32LE(0);
|
|
||||||
const fn = data.slice(4, 4 + fnsize).toString();
|
|
||||||
// Todo: xor the buffer to prevent scanning for file signatures (4chan embedded file detection)?
|
|
||||||
data = data.slice(4 + fnsize);
|
|
||||||
return [{ embed_type: EMBED_TYPES.MEDIA_EMBED, filename: fn, data } as EmbeddedFile];
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
|
@ -87,7 +74,7 @@ export const BufferWriteStream = () => {
|
||||||
return [ret, () => b] as [WritableStream<Buffer>, () => Buffer];
|
return [ret, () => b] as [WritableStream<Buffer>, () => Buffer];
|
||||||
};
|
};
|
||||||
|
|
||||||
const inject = async (container: File, inj: File) => {
|
const inject = async (container: File, injs: File[]) => {
|
||||||
const [writestream, extract] = BufferWriteStream();
|
const [writestream, extract] = BufferWriteStream();
|
||||||
const encoder = new PNGEncoder(writestream);
|
const encoder = new PNGEncoder(writestream);
|
||||||
const decoder = new PNGDecoder(container.stream().getReader());
|
const decoder = new PNGDecoder(container.stream().getReader());
|
||||||
|
@ -97,15 +84,13 @@ const inject = async (container: File, inj: File) => {
|
||||||
if (magic && name != "IDAT")
|
if (magic && name != "IDAT")
|
||||||
break;
|
break;
|
||||||
if (!magic && name == "IDAT") {
|
if (!magic && name == "IDAT") {
|
||||||
await encoder.insertchunk(["tEXt", buildChunk("tEXt", CUM0), 0, 0]);
|
await encoder.insertchunk(["tEXt", buildChunk("tEXt", CUM3), 0, 0]);
|
||||||
magic = true;
|
magic = true;
|
||||||
}
|
}
|
||||||
await encoder.insertchunk([name, chunk, crc, offset]);
|
await encoder.insertchunk([name, chunk, crc, offset]);
|
||||||
}
|
}
|
||||||
const injb = Buffer.alloc(4 + inj.name.length + inj.size);
|
const injb = Buffer.alloc(4);
|
||||||
injb.writeInt32LE(inj.name.length, 0);
|
// TODO
|
||||||
injb.write(inj.name, 4);
|
|
||||||
Buffer.from(await inj.arrayBuffer()).copy(injb, 4 + inj.name.length);
|
|
||||||
await encoder.insertchunk(["IDAT", buildChunk("IDAT", injb), 0, 0]);
|
await encoder.insertchunk(["IDAT", buildChunk("IDAT", injb), 0, 0]);
|
||||||
await encoder.insertchunk(["IEND", buildChunk("IEND", Buffer.from([])), 0, 0]);
|
await encoder.insertchunk(["IEND", buildChunk("IEND", Buffer.from([])), 0, 0]);
|
||||||
return extract();
|
return extract();
|
||||||
|
@ -121,9 +106,7 @@ const has_embed = async (png: Buffer): Promise<EMBED_STATUS> => {
|
||||||
// should exist at the beginning of file to signal decoders if the file indeed has an embedded chunk
|
// should exist at the beginning of file to signal decoders if the file indeed has an embedded chunk
|
||||||
case 'tEXt':
|
case 'tEXt':
|
||||||
buff = chunk;
|
buff = chunk;
|
||||||
if (buff.slice(4, 4 + CUM0.length).equals(CUM0))
|
if (buff.slice(4, 4 + CUM3.length).equals(CUM3))
|
||||||
return EMBED_STATUS.SUCCESS;
|
|
||||||
if (buff.slice(4, 4 + CUM0.length).equals(CUM3))
|
|
||||||
return EMBED_STATUS.SUCCESS;
|
return EMBED_STATUS.SUCCESS;
|
||||||
break;
|
break;
|
||||||
case 'IDAT':
|
case 'IDAT':
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { EmbeddedFile, EMBED_STATUS, EMBED_TYPES, ImageProcessor } from "./main"
|
||||||
import { GM_fetch, GM_head } from "./requests";
|
import { GM_fetch, GM_head } from "./requests";
|
||||||
import type { Buffer } from "buffer";
|
import type { Buffer } from "buffer";
|
||||||
import thumbnail from "./assets/hasembed.png";
|
import thumbnail from "./assets/hasembed.png";
|
||||||
|
import { settings } from "./stores";
|
||||||
|
|
||||||
const sources = [
|
const sources = [
|
||||||
{ host: 'Catbox', prefix: 'https://files.catbox.moe/' },
|
{ host: 'Catbox', prefix: 'https://files.catbox.moe/' },
|
||||||
|
@ -9,6 +10,11 @@ const sources = [
|
||||||
{ host: 'Pomf', prefix: 'https://a.pomf.cat/' },
|
{ host: 'Pomf', prefix: 'https://a.pomf.cat/' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export let csettings: Parameters<typeof settings['set']>[0];
|
||||||
|
settings.subscribe(b => {
|
||||||
|
csettings = b;
|
||||||
|
});
|
||||||
|
|
||||||
const getExt = (fn: string) => {
|
const getExt = (fn: string) => {
|
||||||
const isDum = fn!.match(/^([a-z0-9]{6}\.(?:jpe?g|png|webm|gif))/gi);
|
const isDum = fn!.match(/^([a-z0-9]{6}\.(?:jpe?g|png|webm|gif))/gi);
|
||||||
const isB64 = fn!.match(/^((?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=))?\.(gif|jpe?g|png|webm)/);
|
const isB64 = fn!.match(/^((?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=))?\.(gif|jpe?g|png|webm)/);
|
||||||
|
@ -41,7 +47,7 @@ const extract = async (b: Buffer, fn?: string) : Promise<EmbeddedFile[]> => {
|
||||||
return [{
|
return [{
|
||||||
embed_type: EMBED_TYPES.MEDIA_EMBED,
|
embed_type: EMBED_TYPES.MEDIA_EMBED,
|
||||||
filename: ext,
|
filename: ext,
|
||||||
data: async (lsn) => {
|
data: csettings.hotlink ? rsource! : async (lsn) => {
|
||||||
try {
|
try {
|
||||||
return (await GM_fetch(rsource, undefined, lsn)).arrayBuffer();
|
return (await GM_fetch(rsource, undefined, lsn)).arrayBuffer();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -23,6 +23,7 @@ export const settings = writable(localLoad('settingsv2', {
|
||||||
sh: false,
|
sh: false,
|
||||||
ep: false,
|
ep: false,
|
||||||
expte: false,
|
expte: false,
|
||||||
|
hotlink: false,
|
||||||
conc: 8,
|
conc: 8,
|
||||||
ho: false,
|
ho: false,
|
||||||
blacklist: ['guro', 'scat', 'ryona', 'gore'],
|
blacklist: ['guro', 'scat', 'ryona', 'gore'],
|
||||||
|
|
|
@ -3,6 +3,11 @@ import { GM_fetch } from "./requests";
|
||||||
import { localLoad, settings } from "./stores";
|
import { localLoad, settings } from "./stores";
|
||||||
import { Buffer } from "buffer";
|
import { Buffer } from "buffer";
|
||||||
|
|
||||||
|
export let csettings: Parameters<typeof settings['set']>[0];
|
||||||
|
settings.subscribe(b => {
|
||||||
|
csettings = b;
|
||||||
|
});
|
||||||
|
|
||||||
export type Booru = {
|
export type Booru = {
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -163,10 +168,17 @@ const extract = async (b: Buffer, fn?: string) : Promise<EmbeddedFile[]> => {
|
||||||
cachedPrev = (await (await GM_fetch(prev || full)).arrayBuffer()); // prefer preview
|
cachedPrev = (await (await GM_fetch(prev || full)).arrayBuffer()); // prefer preview
|
||||||
return cachedPrev;
|
return cachedPrev;
|
||||||
},
|
},
|
||||||
data: async (lsn) => {
|
data: csettings.hotlink ? (full || prev) : async (lsn) => {
|
||||||
if (!cachedFull)
|
if (!cachedFull)
|
||||||
cachedFull = (await (await GM_fetch(full || prev, undefined, lsn)).arrayBuffer()); // prefer full
|
cachedFull = (await (await GM_fetch(full || prev, undefined, lsn)).arrayBuffer()); // prefer full
|
||||||
return cachedFull;
|
return cachedFull;
|
||||||
|
|
||||||
|
// thumbnail: (await (await GM_fetch(prev || full)).arrayBuffer()), // prefer preview
|
||||||
|
// data: csettings.hotlink ? (full || prev) : async (lsn) => {
|
||||||
|
// if (!cachedFile)
|
||||||
|
// cachedFile = (await (await GM_fetch(full || prev, undefined, lsn)).arrayBuffer()); // prefer full
|
||||||
|
// return cachedFile;
|
||||||
|
|
||||||
}
|
}
|
||||||
} as EmbeddedFile];
|
} as EmbeddedFile];
|
||||||
};
|
};
|
||||||
|
|
53
src/utils.ts
53
src/utils.ts
|
@ -1,5 +1,52 @@
|
||||||
import type { Buffer } from "buffer";
|
import { Buffer } from "buffer";
|
||||||
|
import { GM_fetch, headerStringToObject } from "./requests";
|
||||||
|
import thumbnail from "./assets/hasembed.png";
|
||||||
|
import { EmbeddedFile, EMBED_TYPES } from './main';
|
||||||
|
|
||||||
export const decodeCoom3Payload = (buff: Buffer) => {
|
/*
|
||||||
//
|
header (must be < 2k): [1 byte bitfield](if hasfilename: null terminated string)(if has tags: [X null terminated string, tags are whitespace-separated])
|
||||||
|
(if has thumbnail: [thumbnail size X]
|
||||||
|
rest: [X bytes of thumbnail data])[file bytes]
|
||||||
|
&1 => has filename
|
||||||
|
&2 => has tags
|
||||||
|
&4 => has thumbnail
|
||||||
|
*/
|
||||||
|
export const decodeCoom3Payload = async (buff: Buffer) : Promise<EmbeddedFile[]> => {
|
||||||
|
const pees = buff.toString().split('\0');
|
||||||
|
return Promise.all(pees.map(async pee => {
|
||||||
|
const res = await GM_fetch(pee, { headers: { ranges: 'bytes=0-2048' } });
|
||||||
|
const size = +(res.headers.get('content-size') || 0);
|
||||||
|
const header = Buffer.from(await res.arrayBuffer());
|
||||||
|
const flags = header[0];
|
||||||
|
const hasFn = flags & 1;
|
||||||
|
const hasTags = flags & 2;
|
||||||
|
const hasThumbnail = flags & 4;
|
||||||
|
let [ptr, ptr2] = [1, 1];
|
||||||
|
let fn = 'embedded';
|
||||||
|
let tags = [];
|
||||||
|
let thumb: EmbeddedFile['thumbnail'] = Buffer.from(thumbnail);
|
||||||
|
if (hasFn) {
|
||||||
|
while (buff[ptr2] != 0)
|
||||||
|
ptr2++;
|
||||||
|
fn = header.slice(ptr, ptr2).toString();
|
||||||
|
ptr = ++ptr2;
|
||||||
|
}
|
||||||
|
if (hasTags) {
|
||||||
|
while (buff[ptr2] != 0)
|
||||||
|
ptr2++;
|
||||||
|
tags = header.slice(ptr, ptr2).toString().split(/\s+/);
|
||||||
|
}
|
||||||
|
let thumbsize = 0;
|
||||||
|
if (hasThumbnail) {
|
||||||
|
thumbsize = header.readInt32LE(ptr);
|
||||||
|
thumb = Buffer.from(await (await GM_fetch(pee, { headers: { range: `bytes: ${ptr + 4}-${ptr + 4 + thumbsize}` } })).arrayBuffer());
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
embed_type: EMBED_TYPES.MEDIA_EMBED,
|
||||||
|
filename: fn,
|
||||||
|
data: async (lsn) =>
|
||||||
|
Buffer.from(await (await GM_fetch(pee, { headers: { range: `bytes: ${ptr + 4 + thumbsize}-${size-1}` } }, lsn)).arrayBuffer()),
|
||||||
|
thumbnail: thumb,
|
||||||
|
} as EmbeddedFile;
|
||||||
|
}));
|
||||||
};
|
};
|
|
@ -123,7 +123,7 @@ const extract = (webm: Buffer) : EmbeddedFile[] | undefined => {
|
||||||
return [{ embed_type: EMBED_TYPES.MEDIA_EMBED, filename: 'string', data: chk.data } as EmbeddedFile];
|
return [{ embed_type: EMBED_TYPES.MEDIA_EMBED, filename: 'string', data: chk.data } as EmbeddedFile];
|
||||||
};
|
};
|
||||||
|
|
||||||
const inject = async (container: File, inj: File): Promise<Buffer> =>
|
const inject = async (container: File, [inj]: File[]): Promise<Buffer> =>
|
||||||
embed(Buffer.from(await container.arrayBuffer()), Buffer.from(await inj.arrayBuffer()));
|
embed(Buffer.from(await container.arrayBuffer()), Buffer.from(await inj.arrayBuffer()));
|
||||||
|
|
||||||
const has_embed = (webm: Buffer) : EMBED_STATUS => {
|
const has_embed = (webm: Buffer) : EMBED_STATUS => {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user