Browse Source

Add GIF support

pull/46/head
coomdev 2 years ago
parent
commit
f3ab82face
  1. 2
      main.meta.js
  2. 1074
      main.user.js
  3. 97
      src/gif.ts
  4. 57
      src/main.ts
  5. 2
      src/png.ts
  6. 1
      src/webm.ts

2
main.meta.js

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

1074
main.user.js

File diff suppressed because it is too large

97
src/gif.ts

@ -0,0 +1,97 @@
import { Buffer } from "buffer";
import { BufferWriteStream, concatAB } from "./png";
const netscape = Buffer.from("!\xFF\x0BNETSCAPE2.0\x03\x01\x00\x00\x00");
const magic = Buffer.from("!\xFF\x0B" + "COOMTECH0.1", 'ascii');
const extractBuff = (gif: Buffer) => {
let field = gif.readUInt8(10);
let gcte = !!(field & (1 << 7));
let end = 13;
if (gcte) {
end += 3 * (1 << ((field & 7) + 1));
}
// skip beeg blocks
while (gif.readUInt8(end) == '!'.charCodeAt(0)) {
if (magic.compare(gif, end, end + magic.byteLength) != 0) {
end += 3 + gif.readUInt8(end + 2);
while (1) { // skip sub blocks
let v = gif.readUInt8(end++);
if (!v)
break;
end += v;
}
} else {
let count = end + magic.byteLength;
let t = 0;
let v = 0;
while ((v = gif.readUInt8(count)) != 0) {
t += v;
count += v + 1;
}
let buff = Buffer.alloc(t);
count = end + magic.byteLength;
t = 0;
while ((v = gif.readUInt8(count)) != 0) {
gif.copy(buff, t, count + 1, count + 1 + v)
t += v;
count += v + 1;
}
return buff;
}
}
// metadata ended, nothing...
};
export const extract = async (reader: ReadableStreamDefaultReader<Uint8Array>): Promise<{ filename: string; data: Buffer } | undefined> => {
let total = Buffer.from('');
let chunk: ReadableStreamDefaultReadResult<Uint8Array>;
// todo: early reject
do {
chunk = await reader.read();
if (chunk.value)
total = concatAB(total, Buffer.from(chunk.value));
} while (!chunk.done);
const data = extractBuff(total);
if (!data)
return;
return { filename: 'embedded', data };
};
const write_embedding = async (writer: WritableStreamDefaultWriter<Buffer>, inj: Buffer) => {
await writer.write(magic);
const byte = 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);
};
export const inject = async (container: File, inj: File) => {
const [writestream, extract] = BufferWriteStream();
const writer = writestream.getWriter();
let contbuff = Buffer.from(await container.arrayBuffer());
let field = contbuff.readUInt8(10);
let gcte = !!(field & (1 << 0x7))
let endo = 13;
if (gcte)
endo += 3 * (1 << ((field & 7) + 1));
if (netscape.compare(contbuff, endo, endo + netscape.byteLength) == 0)
endo += netscape.byteLength;
await writer.write(contbuff.slice(0, endo));
await write_embedding(writer, Buffer.from(await inj.arrayBuffer()));
await writer.write(contbuff.slice(endo));
return extract();
};

57
src/main.ts

