Skip to content

Commit

Permalink
🔖 Release version 2.0.1
Browse files Browse the repository at this point in the history
  • Loading branch information
younesaassila authored Jun 12, 2023
2 parents 00c805b + 889d9a8 commit 5c90115
Show file tree
Hide file tree
Showing 26 changed files with 1,425 additions and 1,898 deletions.
2,241 changes: 784 additions & 1,457 deletions package-lock.json

Large diffs are not rendered by default.

31 changes: 18 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ttv-lol-pro",
"version": "2.0.0",
"version": "2.0.1",
"description": "TTV LOL PRO removes most livestream ads from Twitch.",
"@parcel/bundler-default": {
"minBundles": 10000000,
Expand All @@ -18,12 +18,17 @@
},
"browserslist": "> 0.5%, last 2 versions, not dead",
"scripts": {
"dev:firefox": "cpy src/manifest.firefox.json . --rename=manifest.json && parcel src/manifest.json --host localhost --target webext-dev --no-hmr",
"dev:chromium": "cpy src/manifest.chromium.json . --rename=manifest.json && parcel src/manifest.json --host localhost --target webext-dev --no-hmr",
"predev:firefox": "npm run clean && shx cp src/manifest.firefox.json src/manifest.json",
"predev:chromium": "npm run clean && shx cp src/manifest.chromium.json src/manifest.json",
"dev:firefox": "parcel src/manifest.json --host localhost --target webext-dev --no-hmr",
"dev:chromium": "parcel src/manifest.json --host localhost --target webext-dev --no-hmr",
"lint": "prettier --check ./src",
"lint:fix": "prettier --write ./src",
"build:firefox": "cpy src/manifest.firefox.json . --rename=manifest.json && parcel build src/manifest.json --target webext-prod --no-source-maps",
"build:chromium": "cpy src/manifest.chromium.json . --rename=manifest.json && parcel build src/manifest.json --target webext-prod --no-source-maps"
"prebuild:firefox": "npm run clean && shx cp src/manifest.firefox.json src/manifest.json",
"prebuild:chromium": "npm run clean && shx cp src/manifest.chromium.json src/manifest.json",
"build:firefox": "parcel build src/manifest.json --target webext-prod --no-source-maps",
"build:chromium": "parcel build src/manifest.json --target webext-prod --no-source-maps",
"clean": "shx rm -rf .parcel-cache ; shx rm -rf dist ; shx --silent rm src/manifest.json ; exit 0"
},
"keywords": [
"twitch",
Expand All @@ -37,21 +42,21 @@
"license": "GPL-3.0",
"dependencies": {
"bowser": "^2.11.0",
"semver-compare": "^1.0.0",
"xhook": "^1.6.0"
"ip": "^1.1.8"
},
"devDependencies": {
"@parcel/config-webextension": "^2.9.1",
"@parcel/config-webextension": "^2.9.2",
"@types/chrome": "^0.0.237",
"@types/semver-compare": "^1.0.1",
"@types/ip": "^1.1.0",
"@types/webextension-polyfill": "^0.10.0",
"@types/xhook": "^1.5.0",
"cpy-cli": "^4.2.0",
"parcel": "^2.9.1",
"buffer": "^6.0.3",
"os-browserify": "^0.3.0",
"parcel": "^2.9.2",
"postcss": "^8.4.24",
"prettier": "^2.8.8",
"prettier-plugin-css-order": "^1.3.0",
"prettier-plugin-css-order": "^1.3.1",
"prettier-plugin-organize-imports": "^3.2.2",
"shx": "^0.3.4",
"typescript": "^4.9.5",
"webextension-polyfill": "^0.10.0"
},
Expand Down
13 changes: 9 additions & 4 deletions src/background/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,19 @@ if (isChromium) {
// Block tracking pixels.
browser.webRequest.onBeforeRequest.addListener(
() => ({ cancel: true }),
{ urls: ["https://*.twitch.tv/r/*"] },
{ urls: ["https://*.twitch.tv/r/s/*", "https://*.twitch.tv/r/c/*"] },
["blocking"]
);
// Map channel names to video-weaver URLs.

// Map channel names to Video Weaver URLs.
browser.webRequest.onBeforeRequest.addListener(
onBeforeUsherRequest,
{
urls: ["https://usher.ttvnw.net/api/channel/hls/*"],
},
["blocking"]
);

// Proxy requests.
browser.proxy.onRequest.addListener(
onProxyRequest,
Expand All @@ -52,22 +54,25 @@ if (isChromium) {
},
["requestHeaders"]
);
// Remove the Accept flag from requests.

// Remove the Accept flag from flagged requests.
browser.webRequest.onBeforeSendHeaders.addListener(
onBeforeSendHeaders,
{
urls: ["https://*.ttvnw.net/*", "https://*.twitch.tv/*"],
},
["blocking", "requestHeaders"]
);
// Check for ads in video-weaver responses.

// Check for ads in Video Weaver responses.
browser.webRequest.onBeforeRequest.addListener(
onBeforeVideoWeaverRequest,
{
urls: ["https://*.ttvnw.net/*"],
},
["blocking"]
);

// Monitor responses of proxied requests.
browser.webRequest.onHeadersReceived.addListener(onHeadersReceived, {
urls: ["https://*.ttvnw.net/*", "https://*.twitch.tv/*"],
Expand Down
43 changes: 32 additions & 11 deletions src/background/handlers/onAuthRequired.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,50 @@
import { WebRequest } from "webextension-polyfill";
import getProxyInfoFromUrl from "../../common/ts/getProxyInfoFromUrl";
import store from "../../store";

const pendingRequests = [];

export default function onAuthRequired(
details: WebRequest.OnAuthRequiredDetailsType
) {
): void | WebRequest.BlockingResponseOrPromise {
if (!details.isProxy) return;

if (pendingRequests.includes(details.requestId)) {
console.error(
`🔐 Incorrect credentials provided for proxy ${details.challenger.host}:${details.challenger.port}.`
`🔐 Provided invalid credentials for proxy ${details.challenger.host}:${details.challenger.port}.`
);
// TODO: Remove the proxy from the list of online proxies.
return;
}
pendingRequests.push(details.requestId);

let predicate = (proxy: string) =>
proxy.endsWith(`@${details.challenger.host}:${details.challenger.port}`);
if (details.challenger.port === 3128) {
// Default port
predicate = (proxy: string) =>
proxy.endsWith(
`@${details.challenger.host}:${details.challenger.port}`
) || proxy.endsWith(`@${details.challenger.host}`);
}

const proxies = store.state.optimizedProxiesEnabled
? store.state.optimizedProxies
: store.state.normalProxies;
const proxy = proxies.find(proxy =>
proxy.includes(`@${details.challenger.host}`)
);
if (!proxy) return;
const [username, password] = proxy
.substring(0, proxy.lastIndexOf("@"))
.split(":");
console.log("Provided credentials for proxy", details.challenger.host);
return { authCredentials: { username, password } };
const proxy = proxies.find(predicate);
if (!proxy) {
console.error(
`🔐 No credentials found for proxy ${details.challenger.host}:${details.challenger.port}.`
);
return;
}

console.log(`🔑 Providing credentials for proxy ${proxy}.`);
const proxyInfo = getProxyInfoFromUrl(proxy);
return {
authCredentials: {
username: proxyInfo.username,
password: proxyInfo.password,
},
};
}
3 changes: 2 additions & 1 deletion src/background/handlers/onHeadersReceived.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import getHostFromUrl from "../../common/ts/getHostFromUrl";
import {
passportHostRegex,
twitchGqlHostRegex,
twitchTvHostRegex,
usherHostRegex,
videoWeaverHostRegex,
} from "../../common/ts/regexes";
Expand All @@ -21,7 +22,7 @@ export default function onHeadersReceived(
const proxy = getProxyFromDetails(details);

// Twitch webpage requests.
if (store.state.proxyTwitchWebpage && host === "www.twitch.tv") {
if (store.state.proxyTwitchWebpage && twitchTvHostRegex.test(host)) {
if (!proxy) return console.log(`❌ Did not proxy ${details.url}`);
console.log(`✅ Proxied ${details.url} through ${proxy}`);
}
Expand Down
31 changes: 10 additions & 21 deletions src/background/handlers/onProxyRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import { Proxy } from "webextension-polyfill";
import findChannelFromUsherUrl from "../../common/ts/findChannelFromUsherUrl";
import findChannelFromVideoWeaverUrl from "../../common/ts/findChannelFromVideoWeaverUrl";
import getHostFromUrl from "../../common/ts/getHostFromUrl";
import getProxyInfoFromUrl from "../../common/ts/getProxyInfoFromUrl";
import isChannelWhitelisted from "../../common/ts/isChannelWhitelisted";
import isFlaggedRequest from "../../common/ts/isFlaggedRequest";
import {
passportHostRegex,
twitchGqlHostRegex,
twitchTvHostRegex,
usherHostRegex,
videoWeaverHostRegex,
} from "../../common/ts/regexes";
Expand Down Expand Up @@ -34,19 +36,13 @@ export default async function onProxyRequest(
(store.state.optimizedProxiesEnabled &&
isFlaggedRequest(details.requestHeaders)) ||
!store.state.optimizedProxiesEnabled;
const proxies = (
store.state.optimizedProxiesEnabled
? store.state.optimizedProxies
: store.state.normalProxies
).map(host =>
host.includes("@")
? host.slice(host.lastIndexOf("@") + 1, host.length)
: host
);
const proxyInfoArray = getProxyInfoArrayFromHosts(proxies);
const proxies = store.state.optimizedProxiesEnabled
? store.state.optimizedProxies
: store.state.normalProxies;
const proxyInfoArray = getProxyInfoArrayFromUrls(proxies);

// Twitch webpage requests.
if (store.state.proxyTwitchWebpage && host === "www.twitch.tv") {
if (store.state.proxyTwitchWebpage && twitchTvHostRegex.test(host)) {
console.log(`⌛ Proxying ${details.url} through one of: <empty>`);
return proxyInfoArray;
}
Expand Down Expand Up @@ -91,7 +87,7 @@ export default async function onProxyRequest(
return proxyInfoArray;
}

// Video-weaver requests.
// Video Weaver requests.
if (videoWeaverHostRegex.test(host) && isFlagged) {
// Don't proxy whitelisted channels.
const channelName = findChannelFromVideoWeaverUrl(details.url);
Expand All @@ -110,16 +106,9 @@ export default async function onProxyRequest(
return { type: "direct" };
}

function getProxyInfoArrayFromHosts(hosts: string[]): ProxyInfo[] {
function getProxyInfoArrayFromUrls(urls: string[]): ProxyInfo[] {
return [
...hosts.map(host => {
const [hostname, port] = host.split(":");
return {
type: "http",
host: hostname,
port: Number(port) ?? 3128,
} as ProxyInfo;
}),
...urls.map(url => getProxyInfoFromUrl(url)),
{ type: "direct" } as ProxyInfo, // Fallback to direct connection if all proxies fail.
];
}
42 changes: 42 additions & 0 deletions src/common/ts/anonymizeIpAddress.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import ip from "ip";
import getProxyInfoFromUrl from "./getProxyInfoFromUrl";

/**
* Anonymize an IP address by masking the last 2 octets of an IPv4 address
* or the last 8 octets of an IPv6 address.
* @param url
* @returns
*/
export function anonymizeIpAddress(url: string): string {
const proxyInfo = getProxyInfoFromUrl(url);

let proxyHost = proxyInfo.host;
const withinBrackets = /^\[.*\]$/.test(proxyHost);
if (withinBrackets) proxyHost = proxyHost.slice(1, -1);

const isIPv4 = ip.isV4Format(proxyHost);
const isIPv6 = ip.isV6Format(proxyHost);
const isIP = isIPv4 || isIPv6;
const isPublicIP = isIP && !ip.isPrivate(proxyHost);

if (isPublicIP) {
if (isIPv4) {
proxyHost = ip.mask(proxyHost, "255.255.0.0").replace(/\.0\.0$/, ".*.*");
} else if (isIPv6) {
proxyHost = ip.mask(proxyHost, "ffff:ffff:ffff:ffff:0000:0000:0000:0000");
}
}

if (withinBrackets) proxyHost = `[${proxyHost}]`;

return `${proxyHost}:${proxyInfo.port}`;
}

/**
* Anonymize an array of IP addresses. See {@link anonymizeIpAddress}.
* @param urls
* @returns
*/
export function anonymizeIpAddresses(urls: string[]): string[] {
return urls.map(url => anonymizeIpAddress(url));
}
3 changes: 1 addition & 2 deletions src/common/ts/getHostFromUrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ export default function getHostFromUrl(url: string) {
try {
const Url = new URL(url);
return Url.host;
} catch (error) {
console.error(error);
} catch {
return null;
}
}
57 changes: 57 additions & 0 deletions src/common/ts/getProxyInfoFromUrl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import type { ProxyInfo } from "../../types";

export default function getProxyInfoFromUrl(url: string): ProxyInfo {
const lastIndexOfAt = url.lastIndexOf("@");
const hostname = url.substring(lastIndexOfAt + 1, url.length);
const lastIndexOfColon = getLastIndexOfColon(hostname);

let host: string | undefined = undefined;
let port: number | undefined = undefined;
if (lastIndexOfColon === -1) {
host = hostname;
port = 3128; // Default port
} else {
host = hostname.substring(0, lastIndexOfColon);
port = Number(hostname.substring(lastIndexOfColon + 1, hostname.length));
}

let username: string | undefined = undefined;
let password: string | undefined = undefined;
if (lastIndexOfAt !== -1) {
const credentials = url.substring(0, lastIndexOfAt);
const indexOfColon = credentials.indexOf(":");
username = credentials.substring(0, indexOfColon);
password = credentials.substring(indexOfColon + 1, credentials.length);
}

return {
type: "http",
host,
port,
username,
password,
};
}

/**
* Returns the last index of a colon in a hostname, ignoring colons inside brackets.
* Supports IPv6 addresses.
* @param hostname
* @returns Returns -1 if no colon is found.
*/
function getLastIndexOfColon(hostname: string): number {
let lastIndexOfColon = -1;
let bracketDepth = 0;
for (let i = hostname.length - 1; i >= 0; i--) {
const char = hostname[i];
if (char === "]") {
bracketDepth++;
} else if (char === "[") {
bracketDepth--;
} else if (char === ":" && bracketDepth === 0) {
lastIndexOfColon = i;
break;
}
}
return lastIndexOfColon;
}
3 changes: 2 additions & 1 deletion src/common/ts/regexes.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
export const passportHostRegex = /^passport\.twitch\.tv$/i;
export const twitchApiChannelNameRegex = /\/hls\/(.+)\.m3u8/i;
export const twitchChannelNameRegex =
/^https?:\/\/(?:(?:www|m)\.)?twitch\.tv\/(?:videos\/)?(\w+)/i;
/^https?:\/\/(?:www|m)\.twitch\.tv\/(?:videos\/)?(\w+)/i;
export const twitchGqlHostRegex = /^gql\.twitch\.tv$/i;
export const twitchTvHostRegex = /^(?:www|m)\.twitch\.tv$/i;
export const usherHostRegex = /^usher\.ttvnw\.net$/i;
export const videoWeaverHostRegex = /^video-weaver\.\w+\.hls\.ttvnw\.net$/i;
export const videoWeaverUrlRegex =
Expand Down
Loading

0 comments on commit 5c90115

Please sign in to comment.