Skip to content

Commit

Permalink
Merge pull request #340 from younesaassila/v2.3.8
Browse files Browse the repository at this point in the history
Release version 2.3.8
  • Loading branch information
younesaassila authored Aug 23, 2024
2 parents e2aed80 + bb8078d commit 4cf96c3
Show file tree
Hide file tree
Showing 22 changed files with 476 additions and 1,438 deletions.
11 changes: 10 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
name: Build and Test
on: [push, pull_request]
on:
push:
branches:
- "**"
tags-ignore:
- "**"
pull_request:
branches:
- "**"

jobs:
build:
concurrency: ci-${{ github.ref }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
</div>

<div align="center">
<a href="https://discord.gg/AmtFTPwsyH">
<a href="https://discord.ttvlolpro.com/">
<img
alt="Discord server"
src="https://dcbadge.vercel.app/api/server/AmtFTPwsyH"
Expand Down Expand Up @@ -78,7 +78,7 @@ TTV LOL PRO is a fork of TTV LOL that:

TTV LOL PRO does not remove banner ads, nor does it remove ads from VODs. For the best experience, we recommend using [uBlock Origin](https://ublockorigin.com/) alongside TTV LOL PRO.

Any questions? Please read the [wiki](https://wiki.cdn-perfprod.com/) first.
Any questions? Please read the [wiki](https://wiki.ttvlolpro.com/) first.

## Screenshots

Expand Down
1,665 changes: 296 additions & 1,369 deletions package-lock.json

Large diffs are not rendered by default.

17 changes: 9 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ttv-lol-pro",
"version": "2.3.7",
"version": "2.3.8",
"description": "TTV LOL PRO removes most livestream ads from Twitch.",
"@parcel/bundler-default": {
"minBundles": 10000000,
Expand Down Expand Up @@ -43,24 +43,25 @@
"license": "GPL-3.0",
"dependencies": {
"bowser": "^2.11.0",
"ip": "^2.0.1",
"ip-address": "^9.0.5",
"m3u8-parser": "^7.1.0"
},
"devDependencies": {
"@parcel/config-webextension": "^2.12.0",
"@types/chrome": "^0.0.267",
"@types/ip": "^1.1.3",
"@types/webextension-polyfill": "^0.10.7",
"@types/chrome": "^0.0.270",
"@types/jsbn": "^1.2.33",
"@types/node": "^20.16.1",
"@types/webextension-polyfill": "^0.12.0",
"buffer": "^6.0.3",
"os-browserify": "^0.3.0",
"parcel": "^2.12.0",
"postcss": "^8.4.38",
"postcss": "^8.4.41",
"prettier": "2.8.8",
"prettier-plugin-css-order": "^1.3.1",
"prettier-plugin-organize-imports": "^3.2.4",
"shx": "^0.3.4",
"typescript": "^5.4.5",
"webextension-polyfill": "^0.11.0"
"typescript": "^5.5.4",
"webextension-polyfill": "^0.12.0"
},
"private": true
}
2 changes: 1 addition & 1 deletion src/background/handlers/onAuthRequired.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const pendingRequests: string[] = [];

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

if (pendingRequests.includes(details.requestId)) {
Expand Down
2 changes: 1 addition & 1 deletion src/background/handlers/onBeforeTwitchTvSendHeaders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { twitchTvHostRegex } from "../../common/ts/regexes";

export default function onBeforeTwitchTvSendHeaders(
details: WebRequest.OnBeforeSendHeadersDetailsType
): void | WebRequest.BlockingResponseOrPromise {
): WebRequest.BlockingResponseOrPromise | undefined {
const host = getHostFromUrl(details.url);
if (!host || !twitchTvHostRegex.test(host)) return;

Expand Down
6 changes: 2 additions & 4 deletions src/background/handlers/onBeforeVideoWeaverRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default function onBeforeVideoWeaverRequest(
details: WebRequest.OnBeforeRequestDetailsType & {
proxyInfo?: ProxyInfo;
}
): void | WebRequest.BlockingResponseOrPromise {
): WebRequest.BlockingResponseOrPromise | undefined {
// Filter to video-weaver responses.
const host = getHostFromUrl(details.url);
if (!host || !videoWeaverHostRegex.test(host)) return;
Expand All @@ -34,9 +34,7 @@ export default function onBeforeVideoWeaverRequest(
if (isDuplicate) return text;

const channelName = findChannelFromVideoWeaverUrl(details.url);
const isPurpleScreen = textLower.includes(
"https://help.twitch.tv/s/article/ad-experience-on-twitch"
);
const isPurpleScreen = textLower.includes("https://help.twitch.tv/");
const proxy =
details.proxyInfo && details.proxyInfo.type !== "direct"
? getUrlFromProxyInfo(details.proxyInfo)
Expand Down
4 changes: 2 additions & 2 deletions src/background/handlers/onContentScriptMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ const fetchTimeoutMsOverride: Map<ProxyRequestType, number> = new Map([
export default function onContentScriptMessage(
message: any,
sender: Runtime.MessageSender,
sendResponse: () => void
): true | void | Promise<any> {
sendResponse: (message: any) => void
): Promise<any> | true | undefined {
if (message.type === MessageType.EnableFullMode) {
if (!sender.tab?.id) return;

Expand Down
45 changes: 35 additions & 10 deletions src/background/handlers/onResponseStarted.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,13 @@ export default async function onResponseStarted(
const host = getHostFromUrl(details.url);
if (!host) return;

const proxy = getProxyFromDetails(details);
let proxy: string | null = null;
let errorMessage: string | null = null;
try {
proxy = getProxyFromDetails(details);
} catch (error) {
errorMessage = error instanceof Error ? error.message : `${error}`;
}

const requestParams = {
isChromium: isChromium,
Expand Down Expand Up @@ -79,20 +85,34 @@ export default async function onResponseStarted(
findChannelFromTwitchTvUrl(tabUrl);
const streamStatus = getStreamStatus(channelName);
const stats = streamStatus?.stats ?? { proxied: 0, notProxied: 0 };

if (!proxy) {
stats.notProxied++;
let reason = errorMessage ?? streamStatus?.reason ?? "";
try {
const proxySettings = await browser.proxy.settings.get({});
switch (proxySettings.levelOfControl) {
case "controlled_by_other_extensions":
reason = "Proxy settings controlled by other extension";
break;
case "not_controllable":
reason = "Proxy settings not controllable";
break;
}
} catch {}
setStreamStatus(channelName, {
proxied: false,
proxyHost: streamStatus?.proxyHost ? streamStatus.proxyHost : undefined,
proxyCountry: streamStatus?.proxyCountry,
reason: streamStatus?.reason ?? "",
reason,
stats,
});
console.log(
`❌ Did not proxy ${details.url} (${channelName ?? "unknown"})`
);
return;
}

stats.proxied++;
setStreamStatus(channelName, {
proxied: true,
Expand Down Expand Up @@ -126,24 +146,29 @@ function getProxyFromDetails(
}
): string | null {
if (isChromium) {
const proxies = [
...store.state.optimizedProxies,
...store.state.normalProxies,
];
const isDnsError =
proxies.length !== 0 && store.state.dnsResponses.length === 0;
if (isDnsError) {
throw new Error(
"Cannot detect if requests are being proxied due to a DNS error"
);
}
const ip = details.ip;
if (!ip) return null;
const dnsResponse = store.state.dnsResponses.find(
dnsResponse => dnsResponse.ips.indexOf(ip) !== -1
);
if (!dnsResponse) return null;
const proxies = [
...store.state.optimizedProxies,
...store.state.normalProxies,
];
const proxyInfoArray = proxies.map(getProxyInfoFromUrl);
const possibleProxies = proxyInfoArray.filter(
proxy => proxy.host === dnsResponse.host
);
if (possibleProxies.length === 1)
return getUrlFromProxyInfo(possibleProxies[0]);
// TODO: Set reason to some error message about DNS.
return dnsResponse.host;
if (possibleProxies.length === 0) return dnsResponse.host;
return getUrlFromProxyInfo(possibleProxies[0]);
} else {
const proxyInfo = details.proxyInfo; // Firefox only.
if (!proxyInfo || proxyInfo.type === "direct") return null;
Expand Down
23 changes: 17 additions & 6 deletions src/common/ts/anonymizeIpAddress.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import ip from "ip";
import { Address4, Address6 } from "ip-address";
import isPrivateIp from "./isPrivateIp";
import { getProxyInfoFromUrl } from "./proxyInfo";

/**
Expand All @@ -12,16 +13,26 @@ export function anonymizeIpAddress(url: string): string {

let proxyHost = proxyInfo.host;

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

if (isPublicIP) {
if (isIPv4) {
proxyHost = ip.mask(proxyHost, "255.255.0.0").replace(/\.0\.0$/, ".*.*");
proxyHost = new Address4(proxyHost)
.correctForm()
.split(".")
.map((byte, index) => (index < 2 ? byte : "xxx"))
.join(".");
} else if (isIPv6) {
proxyHost = ip.mask(proxyHost, "ffff:ffff:ffff:ffff:0000:0000:0000:0000");
const bytes = new Address6(proxyHost).toByteArray();
const anonymizedBytes = bytes.map((byte, index) =>
index < 6 ? byte : 0x0
);
proxyHost = Address6.fromByteArray(anonymizedBytes)
.correctForm()
.replace(/::$/, "::xxxx");
}
}

Expand Down
27 changes: 27 additions & 0 deletions src/common/ts/isPrivateIp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Address4, Address6 } from "ip-address";

const ip4LinkLocalSubnet = new Address4("169.254.0.0/16");
const ip4LoopbackSubnet = new Address4("127.0.0.0/8");
const ip4PrivateASubnet = new Address4("10.0.0.0/8");
const ip4PrivateBSubnet = new Address4("172.16.0.0/12");
const ip4PrivateCSubnet = new Address4("192.168.0.0/16");

export default function isPrivateIp(address: string): boolean {
try {
const ip4 = new Address4(address);
return (
ip4.isInSubnet(ip4LinkLocalSubnet) ||
ip4.isInSubnet(ip4LoopbackSubnet) ||
ip4.isInSubnet(ip4PrivateASubnet) ||
ip4.isInSubnet(ip4PrivateBSubnet) ||
ip4.isInSubnet(ip4PrivateCSubnet)
);
} catch (error) {}

try {
const ip6 = new Address6(address);
return ip6.isLinkLocal() || ip6.isLoopback();
} catch (error) {}

return false;
}
12 changes: 6 additions & 6 deletions src/common/ts/proxyInfo.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import ip from "ip";
import { Address6 } from "ip-address";
import type { ProxyInfo } from "../../types";

export function getProxyInfoFromUrl(
Expand Down Expand Up @@ -71,14 +71,14 @@ export function getUrlFromProxyInfo(proxyInfo: ProxyInfo): string {
} else if (username) {
url = `${username}@`;
}
const isIPv4 = ip.isV4Format(host);
const isIPv6 = ip.isV6Format(host);
// isV6Format() returns true for IPv4 addresses, so we need to exclude those.
if (isIPv6 && !isIPv4) {
const isIPv6 = Address6.isValid(host);
if (isIPv6) {
url += `[${host}]`;
} else {
url += host;
}
if (port) url += `:${port}`;
if (port) {
url += `:${port}`;
}
return url;
}
29 changes: 16 additions & 13 deletions src/common/ts/updateDnsResponses.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import ip from "ip";
import { Address4, Address6 } from "ip-address";
import store from "../../store";
import type { DnsResponse, DnsResponseJson } from "../../types";
import { getProxyInfoFromUrl } from "./proxyInfo";

export default async function updateDnsResponses() {
const proxies = store.state.optimizedProxiesEnabled
? store.state.optimizedProxies
: store.state.normalProxies;
const proxies = [
...store.state.optimizedProxies,
...store.state.normalProxies,
];
const proxyInfoArray = proxies.map(getProxyInfoFromUrl);

for (const proxyInfo of proxyInfoArray) {
Expand All @@ -25,18 +26,19 @@ export default async function updateDnsResponses() {
}

// If the host is an IP address, we don't need to make a DNS request.
const isIp = ip.isV4Format(host) || ip.isV6Format(host);
const isIp = Address4.isValid(host) || Address6.isValid(host);
if (isIp) {
if (dnsResponseIndex !== -1) {
store.state.dnsResponses.splice(dnsResponseIndex, 1);
}
const dnsResponse: DnsResponse = {
host,
ips: [host],
timestamp: Date.now(),
ttl: Infinity,
};
store.state.dnsResponses.push(dnsResponse);
if (dnsResponseIndex !== -1) {
store.state.dnsResponses.splice(dnsResponseIndex, 1, dnsResponse);
} else {
store.state.dnsResponses.push(dnsResponse);
}
continue;
}

Expand All @@ -59,16 +61,17 @@ export default async function updateDnsResponses() {
}
const { Answer } = data;

if (dnsResponseIndex !== -1) {
store.state.dnsResponses.splice(dnsResponseIndex, 1);
}
const dnsResponse: DnsResponse = {
host,
ips: Answer.map(answer => answer.data),
timestamp: Date.now(),
ttl: Math.max(Math.max(...Answer.map(answer => answer.TTL)), 300),
};
store.state.dnsResponses.push(dnsResponse);
if (dnsResponseIndex !== -1) {
store.state.dnsResponses.splice(dnsResponseIndex, 1, dnsResponse);
} else {
store.state.dnsResponses.push(dnsResponse);
}
} catch (error) {
console.error(error);
}
Expand Down
Loading

0 comments on commit 4cf96c3

Please sign in to comment.