@ -1,17 +1,19 @@
import { Buffer } from "buffer";
import { fileTypeFromBuffer } from 'file-type';
import * as png from "./png";
import * as webm from "./webm";
import App from "./App.svelte";
import { settings } from "./stores";
import * as png from "./png";
import * as webm from "./webm";
import * as gif from "./gif";
let csettings: any;
settings.subscribe(b => csettings = b);
type Awaited<T> = T extends PromiseLike<infer U> ? U : T
const xmlhttprequest = typeof GM_xmlhttpRequest != 'undefined' ? GM_xmlhttpRequest : (GM ? GM.xmlHttpRequest : GM_xmlhttpRequest);
const xmlhttprequest = typeof GM_xmlhttpRequest != 'undefined' ? GM_xmlhttpRequest : (typeof GM != "undefined" ? GM.xmlHttpRequest : GM_xmlhttpRequest);
const headerStringToObject = (s: string) =>
Object.fromEntries(s.split('\n').map(e => {
@ -114,28 +116,15 @@ function iteratorToStream<T>(iterator: AsyncGenerator<T>) {
});
}
// (async () => {
// const iter = streamRemote("https://i.4cdn.org/g/1641097404527.png", 16 * 1024, false);
// //const str = iteratorToStream(iter);
// const cum: Buffer[] = [];
// for await (const buf of iter) {
// console.log(buf.byteLength);
// cum.push(buf);
// }
// const total = cum.reduce((a, b) => png.concatAB(a, b));
// console.log(await fileTypeFromBuffer(total));
// })();
const processors: [RegExp,
(reader: ReadableStreamDefaultReader<Uint8Array>) => Promise<{ filename: string; data: Buffer } | undefined>,
(container: File, inj: File) => Promise<Buffer>][] = [
[/\.png$/, png.extract, png.inject],
[/\.webm$/, webm.extract, webm.inject]
[/\.webm$/, webm.extract, webm.inject],
[/\.gif$/, gif.extract, gif.inject],
];
const processImage = async (src: string) => {
if (src.includes('/images/')) // thirdeye removes the original link and puts a non-existing link
return; // cant do shit about that.
const proc = processors.find(e => src.match(e[0]));
if (!proc)
return;
@ -153,9 +142,10 @@ const textToElement = <T = HTMLElement>(s: string) =>
const processPost = async (post: HTMLDivElement) => {
const thumb = post.querySelector(".fileThumb") as HTMLAnchorElement;
if (!thumb)
const origlink = post.querySelector('.file-info > a') as HTMLAnchorElement;
if (!thumb || !origlink)
return;
const res = await processImage(thumb.href);
const res = await processImage(origlink.href);
if (!res)
return;
const replyBox = post.querySelector('.post');
@ -295,14 +285,16 @@ const processPost = async (post: HTMLDivElement) => {
const startup = async () => {
//await Promise.all([...document.querySelectorAll('.postContainer')].filter(e => e.textContent?.includes("191 KB")).map(e => processPost(e as any)));
await Promise.all([...document.querySelectorAll('.postContainer')].filter(e => e.textContent?.includes("191 KB")).map(e => processPost(e as any)));
// Basically this is a misnommer: fires even when inlining existings posts, also posts are inlined through some kind of dom projection
// document.addEventListener('PostsInserted', <any>(async (e: CustomEvent<string>) => {
// const threadelement = e.target as HTMLDivElement;
// const posts = [...threadelement.querySelectorAll(".postContainer")].filter(e => !e.hasAttribute('data-processed'));
// posts.map(e => processPost(e as any));
// }));threadelement
document.addEventListener('ThreadUpdate', <any>(async (e: CustomEvent<any>) => {
let newPosts = e.detail.newPosts;
for (const post of newPosts) {
let postContainer = document.getElementById("pc" + post.substring(post.indexOf(".") + 1)) as HTMLDivElement;
processPost(postContainer);
}
}));
const mo = new MutationObserver(reco => {
for (const rec of reco)
@ -423,19 +415,24 @@ customStyles.appendChild(document.createTextNode(
));
document.documentElement.insertBefore(customStyles, null);
// import * as gif from './gif';
// onload = () => {
// let container = document.getElementById("container") as HTMLInputElement;
// let injection = document.getElementById("injection") as HTMLInputElement;
// container.onchange = injection.onchange = async () => {
// if (container.files?.length && injection.files?.length) {
// let res = await buildInjection(container.files[0], injection.files[0]);
// let res = await gif.inject(container.files[0], injection.files[0]);
// let result = document.getElementById("result") as HTMLImageElement;
// let extracted = document.getElementById("extracted") as HTMLImageElement;
// result.src = URL.createObjectURL(res.file);
// let embedded = await extractEmbedded(res.file.stream().getReader());
// let res2 = new Blob([res], { type: 'image/gif' });
// result.src = URL.createObjectURL(res2);
// let embedded = await gif.extract(res2.stream().getReader());
// extracted.src = URL.createObjectURL(new Blob([embedded?.data!]));
// let dlr = document.getElementById("dlr") as HTMLAnchorElement;
// let dle = document.getElementById("dle") as HTMLAnchorElement;
// dlr.href = result.src;
// dle.href = extracted.src;
// }
// }
// }

2
src/png.ts

@ -129,7 +129,7 @@ const buildChunk = (tag: string, data: Buffer) => {
return ret;
};
const BufferWriteStream = () => {
export const BufferWriteStream = () => {
let b = Buffer.from([]);
const ret = new WritableStream<Buffer>({
write(chunk) {

1
src/webm.ts

@ -126,6 +126,7 @@ const extractBuff = (webm: Buffer) => {
export const extract = async (reader: ReadableStreamDefaultReader<Uint8Array>): Promise<{ filename: string; data: Buffer } | undefined> => {
let total = Buffer.from('');
let chunk: ReadableStreamDefaultReadResult<Uint8Array>;
// todo: early reject
do {
chunk = await reader.read();
if (chunk.value)

Loading…
Cancel
Save