Browse Source

Add WIP web extensions

pull/46/head
coomdev 2 years ago
parent
commit
4e78677bbf
  1. 2
      .gitignore
  2. BIN
      PEE-chrome.crx
  3. 9
      README.md
  4. 153
      build-chrome.js
  5. 167
      build-ff.js
  6. 8
      chrome/browser-polyfill.min.js
  7. 107
      chrome/manifest.json
  8. 6
      chrome_update.xml
  9. 3
      firefox/.web-extension-id
  10. 8
      firefox/browser-polyfill.min.js
  11. 113
      firefox/manifest.json
  12. 2
      firefox/polyfill.min.js
  13. 1
      firefox_update.json
  14. 3
      main.d.ts
  15. 10363
      package-lock.json
  16. 5
      package.json
  17. BIN
      pngextraembedder-0.208-an+fx.xpi
  18. 3
      src/Components/App.svelte
  19. 209
      src/background.ts
  20. 4
      src/hydrus.ts
  21. 219
      src/main.ts
  22. 266
      src/platform.ts
  23. 12
      src/requests.ts
  24. 5
      src/thirdeye.ts
  25. 27
      src/utils.ts
  26. 4
      src/websites/index.ts
  27. 6
      update.xml

2
.gitignore

@ -21,3 +21,5 @@ core.js
fag
rollup.config.js
.vscode/settings.json
key.pem
chrome.pem

BIN
PEE-chrome.crx

Binary file not shown.

9
README.md

