mirror of
https://github.com/bytedream/stream-bypass.git
synced 2026-08-04 21:10:40 +02:00
update
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
export enum MessageType {
|
||||
RequestActiveMatch,
|
||||
NotifyActiveMatch
|
||||
}
|
||||
|
||||
export type MessageData<T extends MessageType> = {
|
||||
[MessageType.RequestActiveMatch]: undefined;
|
||||
[MessageType.NotifyActiveMatch]: {
|
||||
id: string;
|
||||
url: string;
|
||||
domain: string;
|
||||
};
|
||||
}[T];
|
||||
|
||||
export async function sendMessage<T extends MessageType>(message: T, data: MessageData<T>) {
|
||||
await browser.runtime.sendMessage({ type: message, data: data });
|
||||
}
|
||||
|
||||
export async function sendMessageToActiveTab<T extends MessageType>(message: T, data: MessageData<T>) {
|
||||
const tabs = await browser.tabs.query({ active: true, currentWindow: true });
|
||||
|
||||
await browser.tabs.sendMessage(tabs[0].id!, { type: message, data: data }).catch(() => {});
|
||||
}
|
||||
|
||||
export function listenMessages(listener: (type: MessageType, data: any) => void): () => void {
|
||||
const callback = (callbackData: { type: MessageType; data: any }) => {
|
||||
const { type, data } = callbackData;
|
||||
listener(type, data);
|
||||
};
|
||||
|
||||
browser.runtime.onMessage.addListener(callback);
|
||||
|
||||
return () => {
|
||||
browser.runtime.onMessage.removeListener(callback);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { HostMatchType, type Host } from '@/lib/host';
|
||||
|
||||
export default {
|
||||
name: 'Doodstream',
|
||||
id: 'doodstream',
|
||||
domains: [
|
||||
'doodstream.com',
|
||||
'dood.pm',
|
||||
'dood.ws',
|
||||
'dood.wf',
|
||||
'dood.cx',
|
||||
'dood.sh',
|
||||
'dood.watch',
|
||||
'dood.work',
|
||||
'dood.to',
|
||||
'dood.so',
|
||||
'dood.la',
|
||||
'dood.li',
|
||||
'dood.re',
|
||||
'dood.yt',
|
||||
'doods.pro',
|
||||
'ds2play.com',
|
||||
'dooood.com',
|
||||
'd000d.com'
|
||||
],
|
||||
replace: true,
|
||||
regex: [/(\/pass_md5\/.*?)'.*(\?token=.*?expiry=)/s],
|
||||
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
const response = await fetch(`https://${window.location.host}${match[1]}`, {
|
||||
headers: {
|
||||
Range: 'bytes=0-'
|
||||
},
|
||||
referrer: `https://${window.location.host}/e/${window.location.pathname.split('/').slice(-1)[0]}`
|
||||
});
|
||||
return {
|
||||
type: HostMatchType.HLS,
|
||||
url: `${await response.text()}1234567890${match[2]}${Date.now()}`
|
||||
};
|
||||
}
|
||||
} satisfies Host;
|
||||
@@ -0,0 +1,17 @@
|
||||
import { HostMatchType, type Host } from '@/lib/host';
|
||||
import { unpack } from '@/utils/content';
|
||||
|
||||
export default {
|
||||
name: 'Dropload',
|
||||
id: 'dropload',
|
||||
domains: ['dropload.io'],
|
||||
regex: [/eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms],
|
||||
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
const unpacked = await unpack(match[0]);
|
||||
return {
|
||||
type: HostMatchType.HLS,
|
||||
url: unpacked.match(/(?<=file:").*(?=")/)![0]
|
||||
};
|
||||
}
|
||||
} satisfies Host;
|
||||
@@ -0,0 +1,24 @@
|
||||
import { HostMatchType, type Host } from '@/lib/host';
|
||||
import { HostSettings } from '@/lib/settings';
|
||||
import { unpack } from '@/utils/content';
|
||||
|
||||
export default {
|
||||
name: 'Filemoon',
|
||||
id: 'filemoon',
|
||||
domains: ['filemoon.sx', 'filemoon.to', 'filemoon.in'],
|
||||
regex: [/(?<=<iframe\s*src=")\S*(?=")/s, /eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms],
|
||||
replace: true,
|
||||
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
if (window.location.host.startsWith('filemoon')) {
|
||||
await HostSettings.addTemporaryHostDomain(this, new URL(match[0]).host);
|
||||
return null;
|
||||
}
|
||||
|
||||
const unpacked = await unpack(match[0]);
|
||||
return {
|
||||
type: HostMatchType.HLS,
|
||||
url: unpacked.match(/(?<=file:")\S*(?=")/)![0]
|
||||
};
|
||||
}
|
||||
} satisfies Host;
|
||||
@@ -0,0 +1,15 @@
|
||||
import { HostMatchType, type Host } from '@/lib/host';
|
||||
|
||||
export default {
|
||||
name: 'Goodstream',
|
||||
id: 'goodstream',
|
||||
domains: ['goodstream.uno'],
|
||||
regex: [/(?<=file:\s+").*(?=")/g],
|
||||
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
return {
|
||||
type: HostMatchType.HLS,
|
||||
url: match[0]
|
||||
};
|
||||
}
|
||||
} satisfies Host;
|
||||
@@ -0,0 +1,78 @@
|
||||
import Doodstream from './doodstream';
|
||||
import Dropload from './dropload';
|
||||
import Filemoon from './filemoon';
|
||||
import Goodstream from './goodstream';
|
||||
import Kwik from './kwik';
|
||||
import Loadx from './loadx';
|
||||
import Luluvdo from './luluvdo';
|
||||
import Mixdrop from './mixdrop';
|
||||
import Mp4Upload from './mp4upload';
|
||||
import Newgrounds from './newgrounds';
|
||||
import StreamA2z from './streama2z';
|
||||
import Streamtape from './streamtape';
|
||||
import Streamzz from './streamzz';
|
||||
import SuperVideo from './supervideo';
|
||||
import Upstream from './upstream';
|
||||
import Vidmoly from './vidmoly';
|
||||
import Vidoza from './vidoza';
|
||||
import Voe from './voe';
|
||||
import Vupload from './vupload';
|
||||
import { HostSettings } from '@/lib/settings';
|
||||
|
||||
export enum HostMatchType {
|
||||
NATIVE = 'native',
|
||||
HLS = 'hls'
|
||||
}
|
||||
|
||||
export interface HostMatch {
|
||||
type: HostMatchType;
|
||||
/** If null, it's interpreted that a url should be present but isn't, probably because the website broke */
|
||||
url: string | null;
|
||||
}
|
||||
|
||||
export interface Host {
|
||||
name: string;
|
||||
id: string;
|
||||
domains: string[];
|
||||
replace?: boolean;
|
||||
regex: RegExp[];
|
||||
notice?: string;
|
||||
|
||||
match(match: RegExpMatchArray): Promise<HostMatch | null>;
|
||||
}
|
||||
|
||||
export const hosts = [
|
||||
Doodstream,
|
||||
Dropload,
|
||||
Filemoon,
|
||||
Goodstream,
|
||||
Kwik,
|
||||
Loadx,
|
||||
Luluvdo,
|
||||
Mixdrop,
|
||||
Mp4Upload,
|
||||
Newgrounds,
|
||||
StreamA2z,
|
||||
Streamtape,
|
||||
Streamzz,
|
||||
SuperVideo,
|
||||
Upstream,
|
||||
Vidmoly,
|
||||
Vidoza,
|
||||
Voe,
|
||||
Vupload
|
||||
];
|
||||
|
||||
export async function getHost(domain: string) {
|
||||
if (await HostSettings.getAllHostsDisabled()) return null;
|
||||
|
||||
const disabledIds = await HostSettings.getDisabledHosts();
|
||||
for (const host of hosts) {
|
||||
if (host.domains.includes(domain)) {
|
||||
if (!disabledIds.includes(host.id)) return host;
|
||||
else return null;
|
||||
}
|
||||
}
|
||||
|
||||
return HostSettings.checkTemporaryHostDomain(domain);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { HostMatchType, type Host } from '@/lib/host';
|
||||
import { unpack } from '@/utils/content';
|
||||
|
||||
export default {
|
||||
name: 'Kwik',
|
||||
id: 'kwik',
|
||||
domains: ['kwik.cx'],
|
||||
regex: [/eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms],
|
||||
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
const unpacked = await unpack(match[0]);
|
||||
return {
|
||||
type: HostMatchType.HLS,
|
||||
url: unpacked.match(/(?<=source=').*(?=')/)![0]
|
||||
};
|
||||
}
|
||||
} satisfies Host;
|
||||
@@ -0,0 +1,27 @@
|
||||
import { HostMatchType, type Host } from '@/lib/host';
|
||||
import { lastPathSegment } from '@/utils/extract';
|
||||
|
||||
export default {
|
||||
name: 'LoadX',
|
||||
id: 'loadx',
|
||||
domains: ['loadx.ws'],
|
||||
regex: [/./gm],
|
||||
|
||||
match: async () => {
|
||||
const hash = encodeURIComponent(lastPathSegment(window.location.href));
|
||||
const response = await fetch(`https://${window.location.host}/player/index.php?data=${hash}&do=getVideo`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
}
|
||||
});
|
||||
|
||||
const responseJson = await response.json();
|
||||
const videoSource: string = responseJson['videoSource'];
|
||||
|
||||
return {
|
||||
type: HostMatchType.HLS,
|
||||
url: videoSource.replace('\\/', '/')
|
||||
};
|
||||
}
|
||||
} satisfies Host;
|
||||
@@ -0,0 +1,32 @@
|
||||
import { HostMatchType, type Host } from '@/lib/host';
|
||||
import { unpack } from '@/utils/content';
|
||||
import { lastPathSegment } from '@/utils/extract';
|
||||
|
||||
export default {
|
||||
name: 'Luluvdo',
|
||||
id: 'luluvdo',
|
||||
domains: ['luluvdo.com'],
|
||||
regex: [/./gm],
|
||||
|
||||
match: async () => {
|
||||
const requestBody = new FormData();
|
||||
requestBody.set('op', 'embed');
|
||||
requestBody.set('file_code', lastPathSegment(window.location.href));
|
||||
const response = await fetch(`https://${window.location.host}/dl`, {
|
||||
method: 'POST',
|
||||
body: requestBody,
|
||||
referrer: window.location.href
|
||||
});
|
||||
|
||||
const responseText = await response.text();
|
||||
const evalMatch = responseText.match(/eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms)!;
|
||||
// sometimes is packed, sometimes it's not. looks like someone forgets to obfuscate the code when pushing to
|
||||
// production
|
||||
const unpacked = evalMatch ? await unpack(evalMatch[0]) : responseText;
|
||||
|
||||
return {
|
||||
type: HostMatchType.HLS,
|
||||
url: unpacked.match(/(?<=file:").*(?=")/)![0]
|
||||
};
|
||||
}
|
||||
} satisfies Host;
|
||||
@@ -0,0 +1,18 @@
|
||||
import { HostMatchType, type Host } from '@/lib/host';
|
||||
import { unpack } from '@/utils/content';
|
||||
|
||||
export default {
|
||||
name: 'Mixdrop',
|
||||
id: 'mixdrop',
|
||||
domains: ['mixdrop.bz', 'mixdrop.ch', 'mixdrop.co', 'mixdrop.gl', 'mixdrop.my', 'mixdrop.to'],
|
||||
regex: [/eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms],
|
||||
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
const unpacked = await unpack(match[0]);
|
||||
const url = unpacked.match(/(?<=MDCore.wurl=").*(?=")/)![0];
|
||||
return {
|
||||
type: HostMatchType.NATIVE,
|
||||
url: `https:${url}`
|
||||
};
|
||||
}
|
||||
} satisfies Host;
|
||||
@@ -0,0 +1,18 @@
|
||||
import { HostMatchType, type Host } from '@/lib/host';
|
||||
import { unpack } from '@/utils/content';
|
||||
|
||||
export default {
|
||||
name: 'Mp4Upload',
|
||||
id: 'mp4upload',
|
||||
domains: ['mp4upload.com'],
|
||||
replace: true,
|
||||
regex: [/eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms],
|
||||
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
const unpacked = await unpack(match[0]);
|
||||
return {
|
||||
type: HostMatchType.NATIVE,
|
||||
url: unpacked.match(/(?<=player.src\(").*(?=")/)![0]
|
||||
};
|
||||
}
|
||||
} satisfies Host;
|
||||
@@ -0,0 +1,22 @@
|
||||
import { HostMatchType, type Host } from '@/lib/host';
|
||||
|
||||
export default {
|
||||
name: 'Newgrounds',
|
||||
id: 'newgrounds',
|
||||
domains: ['newgrounds.com'],
|
||||
regex: [/.*/gm],
|
||||
|
||||
match: async () => {
|
||||
const id = window.location.pathname.split('/').slice(-1)[0];
|
||||
const response = await fetch(`https://www.newgrounds.com/portal/video/${id}`, {
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
}
|
||||
});
|
||||
const json = await response.json();
|
||||
return {
|
||||
type: HostMatchType.HLS,
|
||||
url: decodeURI(json['sources'][Object.keys(json['sources'])[0]][0]['src'])
|
||||
};
|
||||
}
|
||||
} satisfies Host;
|
||||
@@ -0,0 +1,20 @@
|
||||
import { HostMatchType, type Host } from '@/lib/host';
|
||||
import { HostSettings } from '@/lib/settings';
|
||||
|
||||
export default {
|
||||
name: 'Stream2Az',
|
||||
id: 'stream2az',
|
||||
domains: ['streama2z.com', 'streama2z.xyz'],
|
||||
regex: [/https?:\/\/\S*m3u8.+(?=['"])/gm],
|
||||
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
if (this.domains.indexOf(window.location.hostname) !== -1) {
|
||||
await HostSettings.addTemporaryHostDomain(this, window.location.hostname);
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
type: HostMatchType.HLS,
|
||||
url: match[0]
|
||||
};
|
||||
}
|
||||
} satisfies Host;
|
||||
@@ -0,0 +1,26 @@
|
||||
import { HostMatchType, type Host } from '@/lib/host';
|
||||
|
||||
export default {
|
||||
name: 'Streamtape',
|
||||
id: 'streamtape',
|
||||
domains: ['streamtape.com', 'streamtape.net', 'shavetape.cash'],
|
||||
regex: [/id=.*(?=')/gm],
|
||||
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
let i = 0;
|
||||
while (i < match.length) {
|
||||
if (match[++i - 1] == match[i]) {
|
||||
return {
|
||||
type: HostMatchType.HLS,
|
||||
url: `https://streamtape.com/get_video?${match[i]}`
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
type: HostMatchType.HLS,
|
||||
// use the old method as fallback
|
||||
url: `https://streamtape.com/get_video?${match.reverse()[0]}`
|
||||
};
|
||||
}
|
||||
} satisfies Host;
|
||||
@@ -0,0 +1,17 @@
|
||||
import { HostMatchType, type Host } from '@/lib/host';
|
||||
|
||||
export default {
|
||||
name: 'Streamzz',
|
||||
id: 'streamzz',
|
||||
domains: ['streamzz.to', 'streamz.ws'],
|
||||
regex: [/(?<=\|)\w{2,}/gm],
|
||||
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
return {
|
||||
type: HostMatchType.HLS,
|
||||
url: `https://get.${location.hostname.split('.')[0]}.tw/getlink-${
|
||||
match.sort((a, b) => b.length - a.length)[0]
|
||||
}.dll`
|
||||
};
|
||||
}
|
||||
} satisfies Host;
|
||||
@@ -0,0 +1,17 @@
|
||||
import { HostMatchType, type Host } from '@/lib/host';
|
||||
import { unpack } from '@/utils/content';
|
||||
|
||||
export default {
|
||||
name: 'Supervideo',
|
||||
id: 'supervideo',
|
||||
domains: ['supervideo.cc', 'supervideo.tv'],
|
||||
regex: [/eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms],
|
||||
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
const unpacked = await unpack(match[0]);
|
||||
return {
|
||||
type: HostMatchType.HLS,
|
||||
url: unpacked.match(/(?<=file:").*(?=")/)![0]
|
||||
};
|
||||
}
|
||||
} satisfies Host;
|
||||
@@ -0,0 +1,17 @@
|
||||
import { HostMatchType, type Host } from '@/lib/host';
|
||||
import { unpack } from '@/utils/content';
|
||||
|
||||
export default {
|
||||
name: 'Upstream',
|
||||
id: 'upstream',
|
||||
domains: ['upstream.to'],
|
||||
regex: [/eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms],
|
||||
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
const unpacked = await unpack(match[0]);
|
||||
return {
|
||||
type: HostMatchType.HLS,
|
||||
url: unpacked.match(/(?<=file:").*(?=")/)![0]
|
||||
};
|
||||
}
|
||||
} satisfies Host;
|
||||
@@ -0,0 +1,16 @@
|
||||
import { HostMatchType, type Host } from '@/lib/host';
|
||||
|
||||
export default {
|
||||
name: 'Vidmoly',
|
||||
id: 'vidmoly',
|
||||
domains: ['vidmoly.me', 'vidmoly.to'],
|
||||
regex: [/(?<=file:").+\.m3u8/gm],
|
||||
replace: true,
|
||||
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
return {
|
||||
type: HostMatchType.HLS,
|
||||
url: match[0]
|
||||
};
|
||||
}
|
||||
} satisfies Host;
|
||||
@@ -0,0 +1,16 @@
|
||||
import { HostMatchType, type Host } from '@/lib/host';
|
||||
|
||||
export default {
|
||||
name: 'Vidoza',
|
||||
id: 'vidoza',
|
||||
domains: ['vidoza.net', 'videzz.net'],
|
||||
regex: [/(?<=src:\s?").+?(?=")/gm],
|
||||
replace: true,
|
||||
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
return {
|
||||
type: HostMatchType.HLS,
|
||||
url: match[0]
|
||||
};
|
||||
}
|
||||
} satisfies Host;
|
||||
@@ -0,0 +1,74 @@
|
||||
import { HostMatchType, type Host } from '@/lib/host';
|
||||
import { HostSettings } from '@/lib/settings';
|
||||
|
||||
function rot13(encrypted: string) {
|
||||
let decrypted = '';
|
||||
for (let i = 0; i < encrypted.length; i++) {
|
||||
let char = encrypted.charCodeAt(i);
|
||||
if (char >= 65 && char <= 90) {
|
||||
char = ((char - 65 + 13) % 26) + 65;
|
||||
} else if (char >= 97 && char <= 122) {
|
||||
char = ((char - 97 + 13) % 26) + 97;
|
||||
}
|
||||
decrypted += String.fromCharCode(char);
|
||||
}
|
||||
return decrypted;
|
||||
}
|
||||
|
||||
function removeSpecialSequences(input: string) {
|
||||
return input
|
||||
.replaceAll(/@\$/g, '')
|
||||
.replaceAll(/\^\^/g, '')
|
||||
.replaceAll(/~@/g, '')
|
||||
.replaceAll(/%\?/g, '')
|
||||
.replaceAll(/\*~/g, '')
|
||||
.replaceAll(/!!/g, '')
|
||||
.replaceAll(/#&/g, '');
|
||||
}
|
||||
|
||||
function shiftString(input: string) {
|
||||
let shifted = '';
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
const char = input.charCodeAt(i);
|
||||
const shiftedChar = char - 3;
|
||||
shifted += String.fromCharCode(shiftedChar);
|
||||
}
|
||||
return shifted;
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'Voe',
|
||||
id: 'voe',
|
||||
domains: ['voe.sx'],
|
||||
regex: [
|
||||
// voe.sx
|
||||
/(?<=window\.location\.href\s=\s')\S*(?=')/gm,
|
||||
// whatever site voe.sx redirects to
|
||||
/(?<=<script type="application\/json">).*(?=<\/script>)/m
|
||||
],
|
||||
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
if (window.location.host === 'voe.sx') {
|
||||
await HostSettings.addTemporaryHostDomain(this, new URL(match[0]).host);
|
||||
return null;
|
||||
} else {
|
||||
let json = match[0];
|
||||
json = JSON.parse(json);
|
||||
|
||||
let deobfuscated = json[0];
|
||||
deobfuscated = rot13(deobfuscated);
|
||||
deobfuscated = removeSpecialSequences(deobfuscated);
|
||||
deobfuscated = atob(deobfuscated);
|
||||
deobfuscated = shiftString(deobfuscated);
|
||||
deobfuscated = deobfuscated.split('').reverse().join('');
|
||||
deobfuscated = atob(deobfuscated);
|
||||
|
||||
const payload = JSON.parse(deobfuscated);
|
||||
|
||||
return {
|
||||
type: HostMatchType.HLS,
|
||||
url: payload['source']
|
||||
};
|
||||
}
|
||||
}
|
||||
} satisfies Host;
|
||||
@@ -0,0 +1,15 @@
|
||||
import { HostMatchType, type Host } from '@/lib/host';
|
||||
|
||||
export default {
|
||||
name: 'Vupload',
|
||||
id: 'vupload',
|
||||
domains: ['vupload.com'],
|
||||
regex: [/(?<=src:\s?").+?(?=")/gm],
|
||||
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
return {
|
||||
type: HostMatchType.HLS,
|
||||
url: match[0]
|
||||
};
|
||||
}
|
||||
} satisfies Host;
|
||||
@@ -1,441 +0,0 @@
|
||||
import { Hosters, Redirect, TmpHost } from './settings';
|
||||
import { lastPathSegment } from './util/extract';
|
||||
import { unpack } from './util/userspace';
|
||||
|
||||
export interface Match {
|
||||
name: string;
|
||||
id: string;
|
||||
domains: string[];
|
||||
replace?: boolean;
|
||||
regex: RegExp[];
|
||||
notice?: string;
|
||||
|
||||
match(
|
||||
match: RegExpMatchArray
|
||||
): Promise<
|
||||
string | { [MatchMediaType.Hls]: string } | { [MatchMediaType.Native]: string } | null
|
||||
>;
|
||||
|
||||
// allow other properties that may be implemented by the objects that use this interface declaration
|
||||
[other: string]: any;
|
||||
}
|
||||
|
||||
export enum MatchMediaType {
|
||||
Hls = 'hls',
|
||||
Native = 'native'
|
||||
}
|
||||
|
||||
export const Doodstream: Match = {
|
||||
name: 'Doodstream',
|
||||
id: 'doodstream',
|
||||
domains: [
|
||||
'doodstream.com',
|
||||
'dood.pm',
|
||||
'dood.ws',
|
||||
'dood.wf',
|
||||
'dood.cx',
|
||||
'dood.sh',
|
||||
'dood.watch',
|
||||
'dood.work',
|
||||
'dood.to',
|
||||
'dood.so',
|
||||
'dood.la',
|
||||
'dood.li',
|
||||
'dood.re',
|
||||
'dood.yt',
|
||||
'doods.pro',
|
||||
'ds2play.com',
|
||||
'dooood.com',
|
||||
'd000d.com'
|
||||
],
|
||||
replace: true,
|
||||
regex: [/(\/pass_md5\/.*?)'.*(\?token=.*?expiry=)/s],
|
||||
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
const response = await fetch(`https://${window.location.host}${match[1]}`, {
|
||||
headers: {
|
||||
Range: 'bytes=0-'
|
||||
},
|
||||
referrer: `https://${window.location.host}/e/${
|
||||
window.location.pathname.split('/').slice(-1)[0]
|
||||
}`
|
||||
});
|
||||
return `${await response.text()}1234567890${match[2]}${Date.now()}`;
|
||||
}
|
||||
};
|
||||
|
||||
export const DropLoad: Match = {
|
||||
name: 'Dropload',
|
||||
id: 'dropload',
|
||||
domains: ['dropload.io'],
|
||||
regex: [/eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms],
|
||||
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
const unpacked = await unpack(match[0]);
|
||||
return unpacked.match(/(?<=file:").*(?=")/)![0];
|
||||
}
|
||||
};
|
||||
|
||||
export const Filemoon: Match = {
|
||||
name: 'Filemoon',
|
||||
id: 'filemoon',
|
||||
domains: ['filemoon.sx', 'filemoon.to', 'filemoon.in'],
|
||||
regex: [/(?<=<iframe\s*src=")\S*(?=")/s, /eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms],
|
||||
replace: true,
|
||||
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
if (window.location.host.startsWith('filemoon')) {
|
||||
await TmpHost.set(new URL(match[0]).host, Filemoon);
|
||||
return null;
|
||||
}
|
||||
|
||||
await TmpHost.delete();
|
||||
|
||||
const unpacked = await unpack(match[0]);
|
||||
return unpacked.match(/(?<=file:")\S*(?=")/)![0];
|
||||
}
|
||||
};
|
||||
|
||||
export const GoodStream: Match = {
|
||||
name: 'Goodstream',
|
||||
id: 'goodstream',
|
||||
domains: ['goodstream.uno'],
|
||||
regex: [/(?<=file:\s+").*(?=")/g],
|
||||
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
return match[0];
|
||||
}
|
||||
};
|
||||
|
||||
export const Kwik: Match = {
|
||||
name: 'Kwik',
|
||||
id: 'kwik',
|
||||
domains: ['kwik.cx'],
|
||||
regex: [/eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms],
|
||||
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
const unpacked = await unpack(match[0]);
|
||||
return unpacked.match(/(?<=source=').*(?=')/)![0];
|
||||
}
|
||||
};
|
||||
|
||||
export const LoadX: Match = {
|
||||
name: 'LoadX',
|
||||
id: 'loadx',
|
||||
domains: ['loadx.ws'],
|
||||
regex: [/./gm],
|
||||
|
||||
match: async () => {
|
||||
const hash = encodeURIComponent(lastPathSegment(window.location.href));
|
||||
const response = await fetch(
|
||||
`https://${window.location.host}/player/index.php?data=${hash}&do=getVideo`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const responseJson = await response.json();
|
||||
const videoSource: string = responseJson['videoSource'];
|
||||
|
||||
// extension of extracted url is '.txt', so we have to manually specify that it's a hls
|
||||
return { [MatchMediaType.Hls]: videoSource.replace('\\/', '/') };
|
||||
}
|
||||
};
|
||||
|
||||
export const Luluvdo: Match = {
|
||||
name: 'Luluvdo',
|
||||
id: 'luluvdo',
|
||||
domains: ['luluvdo.com'],
|
||||
regex: [/./gm],
|
||||
|
||||
match: async () => {
|
||||
const requestBody = new FormData();
|
||||
requestBody.set('op', 'embed');
|
||||
requestBody.set('file_code', lastPathSegment(window.location.href));
|
||||
const response = await fetch(`https://${window.location.host}/dl`, {
|
||||
method: 'POST',
|
||||
body: requestBody,
|
||||
referrer: window.location.href
|
||||
});
|
||||
|
||||
let unpacked;
|
||||
|
||||
const responseText = await response.text();
|
||||
const evalMatch = responseText.match(/eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms)!;
|
||||
// sometimes is packed, sometimes it's not. looks like someone forgets to obfuscate the code when pushing to
|
||||
// production
|
||||
if (evalMatch) {
|
||||
unpacked = await unpack(evalMatch[0]);
|
||||
return unpacked.match(/(?<=file:").*(?=")/)![0];
|
||||
} else {
|
||||
unpacked = responseText;
|
||||
}
|
||||
|
||||
return unpacked.match(/(?<=file:").*(?=")/)![0];
|
||||
}
|
||||
};
|
||||
|
||||
export const Mixdrop: Match = {
|
||||
name: 'Mixdrop',
|
||||
id: 'mixdrop',
|
||||
domains: ['mixdrop.bz', 'mixdrop.ch', 'mixdrop.co', 'mixdrop.gl', 'mixdrop.my', 'mixdrop.to'],
|
||||
regex: [/eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms],
|
||||
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
const unpacked = await unpack(match[0]);
|
||||
const url = unpacked.match(/(?<=MDCore.wurl=").*(?=")/)![0];
|
||||
return `https:${url}`;
|
||||
}
|
||||
};
|
||||
|
||||
export const Mp4Upload: Match = {
|
||||
name: 'Mp4Upload',
|
||||
id: 'mp4upload',
|
||||
domains: ['mp4upload.com'],
|
||||
replace: true,
|
||||
regex: [/eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms],
|
||||
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
const unpacked = await unpack(match[0]);
|
||||
return unpacked.match(/(?<=player.src\(").*(?=")/)![0];
|
||||
}
|
||||
};
|
||||
|
||||
export const Newgrounds: Match = {
|
||||
name: 'Newgrounds',
|
||||
id: 'newgrounds',
|
||||
domains: ['newgrounds.com'],
|
||||
regex: [/.*/gm],
|
||||
|
||||
match: async () => {
|
||||
const id = window.location.pathname.split('/').slice(-1)[0];
|
||||
const response = await fetch(`https://www.newgrounds.com/portal/video/${id}`, {
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
}
|
||||
});
|
||||
const json = await response.json();
|
||||
return decodeURI(json['sources'][Object.keys(json['sources'])[0]][0]['src']);
|
||||
}
|
||||
};
|
||||
|
||||
export const StreamA2z: Match = {
|
||||
name: 'Stream2Az',
|
||||
id: 'stream2az',
|
||||
domains: ['streama2z.com', 'streama2z.xyz'],
|
||||
regex: [/https?:\/\/\S*m3u8.+(?=['"])/gm],
|
||||
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
if (StreamA2z.domains.indexOf(window.location.hostname) !== -1) {
|
||||
await Redirect.set(StreamA2z);
|
||||
return null;
|
||||
}
|
||||
return match[0];
|
||||
}
|
||||
};
|
||||
|
||||
export const Streamtape: Match = {
|
||||
name: 'Streamtape',
|
||||
id: 'streamtape',
|
||||
domains: ['streamtape.com', 'streamtape.net', 'shavetape.cash'],
|
||||
regex: [/id=.*(?=')/gm],
|
||||
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
let i = 0;
|
||||
while (i < match.length) {
|
||||
if (match[++i - 1] == match[i]) {
|
||||
return `https://streamtape.com/get_video?${match[i]}`;
|
||||
}
|
||||
}
|
||||
|
||||
// use the old method as fallback
|
||||
return `https://streamtape.com/get_video?${match.reverse()[0]}`;
|
||||
}
|
||||
};
|
||||
|
||||
export const Streamzz: Match = {
|
||||
name: 'Streamzz',
|
||||
id: 'streamzz',
|
||||
domains: ['streamzz.to', 'streamz.ws'],
|
||||
regex: [/(?<=\|)\w{2,}/gm],
|
||||
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
return `https://get.${location.hostname.split('.')[0]}.tw/getlink-${
|
||||
match.sort((a, b) => b.length - a.length)[0]
|
||||
}.dll`;
|
||||
}
|
||||
};
|
||||
|
||||
export const SuperVideo: Match = {
|
||||
name: 'Supervideo',
|
||||
id: 'supervideo',
|
||||
domains: ['supervideo.cc', 'supervideo.tv'],
|
||||
regex: [/eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms],
|
||||
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
const unpacked = await unpack(match[0]);
|
||||
return unpacked.match(/(?<=file:").*(?=")/)![0];
|
||||
}
|
||||
};
|
||||
|
||||
export const Upstream: Match = {
|
||||
name: 'Upstream',
|
||||
id: 'upstream',
|
||||
domains: ['upstream.to'],
|
||||
regex: [/eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms],
|
||||
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
const unpacked = await unpack(match[0]);
|
||||
return unpacked.match(/(?<=file:").*(?=")/)![0];
|
||||
}
|
||||
};
|
||||
|
||||
export const Vidmoly: Match = {
|
||||
name: 'Vidmoly',
|
||||
id: 'vidmoly',
|
||||
domains: ['vidmoly.me', 'vidmoly.to'],
|
||||
regex: [/(?<=file:").+\.m3u8/gm],
|
||||
replace: true,
|
||||
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
return match[0];
|
||||
}
|
||||
};
|
||||
|
||||
export const Vidoza: Match = {
|
||||
name: 'Vidoza',
|
||||
id: 'vidoza',
|
||||
domains: ['vidoza.net', 'videzz.net'],
|
||||
regex: [/(?<=src:\s?").+?(?=")/gm],
|
||||
replace: true,
|
||||
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
return match[0];
|
||||
}
|
||||
};
|
||||
|
||||
export const Voe: Match = {
|
||||
name: 'Voe',
|
||||
id: 'voe',
|
||||
domains: ['voe.sx'],
|
||||
regex: [
|
||||
// voe.sx
|
||||
/(?<=window\.location\.href\s=\s')\S*(?=')/gm,
|
||||
// whatever site voe.sx redirects to
|
||||
/(?<=<script type="application\/json">).*(?=<\/script>)/m
|
||||
],
|
||||
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
if (window.location.host === 'voe.sx') {
|
||||
const redirectUrl = new URL(match[0]);
|
||||
await TmpHost.set(redirectUrl.host, Voe);
|
||||
return null;
|
||||
} else {
|
||||
let json = match[0];
|
||||
json = JSON.parse(json);
|
||||
|
||||
let deobfuscated = json[0];
|
||||
deobfuscated = this.rot13(deobfuscated);
|
||||
deobfuscated = this.removeSpecialSequences(deobfuscated);
|
||||
deobfuscated = atob(deobfuscated);
|
||||
deobfuscated = this.shiftString(deobfuscated);
|
||||
deobfuscated = deobfuscated.split('').reverse().join('');
|
||||
deobfuscated = atob(deobfuscated);
|
||||
|
||||
const payload = JSON.parse(deobfuscated);
|
||||
|
||||
return payload['source'];
|
||||
}
|
||||
},
|
||||
|
||||
rot13: function (encrypted: string) {
|
||||
let decrypted = '';
|
||||
for (let i = 0; i < encrypted.length; i++) {
|
||||
let char = encrypted.charCodeAt(i);
|
||||
if (char >= 65 && char <= 90) {
|
||||
char = ((char - 65 + 13) % 26) + 65;
|
||||
} else if (char >= 97 && char <= 122) {
|
||||
char = ((char - 97 + 13) % 26) + 97;
|
||||
}
|
||||
decrypted += String.fromCharCode(char);
|
||||
}
|
||||
return decrypted;
|
||||
},
|
||||
removeSpecialSequences: function (input: string) {
|
||||
return input
|
||||
.replaceAll(/@\$/g, '')
|
||||
.replaceAll(/\^\^/g, '')
|
||||
.replaceAll(/~@/g, '')
|
||||
.replaceAll(/%\?/g, '')
|
||||
.replaceAll(/\*~/g, '')
|
||||
.replaceAll(/!!/g, '')
|
||||
.replaceAll(/#&/g, '');
|
||||
},
|
||||
shiftString: function (input: string) {
|
||||
let shifted = '';
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
const char = input.charCodeAt(i);
|
||||
const shiftedChar = char - 3;
|
||||
shifted += String.fromCharCode(shiftedChar);
|
||||
}
|
||||
return shifted;
|
||||
}
|
||||
};
|
||||
|
||||
export const Vupload: Match = {
|
||||
name: 'Vupload',
|
||||
id: 'vupload',
|
||||
domains: ['vupload.com'],
|
||||
regex: [/(?<=src:\s?").+?(?=")/gm],
|
||||
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
return match[0];
|
||||
}
|
||||
};
|
||||
|
||||
export const matches = {
|
||||
[Doodstream.id]: Doodstream,
|
||||
[DropLoad.id]: DropLoad,
|
||||
[Filemoon.id]: Filemoon,
|
||||
[GoodStream.id]: GoodStream,
|
||||
[Kwik.id]: Kwik,
|
||||
[LoadX.id]: LoadX,
|
||||
[Luluvdo.id]: Luluvdo,
|
||||
[Mixdrop.id]: Mixdrop,
|
||||
[Mp4Upload.id]: Mp4Upload,
|
||||
[Newgrounds.id]: Newgrounds,
|
||||
[StreamA2z.id]: StreamA2z,
|
||||
[Streamtape.id]: Streamtape,
|
||||
[Streamzz.id]: Streamzz,
|
||||
[SuperVideo.id]: SuperVideo,
|
||||
[Upstream.id]: Upstream,
|
||||
[Vidmoly.id]: Vidmoly,
|
||||
[Vidoza.id]: Vidoza,
|
||||
[Voe.id]: Voe,
|
||||
[Vupload.id]: Vupload
|
||||
};
|
||||
|
||||
export async function getMatch(domain: string): Promise<Match | null> {
|
||||
if (await Hosters.getAllDisabled()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (const match of Object.values(matches)) {
|
||||
if (
|
||||
match.domains.indexOf(domain) !== -1 &&
|
||||
!(await Hosters.getDisabled().then((d) => d.find((p) => p.id == match.id)))
|
||||
) {
|
||||
return match;
|
||||
}
|
||||
}
|
||||
|
||||
const tmpHost = await TmpHost.get();
|
||||
if (tmpHost && tmpHost[0] === domain) {
|
||||
return tmpHost[1];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
+66
-90
@@ -1,112 +1,88 @@
|
||||
import { matches, type Match } from './match';
|
||||
import { storage } from '#imports';
|
||||
import { hosts, type Host } from '@/lib/host';
|
||||
|
||||
export const Hosters = {
|
||||
getDisabled: async () => {
|
||||
const disabled = (await storageGet('hosters.disabled', [])) as string[];
|
||||
return disabled.map((id) => matches[id]).filter((m) => m !== undefined);
|
||||
},
|
||||
disable: async (match: Match) => {
|
||||
const disabled = (await storageGet('hosters.disabled', [])) as string[];
|
||||
const index = disabled.indexOf(match.id);
|
||||
export class HostSettings {
|
||||
/* disabled hosts */
|
||||
private static disabledHosts = storage.defineItem<string[]>('local:disabledHosts', { fallback: [] });
|
||||
private static allHostsDisabled = storage.defineItem<boolean>('local:allHostsDisabled', { fallback: false });
|
||||
|
||||
static async addDisabledHost(host: Host) {
|
||||
const ids = await this.disabledHosts.getValue();
|
||||
|
||||
const index = ids.indexOf(host.id);
|
||||
if (index === -1) {
|
||||
disabled.push(match.id);
|
||||
await storageSet('hosters.disabled', disabled);
|
||||
ids.push(host.id);
|
||||
await this.disabledHosts.setValue(ids);
|
||||
}
|
||||
},
|
||||
enable: async (match: Match) => {
|
||||
const disabled = (await storageGet('hosters.disabled', [])) as string[];
|
||||
const index = disabled.indexOf(match.id);
|
||||
}
|
||||
static async removeDisabledHost(host: Host) {
|
||||
const ids = await this.disabledHosts.getValue();
|
||||
|
||||
const index = ids.indexOf(host.id);
|
||||
if (index !== -1) {
|
||||
disabled.splice(index, 1);
|
||||
await storageSet('hosters.disabled', disabled);
|
||||
ids.splice(index, 1);
|
||||
await this.disabledHosts.setValue(ids);
|
||||
}
|
||||
},
|
||||
getAllDisabled: async () => {
|
||||
return await storageGet<boolean>('hosters.allDisabled', false);
|
||||
},
|
||||
setAll: async (enable: boolean) => {
|
||||
await storageSet('hosters.allDisabled', !enable);
|
||||
}
|
||||
};
|
||||
|
||||
export const Redirect = {
|
||||
get: async (): Promise<Match | null> => {
|
||||
return matches[(await storageGet('redirect')) as string] || null;
|
||||
},
|
||||
set: async (match: Match) => {
|
||||
await storageSet('redirect', match.id);
|
||||
},
|
||||
delete: async () => {
|
||||
await storageDelete('redirect');
|
||||
static async getDisabledHosts() {
|
||||
return await this.disabledHosts.getValue();
|
||||
}
|
||||
};
|
||||
|
||||
export const TmpHost = {
|
||||
get: async (): Promise<[string, Match] | null> => {
|
||||
const tmphost = await storageGet<[string, number]>('tmphost');
|
||||
if (tmphost === undefined) {
|
||||
return null;
|
||||
}
|
||||
return [tmphost[0], matches[tmphost[1]]];
|
||||
},
|
||||
set: async (domain: string, match: Match) => {
|
||||
await storageSet('tmphost', [domain, match.id]);
|
||||
},
|
||||
delete: async () => {
|
||||
await storageDelete('tmphost');
|
||||
static async getAllHostsDisabled() {
|
||||
return await this.allHostsDisabled.getValue();
|
||||
}
|
||||
};
|
||||
|
||||
export const UrlReferer = {
|
||||
get: async (url: string): Promise<string | null> => {
|
||||
return (await storageGet(`urlReferer.${url}`)) || null;
|
||||
},
|
||||
set: async (url: string, referer: string) => {
|
||||
await storageSet(`urlReferer.${url}`, referer);
|
||||
},
|
||||
delete: async (url: string) => {
|
||||
await storageDelete(`urlReferer.${url}`);
|
||||
static async setAllHostsDisabled(disabled: boolean) {
|
||||
await this.allHostsDisabled.setValue(disabled);
|
||||
}
|
||||
};
|
||||
|
||||
export const Other = {
|
||||
getFf2mpv: async () => {
|
||||
return await storageGet('other.ff2mpv', false);
|
||||
},
|
||||
setFf2mpv: async (enable: boolean) => {
|
||||
await storageSet('other.ff2mpv', enable);
|
||||
}
|
||||
};
|
||||
|
||||
async function storageGet<T>(key: string, defaultValue?: T): Promise<T | undefined> {
|
||||
let resolve: (value: T | undefined) => void;
|
||||
const promise = new Promise<T | undefined>((r) => (resolve = r));
|
||||
|
||||
chrome.storage.local.get(key, (entry) => {
|
||||
const value = entry[key];
|
||||
resolve(value === undefined ? defaultValue : value);
|
||||
/* tmp */
|
||||
private static temporaryHostDomain = storage.defineItem<Record<string, string>>('local:temporaryHostDomain', {
|
||||
fallback: {}
|
||||
});
|
||||
|
||||
return promise;
|
||||
static async addTemporaryHostDomain(host: Host, domain: string) {
|
||||
const temporaryHostDomains = await this.temporaryHostDomain.getValue();
|
||||
|
||||
temporaryHostDomains[domain] = host.id;
|
||||
await this.temporaryHostDomain.setValue(temporaryHostDomains);
|
||||
console.log(await this.temporaryHostDomain.getValue());
|
||||
}
|
||||
static async checkTemporaryHostDomain(domain: string) {
|
||||
const temporaryHostDomains = await this.temporaryHostDomain.getValue();
|
||||
|
||||
const hostId = temporaryHostDomains[domain];
|
||||
return hostId ? (hosts.find((host) => host.id === hostId) ?? null) : null;
|
||||
}
|
||||
}
|
||||
|
||||
async function storageSet<T>(key: string, value: T) {
|
||||
let resolve: () => void;
|
||||
const promise = new Promise<void>((r) => (resolve = r));
|
||||
export class FF2MPVSettings {
|
||||
private static ff2mpvEnabled = storage.defineItem<boolean>('local:ff2mpv', { fallback: false });
|
||||
|
||||
const obj = {
|
||||
[key]: value
|
||||
};
|
||||
chrome.storage.local.set(obj, () => resolve());
|
||||
|
||||
return promise;
|
||||
static async getEnabled() {
|
||||
return await this.ff2mpvEnabled.getValue();
|
||||
}
|
||||
static async setEnabled(enabled: boolean) {
|
||||
console.log('set', enabled);
|
||||
await this.ff2mpvEnabled.setValue(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
async function storageDelete(key: string) {
|
||||
let resolve: () => void;
|
||||
const promise = new Promise<void>((r) => (resolve = r));
|
||||
export class UrlReferer {
|
||||
private static temporaryUrlReferer = storage.defineItem<Record<string, string>>('local:temporaryUrlReferer', {
|
||||
fallback: {}
|
||||
});
|
||||
|
||||
chrome.storage.local.remove(key, () => resolve());
|
||||
static async addTemporary(hostname: string, referer: string) {
|
||||
const tmpUrlReferer = await this.temporaryUrlReferer.getValue();
|
||||
|
||||
return promise;
|
||||
tmpUrlReferer[hostname] = referer;
|
||||
await this.temporaryUrlReferer.setValue(tmpUrlReferer);
|
||||
}
|
||||
|
||||
static async get(hostname: string) {
|
||||
const tmpUrlReferer = await this.temporaryUrlReferer.getValue();
|
||||
|
||||
return tmpUrlReferer[hostname] ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
export function lastPathSegment(path: string): string {
|
||||
while (path.endsWith('/')) {
|
||||
path = path.slice(0, -1);
|
||||
}
|
||||
return path.substring(path.lastIndexOf('/') + 1);
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
// Adapted from http://matthewfl.com/unPacker.html by matthew@matthewfl.com
|
||||
export async function unpack(packed: string): Promise<string> {
|
||||
const context = `
|
||||
{
|
||||
eval: function (c) {
|
||||
packed = c;
|
||||
},
|
||||
window: {},
|
||||
document: {}
|
||||
}
|
||||
`;
|
||||
const toExecute = `
|
||||
function() {
|
||||
let packed = "";
|
||||
with(${context}) {
|
||||
${packed}
|
||||
};
|
||||
return packed;
|
||||
}
|
||||
`;
|
||||
|
||||
const res = (await runInPageContext(toExecute)) as string;
|
||||
return res
|
||||
.replace(/;/g, ';\n')
|
||||
.replace(/{/g, '\n{\n')
|
||||
.replace(/}/g, '\n}\n')
|
||||
.replace(/\n;\n/g, ';\n')
|
||||
.replace(/\n\\n/g, '\n');
|
||||
}
|
||||
|
||||
// Adapted from: https://github.com/arikw/extension-page-context
|
||||
async function runInPageContext<T>(toExecute: string): Promise<T | null> {
|
||||
// test that we are running with the allow-scripts permission
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||
window.sessionStorage;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
|
||||
// returned value container
|
||||
const resultMessageId = crypto.randomUUID();
|
||||
|
||||
// prepare script container
|
||||
const scriptElm = document.createElement('script');
|
||||
scriptElm.setAttribute('type', 'application/javascript');
|
||||
|
||||
// inject the script
|
||||
scriptElm.textContent = `
|
||||
(
|
||||
async function () {
|
||||
const response = {
|
||||
id: '${resultMessageId}'
|
||||
};
|
||||
|
||||
try {
|
||||
response.result = JSON.stringify(await (${toExecute})() ); // run script
|
||||
} catch(err) {
|
||||
response.error = JSON.stringify(err);
|
||||
}
|
||||
|
||||
window.postMessage(response, '*');
|
||||
}
|
||||
)();
|
||||
`;
|
||||
|
||||
// run the script
|
||||
document.documentElement.appendChild(scriptElm);
|
||||
|
||||
// clean up script element
|
||||
scriptElm.remove();
|
||||
|
||||
let resolve: (value: T) => void;
|
||||
let reject: (value: any) => void;
|
||||
const promise = new Promise<T>((res, rej) => {
|
||||
resolve = res;
|
||||
reject = rej;
|
||||
});
|
||||
function onResult(event: MessageEvent) {
|
||||
if (event.data.id === resultMessageId) {
|
||||
window.removeEventListener('message', onResult);
|
||||
if (event.data.error !== undefined) {
|
||||
return reject(JSON.parse(event.data.error));
|
||||
}
|
||||
return resolve(event.data.result !== undefined ? JSON.parse(event.data.result) : undefined);
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('message', onResult);
|
||||
|
||||
return await promise;
|
||||
}
|
||||
Reference in New Issue
Block a user