Browse Source

Add sources, fix windows middle click bug

pull/46/head 0.98
coomdev 2 years ago
parent
commit
9a5a7ef381
  1. 2
      main.meta.js
  2. 828
      main.user.js
  3. 5
      src/App.svelte
  4. 32
      src/Embedding.svelte
  5. 16
      src/EyeButton.svelte
  6. 6
      src/main.ts
  7. 6
      src/stores.ts
  8. 23
      src/thirdeye.ts

2
main.meta.js

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

828
main.user.js

File diff suppressed because it is too large

5
src/App.svelte

@ -90,6 +90,11 @@
<input type="checkbox" bind:checked={$settings.sh} /> <input type="checkbox" bind:checked={$settings.sh} />
Show Minimap Show Minimap
</label> </label>
<label>
<input type="checkbox" bind:checked={$settings.ep} />
<!-- svelte-ignore a11y-missing-attribute -->
Turn off embedded file preloading<a title="You might still want to enable 'preload external files'">?</a>
</label>
<label> <label>
<input type="checkbox" bind:checked={$settings.te} /> <input type="checkbox" bind:checked={$settings.te} />
Turn off third-eye. Turn off third-eye.

32
src/Embedding.svelte

@ -119,6 +119,7 @@
export async function bepis(ev: MouseEvent) { export async function bepis(ev: MouseEvent) {
if ($appState.isCatalog) return; if ($appState.isCatalog) return;
if (ev.button == 0) { if (ev.button == 0) {
contracted = !contracted contracted = !contracted
if (hovering) hoverStop() if (hovering) hoverStop()
@ -137,15 +138,17 @@
if (file.thumbnail && !furl) { if (file.thumbnail && !furl) {
// 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();
} else if (ev.button == 1) { // middle click } else if (ev.button == 1) { // middle click
let src = furl || url; 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; src = file.page.url;
} }
ev.preventDefault();
if (isNotChrome) { if (isNotChrome) {
window.open(src, '_blank'); window.open(src, '_blank');
} else } else
@ -244,8 +247,11 @@
<div <div
class:contract={contracted} class:contract={contracted}
class="place" class="place"
on:click={bepis}
on:auxclick={bepis} on:click={e => e.preventDefault()}
on:auxclick={e => e.preventDefault()}
on:mousedown={bepis}
on:mouseover={hoverStart} on:mouseover={hoverStart}
on:mouseout={hoverStop} on:mouseout={hoverStop}
on:mousemove={hoverUpdate} on:mousemove={hoverUpdate}
@ -253,7 +259,12 @@
bind:this={place} bind:this={place}
> >
{#if isImage} {#if isImage}
<img bind:this={imgElem} alt={file.filename} src={furl || url} /> <!-- svelte-ignore a11y-missing-attribute -->
<img
bind:this={imgElem}
alt={file.filename}
src={furl || url}
/>
{/if} {/if}
{#if isAudio} {#if isAudio}
<audio <audio
@ -267,7 +278,8 @@
{/if} {/if}
{#if isVideo} {#if isVideo}
<!-- svelte-ignore a11y-media-has-caption --> <!-- svelte-ignore a11y-media-has-caption -->
<video loop={$settings.loop} bind:this={videoElem} src={furl || url} /> <!-- svelte-ignore a11y-missing-attribute -->
<video 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>
@ -325,16 +337,16 @@
z-index: 9; z-index: 9;
} }
.contract > img, .contract img,
.contract > video { .contract video {
max-width: 125px !important; max-width: 125px !important;
max-height: 125px !important; max-height: 125px !important;
width: auto; width: auto;
height: auto; height: auto;
} }
.place:not(.contract) > video, .place:not(.contract) video,
.place:not(.contract) > img, .place:not(.contract) img,
.hoverer > video, .hoverer > video,
.hoverer > img { .hoverer > img {
max-width: 100vw; max-width: 100vw;

16
src/EyeButton.svelte

@ -50,6 +50,22 @@ import type { EmbeddedFile } from './main';
on:click={downloadFile} on:click={downloadFile}
class="fa fa-download clickable" class="fa fa-download clickable"
/> />
{#if file.source}
<!-- svelte-ignore a11y-missing-content -->
<a
href={file.source}
target="_blank"
class="clickable"
>Source</a>
{/if}
{#if file.page}
<!-- svelte-ignore a11y-missing-content -->
<a
href={file.page.url}
target="_blank"
class="clickable"
>{file.page.title}</a>
{/if}
{#if isNotChrome && isVideo} {#if isNotChrome && isVideo}
<!-- svelte-ignore a11y-missing-attribute --> <!-- svelte-ignore a11y-missing-attribute -->
<a on:click={(ev) => { <a on:click={(ev) => {

6
src/main.ts

@ -14,8 +14,6 @@ import ScrollHighlighter from "./ScrollHighlighter.svelte";
import SettingsButton from './SettingsButton.svelte'; import SettingsButton from './SettingsButton.svelte';
import Embedding from './Embedding.svelte'; import Embedding from './Embedding.svelte';
import EyeButton from './EyeButton.svelte'; import EyeButton from './EyeButton.svelte';
import { fileTypeFromBuffer } from "file-type";
import { buf } from "crc-32";
export interface ImageProcessor { export interface ImageProcessor {
skip?: true; skip?: true;
@ -25,7 +23,7 @@ export interface ImageProcessor {
inject?(b: File, c: File): Buffer | Promise<Buffer>; inject?(b: File, c: File): Buffer | Promise<Buffer>;
} }
let csettings: any; export let csettings: Parameters<typeof settings['set']>[0];
let processors: ImageProcessor[] = let processors: ImageProcessor[] =
[thirdeye, png, webm, gif]; [thirdeye, png, webm, gif];
@ -67,7 +65,7 @@ async function* streamRemote(url: string, chunkSize = 16 * 1024, fetchRestOnNonC
} }
type EmbeddedFileWithPreview = { type EmbeddedFileWithPreview = {
page?: 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
thumbnail: Buffer; thumbnail: Buffer;
filename: string; filename: string;

6
src/stores.ts

@ -1,8 +1,8 @@
import { writable } from "svelte/store"; import { writable } from "svelte/store";
const localLoad = (key: string, def: any) => const localLoad = <T>(key: string, def: T) =>
('__pee__' + key) in localStorage ('__pee__' + key) in localStorage
? JSON.parse(localStorage.getItem('__pee__' + key)!) ? JSON.parse(localStorage.getItem('__pee__' + key)!) as T
: def; : def;
const localSet = (key: string, value: any) => const localSet = (key: string, value: any) =>
@ -18,6 +18,8 @@ export const settings = writable(localLoad('settings', {
ca: false, ca: false,
pre: false, pre: false,
prev: false, prev: false,
sh: false,
ep: false,
blacklist: ['guro', 'scat', 'ryona', 'gore'], blacklist: ['guro', 'scat', 'ryona', 'gore'],
sources: ['gelbooru.com', sources: ['gelbooru.com',
'yande.re', 'yande.re',

23
src/thirdeye.ts

@ -3,6 +3,7 @@ import { GM_fetch } from "./requests";
import { settings } from "./stores"; import { settings } from "./stores";
export type Booru = { export type Booru = {
name: string;
domain: string; domain: string;
endpoint: string; endpoint: string;
quirks: tran; quirks: tran;
@ -19,6 +20,15 @@ export type BooruMatch = {
type tran = (a: any) => BooruMatch[]; type tran = (a: any) => BooruMatch[];
function firstThatFor<T>(promises: Promise<T>[], pred: (v: T) => boolean) {
Promise.any(promises.map(async p => {
const v = await p;
if (pred(v))
return v;
throw v;
}))
}
const gelquirk: (s: string) => tran = prefix => (a => const gelquirk: (s: string) => tran = prefix => (a =>
(a.post || a).map((e: any) => ({ (a.post || a).map((e: any) => ({
ext: e.image.substr(e.image.indexOf('.') + 1), ext: e.image.substr(e.image.indexOf('.') + 1),
@ -31,11 +41,13 @@ const gelquirk: (s: string) => tran = prefix => (a =>
export const boorus: Booru[] = [ export const boorus: Booru[] = [
{ {
name: 'Gelbooru',
domain: 'gelbooru.com', domain: 'gelbooru.com',
endpoint: '/index.php?page=dapi&s=post&q=index&json=1&tags=md5:', endpoint: '/index.php?page=dapi&s=post&q=index&json=1&tags=md5:',
quirks: gelquirk("https://gelbooru.com/index.php?page=post&s=view&id=") quirks: gelquirk("https://gelbooru.com/index.php?page=post&s=view&id=")
}, },
{ {
name: 'Yandere',
domain: 'yande.re', domain: 'yande.re',
endpoint: '/post.json?tags=md5:', endpoint: '/post.json?tags=md5:',
quirks: a => quirks: a =>
@ -49,6 +61,7 @@ export const boorus: Booru[] = [
} as BooruMatch)) } as BooruMatch))
}, },
{ {
name: 'Sankaku',
domain: 'capi-v2.sankakucomplex.com', domain: 'capi-v2.sankakucomplex.com',
endpoint: '/posts/keyset?tags=md5:', endpoint: '/posts/keyset?tags=md5:',
quirks: a => a.data ? quirks: a => a.data ?
@ -63,12 +76,14 @@ export const boorus: Booru[] = [
} as BooruMatch)) : [] } as BooruMatch)) : []
}, },
{ {
name: 'Rule34',
domain: 'api.rule34.xxx', domain: 'api.rule34.xxx',
endpoint: '/index.php?page=dapi&s=post&q=index&json=1&tags=md5:', endpoint: '/index.php?page=dapi&s=post&q=index&json=1&tags=md5:',
// note: rule34 do not seem to give source in their API // note: rule34 do not seem to give source in their API
quirks: gelquirk("https://rule34.xxx/index.php?page=post&s=view&id=") quirks: gelquirk("https://rule34.xxx/index.php?page=post&s=view&id=")
}, },
{ {
name: 'Danbooru',
domain: 'danbooru.donmai.us', domain: 'danbooru.donmai.us',
endpoint: '/posts.json?tags=md5:', endpoint: '/posts.json?tags=md5:',
quirks: a => quirks: a =>
@ -82,6 +97,7 @@ export const boorus: Booru[] = [
} as BooruMatch)) } as BooruMatch))
}, },
{ {
name: 'Lolibooru',
domain: 'lolibooru.moe', domain: 'lolibooru.moe',
endpoint: '/post.json?tags=md5:', endpoint: '/post.json?tags=md5:',
quirks: a => quirks: a =>
@ -125,19 +141,22 @@ const findFileFrom = async (b: Booru, hex: string) => {
const extract = async (b: Buffer, fn?: string) => { const extract = async (b: Buffer, fn?: string) => {
let result!: BooruMatch[]; let result!: BooruMatch[];
let booru!: string;
for (const e of Object.values(boorus)) { for (const e of Object.values(boorus)) {
if (!sources.has(e.domain)) if (!sources.has(e.domain))
continue; continue;
result = await findFileFrom(e, fn!.substring(0, 32)); result = await findFileFrom(e, fn!.substring(0, 32));
if (result.length) if (result.length) {
booru = e.name;
break; break;
}
} }
let cachedFile: ArrayBuffer; let cachedFile: ArrayBuffer;
const prev = result[0].preview_url; const prev = result[0].preview_url;
const full = result[0].full_url; const full = result[0].full_url;
return { return {
source: result[0].source, source: result[0].source,
page: result[0].page, page: { title: booru, url: result[0].page },
filename: fn!.substring(0, 33) + result[0].ext, filename: fn!.substring(0, 33) + result[0].ext,
thumbnail: (await (await GM_fetch(prev || full)).arrayBuffer()), // prefer preview thumbnail: (await (await GM_fetch(prev || full)).arrayBuffer()), // prefer preview
data: async (lsn) => { data: async (lsn) => {

Loading…
Cancel
Save