@ -11,12 +11,19 @@ Also supports desuarchive.
How to Install
==============
Note: 4chanX isn't a hard requirement, just recommended because it's overall a nicer experience. If you don't want to use 4chanX, make sure the native 4chan extension is enabled in your settings.
## Teh olde way
- Make sure you're using a decent Webkit-based browser (Chromium derivatives) or Firefox.
- [Install ViolentMonkey](https://violentmonkey.github.io/get-it/) (it is preferable to TamperMonkey(closed source) and GreaseMonkey(abandonned shit)), use [ViolentMonkey Beta](https://violentmonkey.github.io/get-it/#beta-release) if you want to spite the b4kuck nigger admin (based & redpilled). Be sure to read the trouble shooting section to know how to set this up.
- [Install 4chanX (recommended)](https://www.4chan-x.net/builds/4chan-X.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)
Note: 4chanX isn't a hard requirement, just recommended because it's overall a nicer experience. If you don't want to use 4chanX, make sure the native 4chan extension is enabled in your settings.
## The newer way (WIP)
- [Install 4chanX (recommended)](https://www.4chan-x.net/builds/4chan-X.user.js)
- Install the correct WebExtension for your Browser ([Firefox](https://git.coom.tech/coomdev/PEE/raw/branch/%E4%B8%AD%E5%87%BA%E3%81%97/pngextraembedder-0.208-an+fx.xpi) or [Chrome-based](https://git.coom.tech/coomdev/PEE/raw/branch/%E4%B8%AD%E5%87%BA%E3%81%97/PEE-chrome.crx))
How to Build
============

153
build-chrome.js

@ -0,0 +1,153 @@
/* eslint-disable */
import { spawnSync } from 'child_process';
import { writeFileSync, readFileSync, copyFileSync } from 'fs'
import esbuild from "esbuild";
import esbuildSvelte from "esbuild-svelte";
import sveltePreprocess from "svelte-preprocess";
import path from 'path';
import ChromeExtension from 'crx';
const crx = new ChromeExtension({
codebase: 'http://localhost:8000/myExtension.crx',
privateKey: readFileSync('./key.pem')
});
let res = spawnSync("git", ["rev-list", "--count", "HEAD"]);
let rev = +res.stdout;
const domains = [
"https://*.coom.tech/*",
"https://boards.4channel.org/*",
"https://boards.4chan.org/*",
"https://desuarchive.org/*",
"https://archived.moe/*",
"https://archive.nyafuu.org/*",
"https://arch.b4k.co/*",
"https://archive.wakarimasen.moe/*",
"https://b4k.co/*",
"https://fireden.net/*",
"https://thebarchive.com/*",
"https://archiveofsins.com/*",
"https://catbox.moe/*",
"https://zz.ht/*",
"https://take-me-to.space/*",
"https://*.4cdn.org/*",
"https://*.desuarchive.org/*",
"https://*.archived.moe/*",
"https://*.archive.nyafuu.org/*",
"https://*.b4k.co/*",
"https://*.wakarimasen.moe/*",
"https://*.fireden.net/*",
"https://*.thebarchive.com/*",
"https://*.archiveofsins.com/*",
"https://*.catbox.moe/*",
"https://*.zz.ht/*",
"https://*.imouto.kawaii.su/*",
"https://*.take-me-to.space/*",
"https://*.gelbooru.com/*",
"https://*.yande.re/*",
"https://*.sankakucomplex.com/*",
"https://*.rule34.xxx/*",
"https://*.donmai.us/*",
"https://*.lolibooru.moe/*",
"https://*.allthefallen.moe/*",
];
const manif = {
"manifest_version": 2,
"update_url": "https://git.coom.tech/fuckjannies/lolipiss/raw/branch/%E4%B8%AD%E5%87%BA%E3%81%97/chrome_update.xml",
"name": "PngExtraEmbedder",
"description": "Discover embedded files on 4chan and archives!",
"version": "0." + rev,
"icons": {
"64": "1449696017588.png"
},
"permissions": [
"notifications",
"clipboardWrite",
"menus",
"activeTab",
"webRequest",
"webRequestBlocking",
"contextMenus",
...domains
],
"content_scripts": [
{
"matches": domains,
"css": [],
"run_at": "document_start",
"js": ["dist/main.js"],
}
],
"background": {
persistent: true,
"scripts": [
"browser-polyfill.min.js",
"dist/background.js"
]
}
};
(async () => {
let res;
res = await esbuild
.build({
entryPoints: ["src/main.ts"],
bundle: true,
treeShaking: true,
outfile: "./chrome/dist/main.js",
define: {
global: 'window',
execution_mode: '"chrome_api"',
isBackground: 'false',
BUILD_VERSION: JSON.stringify([0, rev])
},
inject: ['./esbuild.inject.js'],
plugins: [
esbuildSvelte({
compilerOptions: { css: true, accessors: true },
preprocess: sveltePreprocess(),
})
],
loader: {
'.css': 'text',
'.png': 'binary'
},
metafile: true
})
console.log(res.metafile.inputs);
console.log(Object.entries(res.metafile.inputs).sort((a, b) => a[1].bytes - b[1].bytes).map(e => `${e[0]} -> ${e[1].bytes}`).join('\n'));
res = await esbuild
.build({
entryPoints: ["src/background.ts"],
bundle: true,
treeShaking: true,
outfile: "./chrome/dist/background.js",
define: {
global: 'window',
execution_mode: '"chrome_api"',
isBackground: 'false',
BUILD_VERSION: JSON.stringify([0, rev])
},
inject: ['./esbuild.inject.js'],
metafile: true
});
console.log(res.metafile.inputs);
console.log(Object.entries(res.metafile.inputs).sort((a, b) => a[1].bytes - b[1].bytes).map(e => `${e[0]} -> ${e[1].bytes}`).join('\n'));
writeFileSync('./chrome/manifest.json', JSON.stringify(manif, null, 2));
copyFileSync("./1449696017588.png", "./chrome/1449696017588.png");
const ext = await crx.load('./chrome');
const crxBuffer = await ext.pack();
const updateXML = crx.generateUpdateXML();
writeFileSync('./chrome_update.xml', updateXML);
writeFileSync('./PEE-chrome.crx', crxBuffer);
})();

167
build-ff.js

@ -0,0 +1,167 @@
/* eslint-disable */
import { spawnSync } from 'child_process';
import { writeFileSync, readFileSync, copyFileSync } from 'fs'
import esbuild from "esbuild";
import esbuildSvelte from "esbuild-svelte";
import sveltePreprocess from "svelte-preprocess";
import webExt from 'web-ext';
let res = spawnSync("git", ["rev-list", "--count", "HEAD"]);
let rev = +res.stdout;
const domains = [
"https://*.coom.tech/*",
"https://boards.4channel.org/*",
"https://boards.4chan.org/*",
"https://desuarchive.org/*",
"https://archived.moe/*",
"https://archive.nyafuu.org/*",
"https://arch.b4k.co/*",
"https://archive.wakarimasen.moe/*",
"https://b4k.co/*",
"https://fireden.net/*",
"https://thebarchive.com/*",
"https://archiveofsins.com/*",
"https://catbox.moe/*",
"https://zz.ht/*",
"https://take-me-to.space/*",
"https://*.4cdn.org/*",
"https://*.desuarchive.org/*",
"https://*.archived.moe/*",
"https://*.archive.nyafuu.org/*",
"https://*.b4k.co/*",
"https://*.wakarimasen.moe/*",
"https://*.fireden.net/*",
"https://*.thebarchive.com/*",
"https://*.archiveofsins.com/*",
"https://*.catbox.moe/*",
"https://*.zz.ht/*",
"https://*.imouto.kawaii.su/*",
"https://*.take-me-to.space/*",
"https://*.gelbooru.com/*",
"https://*.yande.re/*",
"https://*.sankakucomplex.com/*",
"https://*.rule34.xxx/*",
"https://*.donmai.us/*",
"https://*.lolibooru.moe/*",
"https://*.allthefallen.moe/*",
];
const manif = {
"manifest_version": 2,
"browser_specific_settings": {
"gecko": {
"update_url": "https://git.coom.tech/fuckjannies/lolipiss/raw/branch/%E4%B8%AD%E5%87%BA%E3%81%97/firefox_update.json",
}
},
"name": "PngExtraEmbedder",
"description": "Discover embedded files on 4chan and archives!",
"version": "0." + rev,
"icons": {
"64": "1449696017588.png"
},
"permissions": [
"notifications",
"clipboardWrite",
"menus",
"activeTab",
"webRequest",
"webRequestBlocking",
"contextMenus",
...domains
],
"content_scripts": [
{
"matches": domains,
"css": [],
"run_at": "document_start",
"js": ["polyfill.min.js", "dist/main.js"],
}
],
"background": {
persistent: true,
"scripts": [
"polyfill.min.js",
"browser-polyfill.min.js",
"dist/background.js"
]
}
};
(async () => {
let res;
res = await esbuild
.build({
entryPoints: ["src/main.ts"],
bundle: true,
treeShaking: true,
outfile: "./firefox/dist/main.js",
define: {
global: 'window',
execution_mode: '"ff_api"',
isBackground: 'false',
BUILD_VERSION: JSON.stringify([0, rev])
},
inject: ['./esbuild.inject.js'],
plugins: [
esbuildSvelte({
compilerOptions: { css: true, accessors: true },
preprocess: sveltePreprocess(),
})
],
loader: {
'.css': 'text',
'.png': 'binary'
},
metafile: true
})
console.log(res.metafile.inputs);
console.log(Object.entries(res.metafile.inputs).sort((a, b) => a[1].bytes - b[1].bytes).map(e => `${e[0]} -> ${e[1].bytes}`).join('\n'));
res = await esbuild
.build({
entryPoints: ["src/background.ts"],
bundle: true,
treeShaking: true,
outfile: "./firefox/dist/background.js",
define: {
global: 'window',
execution_mode: '"ff_api"',
isBackground: 'false',
BUILD_VERSION: JSON.stringify([0, rev])
},
inject: ['./esbuild.inject.js'],
metafile: true
});
console.log(res.metafile.inputs);
console.log(Object.entries(res.metafile.inputs).sort((a, b) => a[1].bytes - b[1].bytes).map(e => `${e[0]} -> ${e[1].bytes}`).join('\n'));
writeFileSync('./firefox/manifest.json', JSON.stringify(manif, null, 2));
copyFileSync("./1449696017588.png", "./firefox/1449696017588.png");
res = await webExt.cmd.build({
sourceDir: './firefox/',
artifactsDir: '.',
filename: 'PEE-firefox.zip',
overwriteDest: true,
});
console.log(res);
writeFileSync('./firefox_update.json', JSON.stringify({
"addons": {
"{34ac4994-07f2-44d2-8599-682516a6c6a6}": {
"updates": [
{
"version": manif.version,
"update_link": "https://git.coom.tech/fuckjannies/lolipiss/raw/branch/%E4%B8%AD%E5%87%BA%E3%81%97/pee-firefox.zip",
}
]
}
}
}));
})();

8
chrome/browser-polyfill.min.js

File diff suppressed because one or more lines are too long

107
chrome/manifest.json

@ -0,0 +1,107 @@
{
"manifest_version": 2,
"update_url": "https://git.coom.tech/fuckjannies/lolipiss/raw/branch/%E4%B8%AD%E5%87%BA%E3%81%97/chrome_update.xml",
"name": "PngExtraEmbedder",
"description": "Discover embedded files on 4chan and archives!",
"version": "0.208",
"icons": {
"64": "1449696017588.png"
},
"permissions": [
"notifications",
"clipboardWrite",
"menus",
"activeTab",
"webRequest",
"webRequestBlocking",
"contextMenus",
"https://*.coom.tech/*",
"https://boards.4channel.org/*",
"https://boards.4chan.org/*",
"https://desuarchive.org/*",
"https://archived.moe/*",
"https://archive.nyafuu.org/*",
"https://arch.b4k.co/*",
"https://archive.wakarimasen.moe/*",
"https://b4k.co/*",
"https://fireden.net/*",
"https://thebarchive.com/*",
"https://archiveofsins.com/*",
"https://catbox.moe/*",
"https://zz.ht/*",
"https://take-me-to.space/*",
"https://*.4cdn.org/*",
"https://*.desuarchive.org/*",
"https://*.archived.moe/*",
"https://*.archive.nyafuu.org/*",
"https://*.b4k.co/*",
"https://*.wakarimasen.moe/*",
"https://*.fireden.net/*",
"https://*.thebarchive.com/*",
"https://*.archiveofsins.com/*",
"https://*.catbox.moe/*",
"https://*.zz.ht/*",
"https://*.imouto.kawaii.su/*",
"https://*.take-me-to.space/*",
"https://*.gelbooru.com/*",
"https://*.yande.re/*",
"https://*.sankakucomplex.com/*",
"https://*.rule34.xxx/*",
"https://*.donmai.us/*",
"https://*.lolibooru.moe/*",
"https://*.allthefallen.moe/*"
],
"content_scripts": [
{
"matches": [
"https://*.coom.tech/*",
"https://boards.4channel.org/*",
"https://boards.4chan.org/*",
"https://desuarchive.org/*",
"https://archived.moe/*",
"https://archive.nyafuu.org/*",
"https://arch.b4k.co/*",
"https://archive.wakarimasen.moe/*",
"https://b4k.co/*",
"https://fireden.net/*",
"https://thebarchive.com/*",
"https://archiveofsins.com/*",
"https://catbox.moe/*",
"https://zz.ht/*",
"https://take-me-to.space/*",
"https://*.4cdn.org/*",
"https://*.desuarchive.org/*",
"https://*.archived.moe/*",
"https://*.archive.nyafuu.org/*",
"https://*.b4k.co/*",
"https://*.wakarimasen.moe/*",
"https://*.fireden.net/*",
"https://*.thebarchive.com/*",
"https://*.archiveofsins.com/*",
"https://*.catbox.moe/*",
"https://*.zz.ht/*",
"https://*.imouto.kawaii.su/*",
"https://*.take-me-to.space/*",
"https://*.gelbooru.com/*",
"https://*.yande.re/*",
"https://*.sankakucomplex.com/*",
"https://*.rule34.xxx/*",
"https://*.donmai.us/*",
"https://*.lolibooru.moe/*",
"https://*.allthefallen.moe/*"
],
"css": [],
"run_at": "document_start",
"js": [
"dist/main.js"
]
}
],
"background": {
"persistent": true,
"scripts": [
"browser-polyfill.min.js",
"dist/background.js"
]
}
}

6
chrome_update.xml

@ -0,0 +1,6 @@
<?xml version='1.0' encoding='UTF-8'?>
<gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>
<app appid='ilffidhdekahjldemialkgahicnajchb'>
<updatecheck codebase='http://localhost:8000/myExtension.crx' version='0.208' prodversionmin='64.0.3242' />
</app>
</gupdate>

3
firefox/.web-extension-id

@ -0,0 +1,3 @@
# This file was created by https://github.com/mozilla/web-ext
# Your auto-generated extension ID for addons.mozilla.org is:
{34ac4994-07f2-44d2-8599-682516a6c6a6}

8
firefox/browser-polyfill.min.js

File diff suppressed because one or more lines are too long

113
firefox/manifest.json

@ -0,0 +1,113 @@
{
"manifest_version": 2,
"browser_specific_settings": {
"gecko": {
"update_url": "https://git.coom.tech/fuckjannies/lolipiss/raw/branch/%E4%B8%AD%E5%87%BA%E3%81%97/firefox_update.json"
}
},
"name": "PngExtraEmbedder",
"description": "Discover embedded files on 4chan and archives!",
"version": "0.208",
"icons": {
"64": "1449696017588.png"
},
"permissions": [
"notifications",
"clipboardWrite",
"menus",
"activeTab",
"webRequest",
"webRequestBlocking",
"contextMenus",
"https://*.coom.tech/*",
"https://boards.4channel.org/*",
"https://boards.4chan.org/*",
"https://desuarchive.org/*",
"https://archived.moe/*",
"https://archive.nyafuu.org/*",
"https://arch.b4k.co/*",
"https://archive.wakarimasen.moe/*",
"https://b4k.co/*",
"https://fireden.net/*",
"https://thebarchive.com/*",
"https://archiveofsins.com/*",
"https://catbox.moe/*",
"https://zz.ht/*",
"https://take-me-to.space/*",
"https://*.4cdn.org/*",
"https://*.desuarchive.org/*",
"https://*.archived.moe/*",
"https://*.archive.nyafuu.org/*",
"https://*.b4k.co/*",
"https://*.wakarimasen.moe/*",
"https://*.fireden.net/*",
"https://*.thebarchive.com/*",
"https://*.archiveofsins.com/*",
"https://*.catbox.moe/*",
"https://*.zz.ht/*",
"https://*.imouto.kawaii.su/*",
"https://*.take-me-to.space/*",
"https://*.gelbooru.com/*",
"https://*.yande.re/*",
"https://*.sankakucomplex.com/*",
"https://*.rule34.xxx/*",
"https://*.donmai.us/*",
"https://*.lolibooru.moe/*",
"https://*.allthefallen.moe/*"
],
"content_scripts": [
{
"matches": [
"https://*.coom.tech/*",
"https://boards.4channel.org/*",
"https://boards.4chan.org/*",
"https://desuarchive.org/*",
"https://archived.moe/*",
"https://archive.nyafuu.org/*",
"https://arch.b4k.co/*",
"https://archive.wakarimasen.moe/*",
"https://b4k.co/*",
"https://fireden.net/*",
"https://thebarchive.com/*",
"https://archiveofsins.com/*",
"https://catbox.moe/*",
"https://zz.ht/*",
"https://take-me-to.space/*",
"https://*.4cdn.org/*",
"https://*.desuarchive.org/*",
"https://*.archived.moe/*",
"https://*.archive.nyafuu.org/*",
"https://*.b4k.co/*",
"https://*.wakarimasen.moe/*",
"https://*.fireden.net/*",
"https://*.thebarchive.com/*",
"https://*.archiveofsins.com/*",
"https://*.catbox.moe/*",
"https://*.zz.ht/*",
"https://*.imouto.kawaii.su/*",
"https://*.take-me-to.space/*",
"https://*.gelbooru.com/*",
"https://*.yande.re/*",
"https://*.sankakucomplex.com/*",
"https://*.rule34.xxx/*",
"https://*.donmai.us/*",
"https://*.lolibooru.moe/*",
"https://*.allthefallen.moe/*"
],
"css": [],
"run_at": "document_start",
"js": [
"polyfill.min.js",
"dist/main.js"
]
}
],
"background": {
"persistent": true,
"scripts": [
"polyfill.min.js",
"browser-polyfill.min.js",
"dist/background.js"
]
}
}

2
firefox/polyfill.min.js

File diff suppressed because one or more lines are too long

1
firefox_update.json

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

3
main.d.ts

@ -59,4 +59,5 @@ declare const BUILD_VERSION: [number, number];
declare const execution_mode: 'userscript' | 'chrome_api' | 'ff_api';
declare const isBackground: boolean;
declare const chrome: typeof browser;
declare const _DOMParser: typeof DOMParser;
declare const _DOMParser: typeof DOMParser;
declare function GM_addElement(parent: HTMLElement, tagname: string, attrs: Record<string, string>)

10363
package-lock.json

File diff suppressed because it is too large

5
package.json

@ -8,7 +8,8 @@
"test": "echo \"Error: no test specified\" && exit 1",
"build": "node ./build.js",
"ver": "tsc -v",
"build2": "rollup -c",
"build_chrome": "node ./build-chrome.js",
"build_ff": "node ./build-ff.js",
"watch": "esbuild src/main.ts --bundle --outfile=dist/main.js --define:global=window --watch"
},
"author": "",
@ -34,6 +35,7 @@
"@types/lodash": "^4.14.181",
"@types/tampermonkey": "^4.0.5",
"@typescript-eslint/eslint-plugin": "^5.9.0",
"crx": "^5.0.1",
"esbuild": "^0.14.7",
"esbuild-css-modules-plugin": "^2.0.9",
"esbuild-svelte": "^0.6.0",
@ -42,6 +44,7 @@
"svelte-check": "^2.2.11",
"svelte-preprocess": "^4.10.1",
"typescript": "^4.5.4",
"web-ext": "^6.8.0",
"web-ext-types": "^3.2.1"
},
"browser": {

BIN
pngextraembedder-0.208-an+fx.xpi

Binary file not shown.

3
src/Components/App.svelte

@ -12,6 +12,7 @@
import { settings, appState } from "../stores";
import { filehosts } from "../filehosts";
import HydrusSearch from "./HydrusSearch.svelte";
import { ifetch } from "../platform";
let newbooru: Partial<Omit<Booru, "quirks"> & { view: string }> = {};
let dial: Dialog;
@ -50,7 +51,7 @@
if ($settings.phash) {
params = "?mdist=" + $settings.mdist;
}
let res = await fetch(
let res = await ifetch(
"https://shoujo.coom.tech/listing/" + boardname + params
);
threads = await res.json();

209
src/background.ts

@ -6,27 +6,210 @@ type Methods<T> = {
[k in Exclude<keyof T, 'prototype'>]: T[k] extends Function ? T[k] : never;
};
const types = [
"csp_report",
"font",
"image",
"main_frame",
"media",
"object",
"other",
"ping",
"script",
"stylesheet",
"sub_frame",
"websocket",
"xmlhttprequest"
] as browser.webRequest.ResourceType[];
const filts = {
urls: ["https://boards.4channel.org/*",
"https://boards.4chan.org/*",
"https://desuarchive.org/*",
"https://archived.moe/*",
"https://archive.nyafuu.org/*",
"https://arch.b4k.co/*",
"https://archive.wakarimasen.moe/*",
"https://fireden.net/*",
"https://thebarchive.com/*",
"https://files.catbox.moe/*",
"https://de.catbox.moe/*",
"https://based.coom.tech/*",
"https://archiveofsins.com/*"],
types
};
/*
obj.webRequest.onBeforeSendHeaders.addListener(
function (details: any) {
details.requestHeaders.push({ name: 'x-basldas', value: 'aslkhfqe' });
return { requestHeaders: details.requestHeaders };
},
{ urls: ["<all_urls>"], types },
["blocking", "requestHeaders"]
);
*/
//const ridh = {
//} as any;
//obj.webRequest.onCompleted.addListener(details => {
// if (details.requestId in ridh)
// delete ridh[details.requestId];
//}, filts);
obj.webRequest.onHeadersReceived.addListener(details => {
if (details.url.startsWith('https://arch.b4k.co/') && details.type == "main_frame") {
const e = details.responseHeaders!.findIndex(e => e.name.toLowerCase() == "content-security-policy");
if (e >= 0)
details.responseHeaders![e].value = "";
return {
responseHeaders: [
...details.responseHeaders!,
{ name: 'access-control-allow-origin', value: '*' }
]
} as browser.webRequest.BlockingResponse;
}
}, filts, ['blocking', 'responseHeaders', ...(execution_mode == "chrome_api" ? ['extraHeaders' as 'blocking'] : [])]);
/*
obj.webRequest.onBeforeSendHeaders.addListener((details) => {
if (ridh[details.requestId]) {
const res = ridh[details.requestId];
return {
requestHeaders: [res]
} as browser.webRequest.BlockingResponse;
}
}, filts, ['blocking']);
obj.webRequest.onBeforeRequest.addListener((details) => {
const redirectUrl = details.url;
if (!redirectUrl.startsWith("https://loli.piss/"))
return;
const m = redirectUrl.match(/https:\/\/loli.piss\/(?<domain>.*?)(?<path>\/.*)\/(?<start>.*)\/(?<end>.*)/);
if (!m)
let idx: number;
if ((idx = redirectUrl.indexOf(spe)) == -1)
return;
const { domain, path, start, end } = m.groups!;
const parts = redirectUrl.slice(idx + spe.length).split('/').filter(e => e);
const [domain, path, [start, end]] = [
parts[0],
parts.slice(1, -2).join('/'),
parts.slice(-2)
];
ridh[details.requestId] = {
name: "range",
value: `bytes=${start}-${end}`
};
return {
redirectUrl: `https://${domain}${path}`,
requestHeaders: [{
name: 'range',
value: `bytes=${start}-${end}`
}]
redirectUrl: `https://${domain}/${path}`,
requestHeaders: [{ name: 'x-akjflkd', value: 'qofh3r3' }]
} as browser.webRequest.BlockingResponse;
}, { urls: ['*://loli.piss/*'] }, ['blocking']);
}, filts, ['blocking']);
*/
const pendingFetches = new Map<browser.runtime.Port, { [id in number]: { fetchFully: boolean } }>();
const bgCorsFetch = async (c: browser.runtime.Port, id: number, input: string, init?: RequestInit) => {
/*if (typeof init?.signal == "number") {
const id = init?.signal as any as number;
const ab = new AbortController();
init.signal = ab.signal;
}*/
if (input.startsWith('//')) // wtf fireshit??
input = 'https:' + input;
const k = await fetch(input, init);
let headersStr = '';
const headerObj = {} as any;
k.headers.forEach((v, k) => {
headerObj[k] = v;
headersStr += `${k}: ${v}\n`;
});
c.postMessage({
id, setRes: true,
ok: k.ok,
headers: headerObj,
responseHeaders: headersStr,
redirected: k.redirected,
type: k.type,
url: k.url,
status: k.status,
bodyUsed: k.bodyUsed,
statusText: k.statusText,
});
pendingFetches.set(c, {
[id]: {
fetchFully: false
},
...(pendingFetches.get(c) || {})
});
let buff: Buffer[] = [];
// sequence number, required to reorder messages client-side
// if they get processed out of order
let s = 0;
const e = {
write(chunk: Uint8Array) {
if (!pendingFetches.get(c)![id].fetchFully) {
const url = URL.createObjectURL(new Blob([chunk]));
console.log('created blob of size', chunk.byteLength);
c.postMessage({ id, s: s++, pushData: { data: url } });
} else {
buff.push(Buffer.from(chunk));
}
},
close() {
if (buff.length > 0) {
const url = URL.createObjectURL(new Blob([Buffer.concat(buff)]));
c.postMessage({ id, s: s++, pushData: { data: url } });
buff = [];
}
const obj = pendingFetches.get(c)!;
delete obj[id];
if (Object.keys(obj).length == 0)
pendingFetches.delete(c);
c.postMessage({ id, s: s++, pushData: {} });
}
};
const reader = k.body?.getReader();
let res: ReadableStreamDefaultReadResult<Uint8Array>;
for (; ;) {
res = await reader!.read();
if (res.done) break;
e.write(res.value);
}
e.close();
reader?.releaseLock();
};
obj.runtime.onConnect.addListener((c) => {
c.onMessage.addListener(async obj => {
const { id, name, args } = obj as {id: number, name: keyof Methods<typeof Platform>, args: Parameters<typeof Platform[keyof Methods<typeof Platform>]>};
const res = await Platform[name](...args);
const { id, name, args, sid, fid, url } = obj as { url?: string, fid?: number, sid?: number, id: number, name: string, args: Parameters<typeof Platform[keyof Methods<typeof Platform>]> };
if (name == "abortCorsFetch") {
//chrome.runtime.sendMessage({ name, sid });
return;
}
if (name == "corsFetch") {
// this handles the reply
(bgCorsFetch as any)(c, id, ...args);
return;
}
if (name == "revoke") {
URL.revokeObjectURL(url!);
c.postMessage({
id, ok: 1
});
return;
}
if (name == "fullyRead") {
const obj = pendingFetches.get(c)!;
if (obj && fid! in obj)
obj[fid!].fetchFully = true;
c.postMessage({
id, ok: 1
});
return;
}
const res = await (Platform as any)[name](...args);
c.postMessage({
id, res
});

4
src/hydrus.ts

@ -1,3 +1,5 @@
import { ifetch } from "./platform";
type TagList = (string | TagList)[];
export interface MyTags {
@ -62,7 +64,7 @@ export class HydrusClient {
}
async get(params: string) {
return await fetch(this.baseUrl + params, {
return await ifetch(this.baseUrl + params, {
headers: {
'Hydrus-Client-API-Access-Key': this.ak
}

219
src/main.ts

@ -20,11 +20,10 @@ import NotificationsHandler from './Components/NotificationsHandler.svelte';
import { fireNotification, getSelectedFile } from "./utils";
import { getQueryProcessor, QueryProcessor } from "./websites";
import { ifetch, streamRemote, supportedAltDomain } from "./platform";
import { ifetch, Platform, streamRemote, supportedAltDomain } from "./platform";
import TextEmbeddingsSvelte from "./Components/TextEmbeddings.svelte";
import { HydrusClient } from "./hydrus";
import { registerPlugin } from 'linkifyjs';
import { GM_fetch } from "./requests";
export interface ImageProcessor {
skip?: true;
@ -130,6 +129,7 @@ const processImage = async (srcs: AsyncGenerator<string, void, void>, fn: string
onfound();
return [await proc.extract(cumul), false] as [EmbeddedFile[], boolean];
} catch {
debugger;
// ignore error and retry with another link
}
} while (!succ);
@ -151,7 +151,7 @@ const signalNewEmbeds = debounce(async () => {
const reshaped = Object.fromEntries([...new Set(pendingPosts.map(e => e.op))].map(e => [e, pendingPosts.filter(p => p.op == e).map(e => e.id)]));
console.log(reshaped);
const res = await fetch("https://shoujo.coom.tech/listing/" + boardname, {
const res = await ifetch("https://shoujo.coom.tech/listing/" + boardname, {
method: "POST",
body: JSON.stringify(reshaped),
headers: {
@ -195,12 +195,11 @@ const processPost = async (post: HTMLDivElement) => {
};
const versionCheck = async () => {
const [lmajor, lminor] =
(await (await ifetch("https://git.coom.tech/coomdev/PEE/raw/branch/%e4%b8%ad%e5%87%ba%e3%81%97/main.meta.js"))
.text())
.split('\n')
.filter(e => e.includes("// @version"))[0].match(/.*version\s+(.*)/)![1].split('.')
.map(e => +e);
const txt = (await (await ifetch("https://git.coom.tech/coomdev/PEE/raw/branch/%e4%b8%ad%e5%87%ba%e3%81%97/main.meta.js")).text());
debugger;
const [lmajor, lminor] = txt.split('\n')
.filter(e => e.includes("// @version"))[0].match(/.*version\s+(.*)/)![1].split('.')
.map(e => +e);
const [major, minor] = BUILD_VERSION;
if (major < lmajor || (major == lmajor && minor < lminor)) {
fireNotification("info", `Last PEE version is ${lmajor}.${lminor}, you're on ${major}.${minor}`);
@ -328,102 +327,54 @@ const scrapeBoard = async (self: HTMLButtonElement) => {
};
};
const cleanupHTML = async (s: string) => {
const ndom = new _DOMParser().parseFromString(s, "text/html");
const __DOMParser = execution_mode == "userscript" ? _DOMParser : DOMParser;
const cleanupHTML = (ndom: Document) => {
const evalWhenReady: string[] = [];
const addFromSource = async (elem: HTMLElement, url: string) => {
/* const scr = document.createElement('script');
scr.type = 'text/javascript';
scr.src = url;
scr.addEventListener("load", e => console.log('url loaded', e));
elem.append(scr);*/
try {
const code = await (await GM_fetch(url)).text();
evalWhenReady.push(code);
} catch (e) {
console.error(e);
debugger;
}
const addFromSource = (elem: HTMLElement, url: string) => {
const scr = document.createElement('script');
scr.type = 'text/javascript';
scr.src = url;
elem.append(scr);
};
const addFromCode = (elem: HTMLElement, sr: string) => {
/* const scr = document.createElement('script');
scr.type = 'text/javascript';
scr.innerText = sr;
scr.addEventListener("load", e => console.log('code loaded', e));
elem.append(scr);*/
//eval(sr);
evalWhenReady.push(sr);
const scr = document.createElement('script');
scr.type = 'text/javascript';
scr.innerText = sr;
elem.append(scr);
};
[...ndom.head.children].filter(e => e.tagName == "SCRIPT").forEach(e => e.remove());
[...ndom.body.children].filter(e => e.tagName == "SCRIPT").forEach(e => e.remove());
unsafeWindow['isEventSupported'] = () => false;
await addFromSource(ndom.body, "https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js");
await addFromSource(ndom.head, "https://based.coom.tech/highlight.pack.js");
addFromCode(ndom.body, `
document.documentElement.className = "";
document.documentElement.lang = "en";
document.documentElement.dataset.site = "arch.b4k.co";`);
addFromCode(ndom.body, `
hljs.configure({
tableReplace: ' '
});
$('pre,code').each(function(i, block) {
hljs.highlightBlock(block);
});
var backend_vars = {"user_name":false,"user_email":false,"user_pass":"9fOK4K8","site_url":"https://arch.b4k.co/","default_url":"https://arch.b4k.co/","archive_url":"https://arch.b4k.co/","system_url":"https://arch.b4k.co/","api_url":"https://arch.b4k.co/","cookie_domain":null,"cookie_prefix":"foolfuuka_a2e7d4_","selected_theme":"foolz/foolfuuka-theme-foolfuuka","csrf_token_key":"csrf_token","images":{"banned_image":"https://arch.b4k.co/foolfuuka/foolz/foolfuuka-theme-foolfuuka/assets-1.2.28/images/banned-image.png","banned_image_width":150,"banned_image_height":150,"missing_image":"https://arch.b4k.co/foolfuuka/foolz/foolfuuka-theme-foolfuuka/assets-1.2.28/images/missing-image.jpg","missing_image_width":150,"missing_image_height":150},"gettext":{"submit_state":"Submitting","thread_is_real_time":"This thread is being displayed in real time.","update_now":"Update now","ghost_mode":"This thread has entered ghost mode. Your reply will be marked as a ghost post and will only affect the ghost index."},"board_shortname":"v"};`);
// head
//body
{
await addFromSource(ndom.body, "https://based.coom.tech/bootstrap.min.js");
await addFromSource(ndom.body, "https://based.coom.tech/plugins.js");
await addFromSource(ndom.body, "https://based.coom.tech/board.js");
await addFromSource(ndom.body, "https://based.coom.tech/fuuka.js");
await addFromSource(ndom.body, "https://based.coom.tech/lazyload.js");
}
const rm = (e: any) => e.remove();
[...ndom.head.children].filter(e => e.tagName == "SCRIPT").forEach(rm);
[...ndom.body.children].filter(e => e.tagName == "SCRIPT").forEach(rm);
addFromSource(ndom.body, "https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js");
addFromSource(ndom.head, "https://based.coom.tech/highlight.pack.js");
/*
addFromCode(ndom.body, `document.documentElement.className = "";document.documentElement.lang = "en";document.documentElement.dataset.site = "arch.b4k.co";`);
addFromCode(ndom.body, `hljs.configure({ tableReplace: ' ' }); $('pre,code').each(function(i, block) { hljs.highlightBlock(block); }); var backend_vars = {"user_name":false,"user_email":false,"user_pass":"9fOK4K8","site_url":"https://arch.b4k.co/","default_url":"https://arch.b4k.co/","archive_url":"https://arch.b4k.co/","system_url":"https://arch.b4k.co/","api_url":"https://arch.b4k.co/","cookie_domain":null,"cookie_prefix":"foolfuuka_a2e7d4_","selected_theme":"foolz/foolfuuka-theme-foolfuuka","csrf_token_key":"csrf_token","images":{"banned_image":"https://arch.b4k.co/foolfuuka/foolz/foolfuuka-theme-foolfuuka/assets-1.2.28/images/banned-image.png","banned_image_width":150,"banned_image_height":150,"missing_image":"https://arch.b4k.co/foolfuuka/foolz/foolfuuka-theme-foolfuuka/assets-1.2.28/images/missing-image.jpg","missing_image_width":150,"missing_image_height":150},"gettext":{"submit_state":"Submitting","thread_is_real_time":"This thread is being displayed in real time.","update_now":"Update now","ghost_mode":"This thread has entered ghost mode. Your reply will be marked as a ghost post and will only affect the ghost index."},"board_shortname":"v"};`);
// head
//body
await addFromSource(ndom.body, "https://based.coom.tech/bootstrap.min.js");
await addFromSource(ndom.body, "https://based.coom.tech/plugins.js");
await addFromSource(ndom.body, "https://based.coom.tech/board.js");
await addFromSource(ndom.body, "https://based.coom.tech/fuuka.js");
await addFromSource(ndom.body, "https://based.coom.tech/lazyload.js");
*/
return [ndom.documentElement.innerHTML, evalWhenReady] as [string, string[]];
};
let gmo: MutationObserver;
let ispostreload = false;
const earlystartup = async () => {
if (location.host == 'arch.b4k.co') {
if (GM.info.version == "2.13.0") {
alert(`Due to b4k's admin being a faggot, PEE will get you banned if you're not using Greasemonkey Beta and enabled "Synchronous page mode" or TamperMonkey with Instant Injection`);
alert("Because you use the regular GM, PEE will disable itself on this domain");
if (location.host == 'arch.b4k.co' && execution_mode == "userscript") {
if (!GM_getValue("warning_seen2", false)) {
alert(`Due to b4k's admin being a faggot, PEE will get you banned, so the userscript version is disabled here`);
alert("Use the WebExtension version of PEE if you want to use b4k. Cool new features will be coming to it, too!");
GM_setValue("warning_seen2", true);
return false;
}
if (!GM_getValue("warning_seen", false)) {
alert(`Due to b4k's admin being a faggot, PEE will get you banned if you're not using Greasemonkey Beta and enabled "Synchronous page mode" or TamperMonkey with Instant Injection`);
alert("Make sure you have enabled \"Synchronous page mode\" on VM Beta or Instant Injection on TM");
if (!confirm("Proceed?")) {
return false;
}
GM_setValue("warning_seen", true);
}
document.documentElement.innerHTML = '';
const k = await GM_fetch(location.href);
const src = await k.text();
gmo.disconnect();
ispostreload = true;
const [code, scripts] = await cleanupHTML(src);
unsafeWindow.document.documentElement.innerHTML = code;
await new Promise(r => setTimeout(r, 500));
//unsafeWindow.document.documentElement.replaceWith(unsafeWindow.document.documentElement.cloneNode(true));
for (const s of scripts)
unsafeWindow.eval(s);
unsafeWindow.dispatchEvent(new CustomEvent("load"));
// out of spite:
document.querySelectorAll<HTMLImageElement>('img[data-src]').forEach(i => {
i.src = i.getAttribute('data-src')!;
});
}
return true;
};
@ -431,7 +382,7 @@ const earlystartup = async () => {
const startup = async (is4chanX = true) => {
const meta = document.querySelector('meta[name="referrer"]') as HTMLMetaElement;
const customStyles = document.createElement('style');
//return;
customStyles.appendChild(document.createTextNode(globalCss));
document.documentElement.insertBefore(customStyles, null);
@ -471,14 +422,18 @@ const startup = async (is4chanX = true) => {
registerPlugin('quote', postQuote);
if (!is4chanX && location.host.startsWith('boards.4chan')) {
const qr = QR;
const show = qr.show.bind(qr);
qr.show = (...args: any[]) => {
show(...args);
document.dispatchEvent(new CustomEvent("QRDialogCreation", {
detail: document.getElementById('quickReply')
}));
};
(async () => {
while (!('QR' in window))
await new Promise(_ => setTimeout(_, 250));
const qr = QR;
const show = qr.show.bind(qr);
qr.show = (...args: any[]) => {
show(...args);
document.dispatchEvent(new CustomEvent("QRDialogCreation", {
detail: document.getElementById('quickReply')
}));
};
})();
document.addEventListener("QRGetFile", (e) => {
const qr = document.getElementById('qrFile') as HTMLInputElement | null;
@ -492,12 +447,6 @@ const startup = async (is4chanX = true) => {
dt.items.add(new File([e.detail.file], e.detail.name));
qr.files = dt.files;
}) as any);
const notificationHost = document.createElement('span');
new NotificationsHandler({
target: notificationHost
});
document.body.append(notificationHost);
}
//await Promise.all([...document.querySelectorAll('.postContainer')].filter(e => e.textContent?.includes("191 KB")).map(e => processPost(e as any)));
@ -520,14 +469,33 @@ const startup = async (is4chanX = true) => {
document.querySelectorAll('.board').forEach(e => {
mo.observe(e!, { childList: true, subtree: true });
});
const posts = qp.postsWithFiles();
const scts = qp.settingsHost();
const button = textToElement(`<span></span>`);
const settingsButton = new SettingsButton({
target: button
});
scts?.appendChild(button);
if (!document.body) {
let bodyRes: any;
const bodyInit = new Promise(r => bodyRes = r);
const mo2 = new MutationObserver(r => {
if (document.body) {
mo2.disconnect();
bodyRes();
}
});
mo2.observe(document.documentElement, { childList: true, subtree: true });
await bodyInit;
}
if (!is4chanX && location.host.startsWith('boards.4chan')) {
const notificationHost = document.createElement('span');
new NotificationsHandler({
target: notificationHost
});
document.body.append(notificationHost);
}
if (location.host == 'arch.b4k.co') {
document.querySelectorAll<HTMLImageElement>('img[data-src]').forEach(i => {
i.src = i.getAttribute('data-src')!;
});
}
const appHost = textToElement(`<div class="peee-settings"></div>`);
const appInstance = new App({ target: appHost });
@ -537,6 +505,14 @@ const startup = async (is4chanX = true) => {
new ScrollHighlighter({ target: scrollHost });
document.body.append(scrollHost);
const posts = qp.postsWithFiles();
const scts = qp.settingsHost();
const button = textToElement(`<span></span>`);
const settingsButton = new SettingsButton({
target: button
});
scts?.appendChild(button);
appState.set({
...cappState,
isCatalog: !!document.querySelector('.catalog-small') || !!location.pathname.match(/\/catalog$/),
@ -567,16 +543,25 @@ const startup = async (is4chanX = true) => {
//await Promise.all(posts.map(e => processPost(e as any)));
};
document.addEventListener('4chanXInitFinished', () => startup(true));
document.addEventListener('4chanParsingDone', () => startup(false), { once: true });
document.addEventListener('4chanMainInit', () => startup(false), { once: true });
if (supportedAltDomain(location.host)) {
if (location.host == 'arch.b4k.co') {
gmo = new MutationObserver(m => {
for (const r of m) {
if (ispostreload) debugger;
r.addedNodes.forEach(e => {
if ((e as any).tagName == "SCRIPT") {
if (e.parentElement?.tagName == "HEAD" || e.parentElement?.tagName == "BODY")
e.parentElement?.removeChild(e);
const scr = e as HTMLScriptElement;
if (scr.src.startsWith('https://arch.b4k.co/') || scr.src.startsWith('https://b4k.co/')) {
let file = scr.src.slice(scr.src.lastIndexOf('/') + 1);
if (file.includes('?'))
file = file.slice(0, file.lastIndexOf('?'));
scr.src = `https://based.coom.tech/` + file;
return;
}
if ((scr.src && !scr.src.startsWith('https://ajax.googleapis.com/')) || scr.innerHTML.includes('googletagmanager') || scr.src.startsWith("data:")) {
console.log(scr);
scr.parentElement?.removeChild(scr);
}
}
});
}

266
src/platform.ts

@ -1,38 +1,37 @@
import { Buffer } from 'ts-ebml/lib/tools';
import { GM_fetch, GM_head, headerStringToObject } from './requests';
let port: browser.runtime.Port;
const lqueue: ((e: any) => boolean)[] = [];
const lqueue = {} as any;
console.log(execution_mode, isBackground);
if (execution_mode != 'userscript' && !isBackground) {
port = browser.runtime.connect();
// It has to be a content script
port = (chrome || browser).runtime.connect();
port.onMessage.addListener((e: any) => {
const k = lqueue.map(f => f(e));
for (let i = k.length - 1; i != -1; --i) {
if (k[i])
lqueue.splice(i, 1);
}
lqueue[e.id](e);
});
}
let gid = 0;
const sendCmd = <V>(cmd: any) => {
const prom = new Promise<V>(_ => {
const id = gid++;
lqueue[id] = (e: any) => {
_(e.res);
delete lqueue[id];
};
port.postMessage({ id, ...cmd });
});
return prom;
};
const bridge = <U extends any[], V, T extends (...args: U) => V>(name: string, f: T) => {
if (execution_mode != 'userscript' && !isBackground)
return f;
// It has to be the background script
return (...args: U) => {
const id = gid++;
const prom = new Promise<V>(_ => {
lqueue.push((e: any) => {
if (e.id != id)
return false;
_(e.res);
return true;
});
port.postMessage({
id, name, args
});
});
return prom;
return sendCmd({ name, args });
};
};
@ -46,7 +45,7 @@ const Bridged = (ctor: any) => {
export function supportedAltDomain(s: string) {
if (execution_mode == 'userscript')
return GM.info.script.matches.slice(2).some(m => m.includes(s));
return false;
return !location.host.includes('boards.4chan');
}
// Used to call background-only APIs from content scripts
@ -57,61 +56,218 @@ export class Platform {
return GM.openInTab(src, opts);
}
const obj = execution_mode == "chrome_api" ? chrome : browser;
if (execution_mode == 'chrome_api') {
let i: number | undefined;
if (opts.insert)
i = (await obj.tabs.getCurrent()).index + 1;
return obj.tabs.create({ active: opts.active, url: src, index: i });
}
let i: number | undefined;
if (opts.insert)
i = (await obj.tabs.getCurrent()).index + 1;
return obj.tabs.create({ active: opts.active, url: src, index: i });
}
}
const extrBlob = async (url: string) => {
const ret = await (await fetch(url)).arrayBuffer();
await sendCmd({ name: 'revoke', url });
return new Uint8Array(ret);
};
export const corsFetch = async (input: string, init?: RequestInit) => {
const id = gid++;
/* if (init) {
if (init.signal) {
const sid = gid++;
init.signal.addEventListener("abort", () => {
port.postMessage({ name: 'abortCorsFetch', sid });
});
(init as any).signal = sid as any;
}
}*/
const prom = new Promise<Awaited<ReturnType<typeof fetch>>>((_, rej) => {
let gcontroller: ReadableStreamController<Uint8Array> | undefined;
let buffer: Uint8Array[] = [];
let finished = false;
const rs = new ReadableStream<Uint8Array>({
// I think start is not called immediately, but when something tries to pull the response
start(controller) {
// something is finally ready to read
gcontroller = controller;
// flush buffer
buffer.forEach(b => gcontroller?.enqueue(b));
buffer = [];
if (finished) {
gcontroller.close();
}
}
});
// seq num... see background script for explanation
let s: number;
s = 0;
const cmdbuff: any[] = [];
lqueue[id] = (async (e: any) => {
if (e.pushData) {
if (e.s > s) {
// insert before an hypothetical cmd with a higher seq number
// -1 will be returned on empty arrays, which still results in correct insertion
let idx = 0;
while (idx < cmdbuff.length) {
if (cmdbuff[idx].s > e.s)
break;
idx++;
}
cmdbuff.splice(idx, 0, e);
return;
}
// since we start from 0 and
// don't accept command s > local s,
// then these must be equal
// this also means that cmdbuff must contain 0 or more ordered commands that must be processed
// afterward until discontinuity
const processCmd = async (e: any) => {
if (e.pushData.data) {
const data = await extrBlob(e.pushData.data);
if (gcontroller)
gcontroller.enqueue(data);
else
buffer.push(data);
} else {
if (gcontroller)
gcontroller?.close();
else
finished = true;
}
};
await processCmd(e);
s++;
// process remaining sequential buffered commands
while (cmdbuff[0]?.s == s) {
await processCmd(cmdbuff.shift());
s++;
}
}
if (e.setRes) {
const arrayBuffer = async () => {
// read the response fully
const r = rs.getReader();
await sendCmd({ name: 'fullyRead', fid: id });
const abs: Uint8Array[] = [];
let res: ReadableStreamDefaultReadResult<Uint8Array>;
do {
res = await r.read();
if (res.done) break;
abs.push(res.value);
} while (!res.done);
const sum = abs.reduce((a, b) => a + b.byteLength, 0);
const ret = new Uint8Array(sum);
abs.reduce((ptr, arr) => {
ret.set(arr, ptr);
return ptr + arr.byteLength;
}, 0);
r.releaseLock();
return ret;
};
const blob = async () => {
return new Blob([await arrayBuffer()]);
};
const text = async () => {
return new TextDecoder().decode(await arrayBuffer());
};
const json = async () => {
return JSON.parse(await text());
};
if (e.ok)
_({
body: rs,
ok: e.ok,
headers: e.headers,
redirected: e.redirected,
type: e.type,
url: e.url,
status: e.status,
bodyUsed: e.bodyUsed,
statusText: e.statusText,
clone() {
return this as Response;
},
arrayBuffer,
blob,
text,
json,
async formData() {
return new FormData;
}
});
else {
rej(new Error(`${e.url} - ${e.status}`));
}
}
});
port.postMessage({
id, name: 'corsFetch', args: [input, init]
});
});
return prom;
};
export async function getHeaders(s: string) {
if (execution_mode == 'userscript')
return headerStringToObject(await GM_head(s));
const res = await fetch(s, {
const res = await ifetch(s, {
method: "HEAD"
});
return [...res.headers.entries()].reduce((a, b) => (a[b[0]] = b[1], a), {} as ReturnType<typeof headerStringToObject>);
return res.headers as any as Record<string, string>;
}
export async function ifetch(...[url, opt, lisn]: [...Parameters<typeof fetch>, EventTarget?]): ReturnType<typeof fetch> {
if (execution_mode != "userscript")
return fetch(url, opt);
return corsFetch(url.toString(), opt);
return GM_fetch(url, opt, lisn);
}
// most pngs are encoded with 65k idat chunks
export async function* streamRemote(url: string, chunkSize = 72 * 1024, fetchRestOnNonCanceled = true) {
if (execution_mode != 'userscript') {
const res = await fetch(url);
const reader = res.body;
const stream = reader?.getReader();
while (!stream?.closed) {
const buff = await stream?.read();
if (buff?.done) {
break;
}
if (buff?.value) {
const e = (yield buff.value) as boolean;
if (e) {
stream?.cancel();
reader?.cancel();
break;
}
}
}
stream?.releaseLock();
return;
}
// if (false) {
// const res = await corsFetch(url);
// const reader = res.body;
// const stream = reader?.getReader();
// while (!stream?.closed) {
// const buff = await stream?.read();
// if (buff?.done) {
// break;
// }
// if (buff?.value) {
// const e = (yield buff.value) as boolean;
// if (e) {
// stream?.cancel();
// reader?.cancel();
// break;
// }
// }
// }
// stream?.releaseLock();
// return;
// }
//const headers = await getHeaders(url);
let size = Number.POSITIVE_INFINITY;
let ptr = 0;
let fetchSize = chunkSize;
while (ptr != size) {
//console.log('doing a fetch of ', url, ptr, ptr + fetchSize - 1);
const res = await ifetch(url, { headers: { range: `bytes=${ptr}-${ptr + fetchSize - 1}` } }) as any as Tampermonkey.Response<any>;
const obj = headerStringToObject(res.responseHeaders);
let obj: Record<string, string>;
const fres = await ifetch(url, { headers: { range: `bytes=${ptr}-${ptr + fetchSize - 1}` } });
if (execution_mode == "userscript") {
obj = headerStringToObject((fres as any as Tampermonkey.Response<any>).responseHeaders);
} else {
obj = (fres as any as Response).headers as any;
}
if (!('content-length' in obj)) {
console.warn("no content lenght???", url);
break;
@ -123,7 +279,7 @@ export async function* streamRemote(url: string, chunkSize = 72 * 1024, fetchRes
ptr += len;
if (fetchRestOnNonCanceled)
fetchSize = size;
const val = Buffer.from(await (res as any).arrayBuffer());
const val = Buffer.from(await (fres as any).arrayBuffer());
const e = (yield val) as boolean;
//console.log('yeieledd, a', e);
if (e) {

12
src/requests.ts

@ -1,5 +1,3 @@
import { localLoad, settings } from "./stores";
const xmlhttprequest = typeof GM_xmlhttpRequest != 'undefined' ?
GM_xmlhttpRequest :
(typeof GM != "undefined" ?
@ -114,10 +112,10 @@ const makePoolable = <T extends any[], U>(fun: (...args: T) => Promise<U>, getPo
};
};
let csettings: Parameters<typeof settings['set']>[0] = localLoad('settingsv2', {} as any);
// let csettings: Parameters<typeof settings['set']>[0] = localLoad('settingsv2', {} as any);
settings.subscribe(s => {
csettings = s;
});
// settings.subscribe(s => {
// csettings = s;
// });
const poolFetch = makePoolable(GM_fetch, () => csettings.conc);
// const poolFetch = makePoolable(GM_fetch, () => csettings.conc);

5
src/thirdeye.ts

@ -90,7 +90,7 @@ const processQueries = async () => {
const md5 = reqQueue.map(e => e[0]).filter(e => !(e in queryCache));
expired = undefined;
if (md5.length > 0) {
const res = await fetch("https://shoujo.coom.tech/api", {
const res = await ifetch("https://shoujo.coom.tech/api", {
method: "POST",
body: JSON.stringify({ md5 }),
headers: {
@ -135,7 +135,8 @@ const findFileFrom = async (b: Booru, hex: string, abort?: EventTarget) => {
return cache[b.domain][hex] as BooruMatch[];
const res = await ifetch(`https://${b.domain}${b.endpoint}${hex}`);
// might throw because some endpoint respond with invalid json when an error occurs
const pres = await res.json();
const txt = await res.text();
const pres = JSON.parse(txt);
const tran = b.quirks(pres).filter(e => !e.tags.some(e => black.has(e)));
if (!(b.domain in cache))
cache[b.domain] = {};

27
src/utils.ts

@ -5,7 +5,6 @@ import { settings } from "./stores";
import { filehosts } from "./filehosts";
import { getHeaders, ifetch, Platform } from "./platform";
import type { HydrusClient } from "./hydrus";
import { GM_fetch } from "./requests";
import { fileTypeFromBuffer } from "file-type";
export let csettings: Parameters<typeof settings['set']>[0];
@ -75,7 +74,7 @@ export const buildPeeFile = async (f: File) => {
f.size /*Teh file*/);
let ptr = 0;
ret.write('PEE\0', 0);
ptr += 4;
ptr += 4;
ret[ptr++] = 1 | ((+(thumbnail.length != 0)) << 2);
namebuf.copy(ret, ptr);
ptr += namebuf.byteLength;
@ -149,23 +148,23 @@ export const decodeCoom3Payload = async (buff: Buffer) => {
if (hasThumbnail) {
thumbsize = header.readInt32LE(ptr);
ptr += 4;
if (execution_mode == 'userscript')
thumb = Buffer.from(await (await ifetch(pee, { headers: { 'user-agent': '', range: `bytes=${ptr}-${ptr + thumbsize}` } })).arrayBuffer());
else
thumb = `https://loli.piss/${domain}${file}/${ptr}/${ptr + thumbsize}`;
// if (execution_mode == 'userscript')
thumb = Buffer.from(await (await ifetch(pee, { headers: { 'user-agent': '', range: `bytes=${ptr}-${ptr + thumbsize}` } })).arrayBuffer());
// else
// thumb = `https://loli.piss/${domain}${file}/${ptr}/${ptr + thumbsize}`;
ptr += thumbsize;
}
const unzip = async (lsn?: EventTarget) =>
Buffer.from(await (await ifetch(pee, { headers: { 'user-agent': '', range: `bytes=${ptr}-${size - 1}` } }, lsn)).arrayBuffer());
let data;
if (execution_mode == 'userscript') {
data = unzip;
if (size < 3072) {
thumb = data = await unzip();
}
} else {
data = `https://loli.piss/${domain}${file}/${ptr}/${size - 1}`;
// if (execution_mode == 'userscript') {
data = unzip;
if (size < 3072) {
thumb = data = await unzip();
}
// } else {
// data = `https://loli.piss/${domain}${file}/${ptr}/${size - 1}`;
// }
return {
filename: fn,
// if file is small, then just get it fully
@ -218,7 +217,7 @@ export async function embeddedToBlob(...efs: EmbeddedFile[]) {
return (await Promise.all(efs.map(async ef => {
let buff: Buffer;
if (typeof ef.data == "string") {
const req = await GM_fetch(ef.data);
const req = await ifetch(ef.data);
buff = Buffer.from(await req.arrayBuffer());
} else if (!Buffer.isBuffer(ef.data))
buff = await ef.data();

4
src/websites/index.ts

@ -1,4 +1,4 @@
import { GM_fetch } from "../requests";
import { ifetch } from "../platform";
export type QueryProcessor = {
getPost: (post: HTMLElement) => HTMLElement;
@ -71,7 +71,7 @@ export const FoolFuuka: QueryProcessor = {
if (location.host == "arch.b4k.co") { //get fucked
const pid = post.id.match(/\d+/)![0];
const board = location.pathname.match(/\/(..?.?)\//)![1];
const res = await GM_fetch(`https://archive.wakarimasen.moe/_/api/chan/post/?board=${board}&num=${pid}`);
const res = await ifetch(`https://archive.wakarimasen.moe/_/api/chan/post/?board=${board}&num=${pid}`);
const data = await res.json();
yield data.media.media_link;
}

6
update.xml

@ -0,0 +1,6 @@
<?xml version='1.0' encoding='UTF-8'?>
<gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>
<app appid='ilffidhdekahjldemialkgahicnajchb'>
<updatecheck codebase='http://localhost:8000/myExtension.crx' version='0.208' prodversionmin='64.0.3242' />
</app>
</gupdate>
Loading…
Cancel
Save