Browse Source

New hosts, organize settings, move some files...

pull/46/head
coomdev 2 years ago
parent
commit
986b687820
  1. 2
      main.meta.js
  2. 4548
      main.user.js
  3. 255
      src/App.svelte
  4. 286
      src/Components/App.svelte
  5. 0
      src/Components/Dialog.svelte
  6. 10
      src/Components/Embedding.svelte
  7. 2
      src/Components/Embeddings.svelte
  8. 4
      src/Components/EyeButton.svelte
  9. 2
      src/Components/NotificationsHandler.svelte
  10. 14
      src/Components/PostOptions.svelte
  11. 5
      src/Components/ScrollHighlighter.svelte
  12. 0
      src/Components/SettingsButton.svelte
  13. 34
      src/Components/Tab.svelte
  14. 9
      src/Components/TabList.svelte
  15. 12
      src/Components/TabPanel.svelte
  16. 63
      src/Components/Tabs.svelte
  17. 0
      src/Components/Tag.svelte
  18. 47
      src/filehosts.ts
  19. 29
      src/main.ts
  20. 2
      src/stores.ts
  21. 20
      src/utils.ts

2
main.meta.js

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

4548
main.user.js

File diff suppressed because it is too large

255
src/App.svelte

@ -1,255 +0,0 @@
<script lang="ts">
import { hasContext, onDestroy } from 'svelte'
import Dialog from './Dialog.svelte';
import { settings } from './stores'
import Tag from './Tag.svelte'
import type { Booru } from './thirdeye';
let newbooru: Partial<Omit<Booru, 'quirks'> & {view: string}> = {};
let dial: Dialog;
function appendBooru() {
$settings.rsources = [...$settings.rsources, newbooru as any];
dial.toggle();
newbooru = {}
}
let visible = false
let penisEvent = () => {
console.log('bepis')
visible = !visible
}
document.addEventListener('penis', penisEvent)
console.log('app loaded')
function removeTag(t: string) {
$settings.blacklist = $settings.blacklist.filter((e: any) => e != t)
}
function removeBooru(t: string) {
const idx = $settings.rsources.findIndex(e => e.domain == t)
const rep = prompt("You DO know what you're doing, right? (type 'y')")
if (!rep || rep != 'y') return
if (idx >= 0) $settings.rsources.splice(idx, 1)
$settings.rsources = $settings.rsources
}
function toggleBooru(t: string) {
const elem = $settings.rsources.find(e => e.domain == t)
if (elem)
elem.disabled = !elem.disabled;
$settings.rsources = $settings.rsources
}
onDestroy(() => {
document.removeEventListener('penis', penisEvent)
})
</script>
<div class="backpanel" class:enabled={visible} class:disabled={!visible}>
<div class="content">
<h1>PEE Settings</h1>
<hr />
<label>
<input type="checkbox" bind:checked={$settings.vercheck} />
Check for new versions at startup.
</label>
<label>
<input type="checkbox" bind:checked={$settings.xpi} />
Autoexpand Images on opening.
</label>
<label>
<input type="checkbox" bind:checked={$settings.xpv} />
Autoexpand Videos on opening.
</label>
<label>
<input type="checkbox" bind:checked={$settings.loop} />
Loop media content.
</label>
<label>
<input type="checkbox" bind:checked={$settings.dh} />
Disable hover preview.
</label>
<label>
<input type="checkbox" bind:checked={$settings.eye} />
Hide embedded content behind an eye.
</label>
{#if $settings.eye}
<label>
<input type="checkbox" bind:checked={$settings.ho} />
Hide original content when hidden content is visible.
</label>
{/if}
<label>
<input type="checkbox" bind:checked={$settings.pre} />
Preload external files.
</label>
<label>
<input type="checkbox" bind:checked={$settings.prev} />
Preload external files when they are in view.
</label>
<label>
<input type="checkbox" bind:checked={$settings.hotlink} />
Hotlink content.
</label>
<label>
<input type="checkbox" bind:checked={$settings.ca} />
Control audio on videos with mouse wheel.
</label>
<label>
<input type="checkbox" bind:checked={$settings.sh} />
Show Minimap
</label>
<label>
<input type="checkbox" bind:checked={$settings.ep} />
<!-- svelte-ignore a11y-missing-attribute -->
Disable embedded file preloading<a
title="You might still want to enable 'preload external files'">?</a
>
</label>
<label>
<input type="checkbox" bind:checked={$settings.te} />
Disable third-eye.
</label>
{#if !$settings.te}
<h3>Booru sources</h3>
<div class="tagcont">
{#each $settings.rsources as source, i}
<Tag
tag={source.name}
on:remove={() => removeBooru(source.domain)}
on:toggle={() => toggleBooru(source.domain)}
toggleable={true}
toggled={!$settings.rsources.find(e => e.domain == source.domain)
?.disabled}
/>
{/each}
</div>
<button
on:click={ev => {
dial.setPos([ev.clientX, ev.clientY])
dial.toggle()
}}>Add a source</button
>
<Dialog bind:this={dial}>
<div class="form">
<label>
Name
<input
type="text"
placeholder="Gelbooru"
bind:value={newbooru.name}
/>
</label>
<label>
Domain
<input
type="text"
placeholder="gelbooru.com"
bind:value={newbooru.domain}
/>
</label>
<label>
API Endpoint
<input
type="text"
placeholder="/post.json?tags=md5:"
bind:value={newbooru.endpoint}
/>
</label>
<label>
Post page prefix (for sources)
<input
type="text"
placeholder="https://yande.re/post/show/"
bind:value={newbooru.view}
/>
</label>
<button on:click={appendBooru}>Add</button>
</div>
</Dialog>
<hr />
<h3>Blacklisted tags</h3>
<div class="tagcont">
{#each $settings.blacklist as tag, i}
<Tag {tag} on:toggle={() => removeTag(tag)} />
{/each}
</div>
<input
placeholder="Press enter after typing your tag"
on:keydown={ev => {
if (ev.key == 'Enter') {
$settings.blacklist = [
...$settings.blacklist,
ev.currentTarget.value,
]
ev.currentTarget.value = ''
}
}}
/>
{/if}
</div>
</div>
<style scoped>
.tagcont {
display: flex;
gap: 5px;
margin-bottom: 10px;
flex-wrap: wrap;
}
.enabled {
display: block;
}
.disabled {
display: none;
}
.content {
display: flex;
flex-direction: column;
}
hr {
width: 100%;
}
h1 {
text-align: center;
}
.form {
display: flex;
flex-direction: column;
gap: 20px;
position: absolute;
padding: 15px;
border: 1px solid white;
background-color: inherit;
border-radius: 10px;
}
.form > label {
display: flex;
flex-direction: column;
gap: 10px;
}
.backpanel {
position: absolute;
right: 32px;
padding: 10px;
width: 15%;
top: 32px;
border: 1px solid;
border-radius: 5px;
background-color: rgba(0, 0, 0, 0.2);
pointer-events: all;
backdrop-filter: blur(9px);
max-height: 80vh;
min-width: 321px;
}
</style>

286
src/Components/App.svelte

@ -0,0 +1,286 @@
<script lang="ts">
import { hasContext, onDestroy } from 'svelte'
import Dialog from './Dialog.svelte'
import Tag from './Tag.svelte'
import type { Booru } from '../thirdeye'
import Tabs from './Tabs.svelte'
import TabList from './TabList.svelte'
import Tab from './Tab.svelte'
import TabPanel from './TabPanel.svelte'
import { settings } from '../stores'
import { filehosts } from '../filehosts'
let newbooru: Partial<Omit<Booru, 'quirks'> & { view: string }> = {}
let dial: Dialog
function appendBooru() {
$settings.rsources = [...$settings.rsources, newbooru as any]
dial.toggle()
newbooru = {}
}
let visible = false
let penisEvent = () => {
console.log('bepis')
visible = !visible
}
document.addEventListener('penis', penisEvent)
console.log('app loaded')
function removeTag(t: string) {
$settings.blacklist = $settings.blacklist.filter((e: any) => e != t)
}
function removeBooru(t: string) {
const idx = $settings.rsources.findIndex((e) => e.domain == t)
const rep = prompt("You DO know what you're doing, right? (type 'y')")
if (!rep || rep != 'y') return
if (idx >= 0) $settings.rsources.splice(idx, 1)
$settings.rsources = $settings.rsources
}
function toggleBooru(t: string) {
const elem = $settings.rsources.find((e) => e.domain == t)
if (elem) elem.disabled = !elem.disabled
$settings.rsources = $settings.rsources
}
onDestroy(() => {
document.removeEventListener('penis', penisEvent)
})
</script>
<div class="backpanel" class:enabled={visible} class:disabled={!visible}>
<div class="content">
<h1>PEE Settings</h1>
<hr />
<Tabs>
<TabList>
<Tab>General</Tab>
<Tab>External</Tab>
<Tab>File Host</Tab>
</TabList>
<TabPanel>
<label>
<input type="checkbox" bind:checked={$settings.vercheck} />
Check for new versions at startup.
</label>
<label>
<input type="checkbox" bind:checked={$settings.xpi} />
Autoexpand Images on opening.
</label>
<label>
<input type="checkbox" bind:checked={$settings.xpv} />
Autoexpand Videos on opening.
</label>
<label>
<input type="checkbox" bind:checked={$settings.loop} />
Loop media content.
</label>
<label>
<input type="checkbox" bind:checked={$settings.dh} />
Disable hover preview.
</label>
<label>
<input type="checkbox" bind:checked={$settings.eye} />
Hide embedded content behind an eye.
</label>
{#if $settings.eye}
<label>
<input type="checkbox" bind:checked={$settings.ho} />
Hide original content when hidden content is visible.
</label>
{/if}
<label>
<input type="checkbox" bind:checked={$settings.pre} />
Preload external files.
</label>
<label>
<input type="checkbox" bind:checked={$settings.prev} />
Preload external files when they are in view.
</label>
<label>
<input type="checkbox" bind:checked={$settings.hotlink} />
Hotlink content.
</label>
<label>
<input type="checkbox" bind:checked={$settings.ca} />
Control audio on videos with mouse wheel.
</label>
<label>
<input type="checkbox" bind:checked={$settings.sh} />
Show Minimap
</label>
<label>
<input type="checkbox" bind:checked={$settings.ep} />
<!-- svelte-ignore a11y-missing-attribute -->
Disable embedded file preloading<a
title="You might still want to enable 'preload external files'">?</a
>
</label>
</TabPanel>
<TabPanel>
<label>
<input type="checkbox" bind:checked={$settings.te} />
Disable third-eye.
</label>
{#if !$settings.te}
<h3>Booru sources</h3>
<div class="tagcont">
{#each $settings.rsources as source, i}
<Tag
tag={source.name}
on:remove={() => removeBooru(source.domain)}
on:toggle={() => toggleBooru(source.domain)}
toggleable={true}
toggled={!$settings.rsources.find(
(e) => e.domain == source.domain,
)?.disabled}
/>
{/each}
</div>
<button
on:click={(ev) => {
dial.setPos([ev.clientX, ev.clientY])
dial.toggle()
}}>Add a source</button
>
<Dialog bind:this={dial}>
<div class="form">
<label>
Name
<input
type="text"
placeholder="Gelbooru"
bind:value={newbooru.name}
/>
</label>
<label>
Domain
<input
type="text"
placeholder="gelbooru.com"
bind:value={newbooru.domain}
/>
</label>
<label>
API Endpoint
<input
type="text"
placeholder="/post.json?tags=md5:"
bind:value={newbooru.endpoint}
/>
</label>
<label>
Post page prefix (for sources)
<input
type="text"
placeholder="https://yande.re/post/show/"
bind:value={newbooru.view}
/>
</label>
<button on:click={appendBooru}>Add</button>
</div>
</Dialog>
<hr />
<h3>Blacklisted tags</h3>
<div class="tagcont">
{#each $settings.blacklist as tag, i}
<Tag {tag} on:toggle={() => removeTag(tag)} />
{/each}
</div>
<input
placeholder="Press enter after typing your tag"
on:keydown={(ev) => {
if (ev.key == 'Enter') {
$settings.blacklist = [
...$settings.blacklist,
ev.currentTarget.value,
]
ev.currentTarget.value = ''
}
}}
/>
{/if}
</TabPanel>
<TabPanel>
<p>Host to use when uploading files (Only permanent hosts)</p>
<select bind:value={$settings.fhost}>
{#each filehosts as fh, i}
<option value={i}>{fh.domain}</option>
{/each}
</select>
<label>
Maximum number of embedded links to display
<input type="number" bind:value={$settings.maxe} />
</label>
</TabPanel>
</Tabs>
</div>
</div>
<style scoped>
.tagcont {
display: flex;
gap: 5px;
margin-bottom: 10px;
flex-wrap: wrap;
}
.enabled {
display: block;
}
.disabled {
display: none;
}
.content {
display: flex;
flex-direction: column;
}
hr {
width: 100%;
}
h1 {
text-align: center;
}
.form {
display: flex;
flex-direction: column;
gap: 20px;
position: absolute;
padding: 15px;
border: 1px solid white;
background-color: inherit;
border-radius: 10px;
}
.form > label {
display: flex;
flex-direction: column;
gap: 10px;
}
.backpanel {
position: absolute;
right: 32px;
padding: 10px;
width: 15%;
top: 32px;
border: 1px solid;
border-radius: 5px;
background-color: rgba(0, 0, 0, 0.2);
pointer-events: all;
backdrop-filter: blur(9px);
max-height: 80vh;
min-width: 321px;
}
</style>

0
src/Dialog.svelte → src/Components/Dialog.svelte

10
src/Embedding.svelte → src/Components/Embedding.svelte

@ -1,10 +1,10 @@
<script lang="ts">
import { fileTypeFromBuffer, FileTypeResult } from 'file-type'
import { settings, appState } from './stores'
import { settings, appState } from '../stores'
import { beforeUpdate, tick } from 'svelte'
import type { EmbeddedFile } from './main'
import type { EmbeddedFile } from '../main'
import { createEventDispatcher } from 'svelte'
import { GM_head, headerStringToObject } from '../dist/requests'
import { GM_head, headerStringToObject } from '../requests'
import { text } from 'svelte/internal'
import App from './App.svelte'
import { Buffer } from 'buffer'
@ -63,7 +63,7 @@
if (!type) return
} else {
let head = headerStringToObject(await GM_head(thumb, undefined))
type = { ext: '' as any, mime: head['content-type'].split(';')[0].trim() }
type = { ext: '' as any, mime: head['content-type'].split(';')[0].trim() as any }
}
ftype = type.mime
isVideo = type.mime.startsWith('video/')
@ -126,7 +126,7 @@
url = file.data
furl = file.data
let head = headerStringToObject(await GM_head(file.data, undefined))
type = { ext: '' as any, mime: head['content-type'].split(';')[0].trim() }
type = { ext: '' as any, mime: head['content-type'].split(';')[0].trim() as any }
}
if (!type) return
isVideo = type.mime.startsWith('video/')

2
src/Embeddings.svelte → src/Components/Embeddings.svelte

@ -1,5 +1,5 @@
<script lang="ts">
import type { EmbeddedFile } from './main';
import type { EmbeddedFile } from '../main';
import { createEventDispatcher } from 'svelte';
import Embedding from './Embedding.svelte';

4
src/EyeButton.svelte → src/Components/EyeButton.svelte

@ -4,9 +4,9 @@
import type Embedding from './Embedding.svelte'
import type Embeddings from './Embeddings.svelte'
import type { EmbeddedFile } from './main'
import type { EmbeddedFile } from '../main'
import { settings } from './stores'
import { settings } from '../stores'
export let id = ''
export let files: EmbeddedFile[]

2
src/NotificationsHandler.svelte → src/Components/NotificationsHandler.svelte

@ -1,5 +1,5 @@
<script lang="ts">
import type { fireNotification } from './utils'
import type { fireNotification } from '../utils'
type t = Parameters<typeof fireNotification>
type Notification = {

14
src/PostOptions.svelte → src/Components/PostOptions.svelte

@ -1,8 +1,8 @@
<script lang="ts">
import { appState } from './stores'
import type { ImageProcessor } from './main'
import { appState, settings } from '../stores'
import type { ImageProcessor } from '../main'
import { fireNotification, getSelectedFile } from './utils'
import { fireNotification, getSelectedFile } from '../utils'
export let processors: ImageProcessor[] = []
export let textinput: HTMLTextAreaElement
@ -11,12 +11,12 @@
const addContent = (...newfiles: File[]) => {
files = [...files, ...newfiles]
if (files.length > 5) {
if (files.length > $settings.maxe) {
fireNotification(
'warning',
'Can only add up to 5 attachments, further attachments will be dropped',
`Can only add up to ${$settings.maxe} attachments, further attachments will be dropped`,
)
files = files.slice(0, 5)
files = files.slice(0, $settings.maxe)
}
}
@ -44,7 +44,7 @@
.filter((e) => e.inject)
.find((e) => e.match(file.name))
if (!proc) throw new Error('Container filetype not supported')
const buff = await proc.inject!(file, [...files].slice(0, 5))
const buff = await proc.inject!(file, [...files].slice(0, $settings.maxe))
document.dispatchEvent(
new CustomEvent('QRSetFile', {
//detail: { file: new Blob([buff]), name: file.name, type: file.type }

5
src/ScrollHighlighter.svelte → src/Components/ScrollHighlighter.svelte

@ -1,9 +1,6 @@
<script lang="ts">
import { fileTypeFromBuffer } from 'file-type';
import { onDestroy, onMount } from 'svelte';
import type { EmbeddedFile } from './main';
import { settings, appState } from './stores'
import { settings, appState } from '../stores'
function getOffset(el: HTMLElement | null) {
var _x = 0;

0
src/SettingsButton.svelte → src/Components/SettingsButton.svelte

34
src/Components/Tab.svelte

@ -0,0 +1,34 @@
<script lang="ts">
import { getContext } from 'svelte'
import { TABS } from './Tabs.svelte'
const tab = {}
const { registerTab, selectTab, selectedTab } = getContext(TABS)
registerTab(tab)
</script>
<button class:selected={$selectedTab === tab} on:click={() => selectTab(tab)}>
<slot />
</button>
<style>
button {
background: none;
border: none;
border-bottom: 2px solid white;
border-radius: 0;
margin: 0;
color: unset;
}
button:hover {
cursor: pointer;
background-color: #8d8d8d80;
}
.selected {
border-bottom: 2px solid;
color: #333;
}
</style>

9
src/Components/TabList.svelte

@ -0,0 +1,9 @@
<div class="tab-list">
<slot />
</div>
<style>
.tab-list {
border-bottom: 1px solid;
}
</style>

12
src/Components/TabPanel.svelte

@ -0,0 +1,12 @@
<script lang="ts">
import { getContext } from 'svelte'
import { TABS } from './Tabs.svelte'
const panel = {}
const { registerPanel, selectedPanel } = getContext(TABS)
registerPanel(panel)
</script>
{#if $selectedPanel === panel}
<slot />
{/if}

63
src/Components/Tabs.svelte

@ -0,0 +1,63 @@
<script context="module">
export const TABS = {}
</script>
<script lang="ts">
import { setContext, onDestroy } from 'svelte'
import { writable } from 'svelte/store'
import type Tab from './Tab.svelte'
import type TabPanel from './TabPanel.svelte'
const tabs: Tab[] = []
const panels: TabPanel[] = []
const selectedTab = writable<Tab | null>(null)
const selectedPanel = writable<TabPanel | null>(null)
setContext(TABS, {
registerTab: (tab: Tab) => {
tabs.push(tab)
selectedTab.update((current) => current || tab)
onDestroy(() => {
const i = tabs.indexOf(tab)
tabs.splice(i, 1)
selectedTab.update((current) =>
current === tab ? tabs[i] || tabs[tabs.length - 1] : current,
)
})
},
registerPanel: (panel: TabPanel) => {
panels.push(panel)
selectedPanel.update((current) => current || panel)
onDestroy(() => {
const i = panels.indexOf(panel)
panels.splice(i, 1)
selectedPanel.update((current) =>
current === panel ? panels[i] || panels[panels.length - 1] : current,
)
})
},
selectTab: (tab: Tab) => {
const i = tabs.indexOf(tab)
selectedTab.set(tab)
selectedPanel.set(panels[i])
},
selectedTab,
selectedPanel,
})
</script>
<div class="tabs">
<slot />
</div>
<style scoped>
.tabs {
display: flex;
flex-direction: column;
}
</style>

0
src/Tag.svelte → src/Components/Tag.svelte

47
src/filehosts.ts

@ -0,0 +1,47 @@
import { GM_fetch } from "./requests";
const lolisafe = (domain: string) => ({
domain,
async uploadFile(f: Blob) {
return '';
}
});
function parseForm(data: object) {
const form = new FormData();
Object.entries(data)
.filter(([key, value]) => value !== null)
.map(([key, value]) => form.append(key, value));
return form;
}
const catbox = (domain: string) => ({
domain,
async uploadFile(inj: Blob) {
const resp = await GM_fetch(`https://${domain}/user/api.php`, {
method: 'POST',
body: parseForm({
reqtype: 'fileupload',
fileToUpload: inj
})
});
return resp.text();
}
});
export type API = {
domain: string;
uploadFile(f: Blob): Promise<string>;
}
export const filehosts: API[] = [
catbox('catbox.moe'),
lolisafe('zz.ht'),
lolisafe('imouto.kawaii.su'),
lolisafe('take-me-to.space'),
lolisafe('loli.solutions'),
lolisafe('loli.graphics'),
lolisafe('sucks-to-b.eu')
];

29
src/main.ts

@ -11,14 +11,14 @@ import pomf from "./pomf";
import { GM_fetch, GM_head, headerStringToObject } from "./requests";
import App from "./App.svelte";
import ScrollHighlighter from "./ScrollHighlighter.svelte";
import PostOptions from "./PostOptions.svelte";
import SettingsButton from './SettingsButton.svelte';
//import Embedding from './Embedding.svelte';
import Embeddings from './Embeddings.svelte';
import EyeButton from './EyeButton.svelte';
import NotificationsHandler from './NotificationsHandler.svelte';
import App from "./Components/App.svelte";
import ScrollHighlighter from "./Components/ScrollHighlighter.svelte";
import PostOptions from "./Components/PostOptions.svelte";
import SettingsButton from './Components/SettingsButton.svelte';
//import Embedding from './Components/Embedding.svelte';
import Embeddings from './Components/Embeddings.svelte';
import EyeButton from './Components/EyeButton.svelte';
import NotificationsHandler from './Components/NotificationsHandler.svelte';
import { buildPeeFile, fireNotification } from "./utils";
import { fileTypeFromBuffer } from "file-type";
import { getQueryProcessor, QueryProcessor } from "./websites";
@ -544,3 +544,16 @@ function processAttachments(post: HTMLDivElement, ress: [EmbeddedFile, boolean][
// };
// };
// }
// if ((window as any)['pagemode']) {
// onload = () => {
// document.body.innerHTML = '';
// new App({
// target: document.body
// });
// setTimeout(() => {
// document.dispatchEvent(new CustomEvent("penis"));
// }, 30);
// };
// }

2
src/stores.ts

@ -25,6 +25,8 @@ export const settings = writable(localLoad('settingsv2', {
expte: false,
hotlink: false,
vercheck: false,
fhost: 0,
maxe: 5,
conc: 8,
ho: false,
blacklist: ['guro', 'scat', 'ryona', 'gore'],

20
src/utils.ts

@ -2,6 +2,14 @@ import { Buffer } from "buffer";
import { GM_fetch, GM_head, headerStringToObject } from "./requests";
import thumbnail from "./assets/hasembed.png";
import type { EmbeddedFile } from './main';
import { settings } from "./stores";
import { filehosts } from "./filehosts";
export let csettings: Parameters<typeof settings['set']>[0];
settings.subscribe(b => {
csettings = b;
});
const generateThumbnail = async (f: File): Promise<Buffer> => {
const can = document.createElement("canvas");
@ -88,7 +96,8 @@ rest: [X bytes of thumbnail data])[file bytes]
&4 => has thumbnail
*/
export const decodeCoom3Payload = async (buff: Buffer) => {
const pees = buff.toString().split(' ').slice(0, 5).filter(e => e.startsWith("https://files.catbox.moe/"));
const allowed_domains = filehosts.map(e => e.domain);
const pees = buff.toString().split(' ').slice(0, csettings.maxe).filter(e => allowed_domains.some(v => e.match(`https://(.*\\.)?${v}/`)));
return (await Promise.all(pees.map(async pee => {
try {
const headers = headerStringToObject(await GM_head(pee));
@ -167,14 +176,7 @@ export const uploadFiles = async (injs: File[]) => {
let total = 0;
fireNotification('info', `Uploading ${injs.length} files...`);
return await Promise.all(injs.map(async inj => {
const resp = await GM_fetch("https://catbox.moe/user/api.php", {
method: 'POST',
body: parseForm({
reqtype: 'fileupload',
fileToUpload: await buildPeeFile(inj)
})
});
const ret = await resp.text();
const ret = await filehosts[csettings.fhost].uploadFile(await buildPeeFile(inj));
fireNotification('info', `Uploaded files [${++total}/${injs.length}] ${ret}`);
return ret;
}));

Loading…
Cancel
Save