From 6799e8840449994a947b15d238a65b65f29820a7 Mon Sep 17 00:00:00 2001 From: Younes Aassila <47226184+younesaassila@users.noreply.github.com> Date: Thu, 15 Jun 2023 14:42:45 +0200 Subject: [PATCH 01/19] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Bump=20version=20num?= =?UTF-8?q?ber=20to=202.0.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 4 ++-- package.json | 2 +- src/manifest.chromium.json | 2 +- src/manifest.firefox.json | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index e0ede248..1ac5edb7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ttv-lol-pro", - "version": "2.0.2", + "version": "2.0.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ttv-lol-pro", - "version": "2.0.2", + "version": "2.0.3", "license": "GPL-3.0", "dependencies": { "bowser": "^2.11.0", diff --git a/package.json b/package.json index b6a090f4..c7edb431 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ttv-lol-pro", - "version": "2.0.2", + "version": "2.0.3", "description": "TTV LOL PRO removes most livestream ads from Twitch.", "@parcel/bundler-default": { "minBundles": 10000000, diff --git a/src/manifest.chromium.json b/src/manifest.chromium.json index 96e224d5..b0b052aa 100644 --- a/src/manifest.chromium.json +++ b/src/manifest.chromium.json @@ -2,7 +2,7 @@ "manifest_version": 3, "name": "TTV LOL PRO", "description": "TTV LOL PRO removes most livestream ads from Twitch.", - "version": "2.0.2", + "version": "2.0.3", "background": { "service_worker": "background/background.ts", "type": "module" diff --git a/src/manifest.firefox.json b/src/manifest.firefox.json index 6b144ad5..901944c1 100644 --- a/src/manifest.firefox.json +++ b/src/manifest.firefox.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "TTV LOL PRO", "description": "TTV LOL PRO removes most livestream ads from Twitch.", - "version": "2.0.2", + "version": "2.0.3", "background": { "scripts": ["background/background.ts"], "persistent": false From 7a08e760d5901a9686c21ce68380fb16488c3dd3 Mon Sep 17 00:00:00 2001 From: Younes Aassila <47226184+younesaassila@users.noreply.github.com> Date: Thu, 15 Jun 2023 14:43:22 +0200 Subject: [PATCH 02/19] =?UTF-8?q?=F0=9F=90=9B=20Fix=20#193=20(#194)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/page/getFetch.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/page/getFetch.ts b/src/page/getFetch.ts index 0203da62..40be9157 100644 --- a/src/page/getFetch.ts +++ b/src/page/getFetch.ts @@ -22,7 +22,13 @@ export function getFetch(options: FetchOptions = {}): typeof fetch { const url = input instanceof Request ? input.url : input.toString(); // Firefox doesn't support relative URLs in content scripts (workers too!). // See https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Chrome_incompatibilities#content_script_https_requests - if (url.startsWith("/")) { + if (url.startsWith("//")) { + // Missing protocol. + const newUrl = `${location.protocol}${url}`; + if (input instanceof Request) input = new Request(newUrl, input); + else input = newUrl; + } else if (url.startsWith("/")) { + // Missing origin. const newUrl = `${location.origin}${url}`; if (input instanceof Request) input = new Request(newUrl, input); else input = newUrl; From d9e65b818a98f197d1d9286f7bf22d2e7bf58496 Mon Sep 17 00:00:00 2001 From: Younes Aassila <47226184+younesaassila@users.noreply.github.com> Date: Thu, 15 Jun 2023 14:44:02 +0200 Subject: [PATCH 03/19] =?UTF-8?q?=F0=9F=90=9B=20Fix=20#165=20(#195)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * πŸ› Fix #165 * 🚸 Check for opened Twitch tabs on startup --- src/background/background.ts | 18 +++++---- .../handlers/checkForOpenedTwitchTabs.ts | 22 ++++++++++ .../handlers/onStartupStoreCleanup.ts | 1 + src/background/handlers/onTabCreated.ts | 18 +++++++++ src/background/handlers/onTabRemoved.ts | 14 +++++++ src/background/handlers/onTabUpdated.ts | 40 +++++++++++++++++++ src/common/ts/clearProxySettings.ts | 5 +++ src/manifest.chromium.json | 1 + src/options/options.ts | 12 ++++-- src/store/getDefaultState.ts | 6 ++- src/store/types.ts | 1 + 11 files changed, 125 insertions(+), 13 deletions(-) create mode 100644 src/background/handlers/checkForOpenedTwitchTabs.ts create mode 100644 src/background/handlers/onTabCreated.ts create mode 100644 src/background/handlers/onTabRemoved.ts create mode 100644 src/background/handlers/onTabUpdated.ts create mode 100644 src/common/ts/clearProxySettings.ts diff --git a/src/background/background.ts b/src/background/background.ts index 21ae58d4..4eb4077f 100644 --- a/src/background/background.ts +++ b/src/background/background.ts @@ -1,7 +1,6 @@ import browser from "webextension-polyfill"; import isChromium from "../common/ts/isChromium"; -import updateProxySettings from "../common/ts/updateProxySettings"; -import store from "../store"; +import checkForOpenedTwitchTabs from "./handlers/checkForOpenedTwitchTabs"; import onAuthRequired from "./handlers/onAuthRequired"; import onBeforeSendHeaders from "./handlers/onBeforeSendHeaders"; import onBeforeUsherRequest from "./handlers/onBeforeUsherRequest"; @@ -9,6 +8,9 @@ import onBeforeVideoWeaverRequest from "./handlers/onBeforeVideoWeaverRequest"; import onHeadersReceived from "./handlers/onHeadersReceived"; import onProxyRequest from "./handlers/onProxyRequest"; import onStartupStoreCleanup from "./handlers/onStartupStoreCleanup"; +import onTabCreated from "./handlers/onTabCreated"; +import onTabRemoved from "./handlers/onTabRemoved"; +import onTabUpdated from "./handlers/onTabUpdated"; console.info("πŸš€ Background script loaded."); @@ -23,12 +25,12 @@ browser.webRequest.onAuthRequired.addListener( ); if (isChromium) { - const setProxySettings = () => { - if (store.readyState !== "complete") - return store.addEventListener("load", setProxySettings); - updateProxySettings(); - }; - setProxySettings(); + // Check if there are any opened Twitch tabs on startup. + checkForOpenedTwitchTabs(); + // Keep track of opened Twitch tabs to enable/disable the PAC script. + browser.tabs.onCreated.addListener(onTabCreated); + browser.tabs.onUpdated.addListener(onTabUpdated); + browser.tabs.onRemoved.addListener(onTabRemoved); } else { // Block tracking pixels. browser.webRequest.onBeforeRequest.addListener( diff --git a/src/background/handlers/checkForOpenedTwitchTabs.ts b/src/background/handlers/checkForOpenedTwitchTabs.ts new file mode 100644 index 00000000..10bc08cb --- /dev/null +++ b/src/background/handlers/checkForOpenedTwitchTabs.ts @@ -0,0 +1,22 @@ +import browser from "webextension-polyfill"; +import isChromium from "../../common/ts/isChromium"; +import updateProxySettings from "../../common/ts/updateProxySettings"; +import store from "../../store"; + +export default function checkForOpenedTwitchTabs() { + if (store.readyState !== "complete") + return store.addEventListener("load", checkForOpenedTwitchTabs); + + browser.tabs.query({ url: ["https://*.twitch.tv/*"] }).then(tabs => { + if (tabs.length === 0) return; + console.log( + `πŸ” Found ${tabs.length} opened Twitch tabs: ${tabs + .map(tab => tab.id) + .join(", ")}` + ); + if (isChromium) { + updateProxySettings(); + } + tabs.forEach(tab => store.state.openedTwitchTabs.push(tab.id)); + }); +} diff --git a/src/background/handlers/onStartupStoreCleanup.ts b/src/background/handlers/onStartupStoreCleanup.ts index 4cb99061..af9db4b8 100644 --- a/src/background/handlers/onStartupStoreCleanup.ts +++ b/src/background/handlers/onStartupStoreCleanup.ts @@ -12,6 +12,7 @@ export default function onStartupStoreCleanup(): void { if (store.readyState !== "complete") return store.addEventListener("load", onStartupStoreCleanup); + store.state.openedTwitchTabs = []; store.state.streamStatuses = {}; store.state.videoWeaverUrlsByChannel = {}; } diff --git a/src/background/handlers/onTabCreated.ts b/src/background/handlers/onTabCreated.ts new file mode 100644 index 00000000..c7b00616 --- /dev/null +++ b/src/background/handlers/onTabCreated.ts @@ -0,0 +1,18 @@ +import { Tabs } from "webextension-polyfill"; +import getHostFromUrl from "../../common/ts/getHostFromUrl"; +import isChromium from "../../common/ts/isChromium"; +import { twitchTvHostRegex } from "../../common/ts/regexes"; +import updateProxySettings from "../../common/ts/updateProxySettings"; +import store from "../../store"; + +export default function onTabCreated(tab: Tabs.Tab): void { + if (!tab.url) return; + const host = getHostFromUrl(tab.url); + if (twitchTvHostRegex.test(host)) { + console.log(`βž• Opened Twitch tab: ${tab.id}`); + if (isChromium && store.state.openedTwitchTabs.length === 0) { + updateProxySettings(); + } + store.state.openedTwitchTabs.push(tab.id); + } +} diff --git a/src/background/handlers/onTabRemoved.ts b/src/background/handlers/onTabRemoved.ts new file mode 100644 index 00000000..6d644507 --- /dev/null +++ b/src/background/handlers/onTabRemoved.ts @@ -0,0 +1,14 @@ +import clearProxySettings from "../../common/ts/clearProxySettings"; +import isChromium from "../../common/ts/isChromium"; +import store from "../../store"; + +export default function onTabRemoved(tabId: number): void { + const index = store.state.openedTwitchTabs.indexOf(tabId); + if (index !== -1) { + console.log(`βž– Closed Twitch tab: ${tabId}`); + store.state.openedTwitchTabs.splice(index, 1); + if (isChromium && store.state.openedTwitchTabs.length === 0) { + clearProxySettings(); + } + } +} diff --git a/src/background/handlers/onTabUpdated.ts b/src/background/handlers/onTabUpdated.ts new file mode 100644 index 00000000..0a0f1e79 --- /dev/null +++ b/src/background/handlers/onTabUpdated.ts @@ -0,0 +1,40 @@ +import { Tabs } from "webextension-polyfill"; +import clearProxySettings from "../../common/ts/clearProxySettings"; +import getHostFromUrl from "../../common/ts/getHostFromUrl"; +import isChromium from "../../common/ts/isChromium"; +import { twitchTvHostRegex } from "../../common/ts/regexes"; +import updateProxySettings from "../../common/ts/updateProxySettings"; +import store from "../../store"; + +export default function onTabUpdated( + tabId: number, + changeInfo: Tabs.OnUpdatedChangeInfoType, + tab: Tabs.Tab +): void { + // Also check for `changeInfo.status === "complete"` because the `url` property + // is not always accurate when navigating to a new page. + if (!(changeInfo.url || changeInfo.status === "complete")) return; + + const url = changeInfo.url || tab.url; + const host = getHostFromUrl(url); + const isTwitchTab = twitchTvHostRegex.test(host); + const wasTwitchTab = store.state.openedTwitchTabs.includes(tabId); + + if (isTwitchTab && !wasTwitchTab) { + console.log(`βž• Opened Twitch tab: ${tabId}`); + if (isChromium && store.state.openedTwitchTabs.length === 0) { + updateProxySettings(); + } + store.state.openedTwitchTabs.push(tabId); + } + if (!isTwitchTab && wasTwitchTab) { + const index = store.state.openedTwitchTabs.indexOf(tabId); + if (index !== -1) { + console.log(`βž– Closed Twitch tab: ${tabId}`); + store.state.openedTwitchTabs.splice(index, 1); + if (isChromium && store.state.openedTwitchTabs.length === 0) { + clearProxySettings(); + } + } + } +} diff --git a/src/common/ts/clearProxySettings.ts b/src/common/ts/clearProxySettings.ts new file mode 100644 index 00000000..3d6293e5 --- /dev/null +++ b/src/common/ts/clearProxySettings.ts @@ -0,0 +1,5 @@ +export default function clearProxySettings() { + chrome.proxy.settings.clear({ scope: "regular" }, function () { + console.log("βš™οΈ Proxy settings cleared"); + }); +} diff --git a/src/manifest.chromium.json b/src/manifest.chromium.json index b0b052aa..dbc7ae22 100644 --- a/src/manifest.chromium.json +++ b/src/manifest.chromium.json @@ -35,6 +35,7 @@ "declarativeNetRequest", "proxy", "storage", + "tabs", "webRequest", "webRequestAuthProvider" ], diff --git a/src/options/options.ts b/src/options/options.ts index ad7db528..a245c6e3 100644 --- a/src/options/options.ts +++ b/src/options/options.ts @@ -92,12 +92,16 @@ function main() { proxyUsherRequestsCheckboxElement.addEventListener("change", () => { const checked = proxyUsherRequestsCheckboxElement.checked; store.state.proxyUsherRequests = checked; - if (isChromium) updateProxySettings(); + if (isChromium && store.state.openedTwitchTabs.length > 0) { + updateProxySettings(); + } }); proxyTwitchWebpageCheckboxElement.checked = store.state.proxyTwitchWebpage; proxyTwitchWebpageCheckboxElement.addEventListener("change", () => { store.state.proxyTwitchWebpage = proxyTwitchWebpageCheckboxElement.checked; - if (isChromium) updateProxySettings(); + if (isChromium && store.state.openedTwitchTabs.length > 0) { + updateProxySettings(); + } }); // Whitelisted channels if (isChromium) { @@ -142,7 +146,9 @@ function main() { isAddAllowed: isNormalProxyUrlAllowed, isEditAllowed: isNormalProxyUrlAllowed, onEdit() { - if (isChromium) updateProxySettings(); + if (isChromium && store.state.openedTwitchTabs.length > 0) { + updateProxySettings(); + } }, hidePromptMarker: true, insertMode: "both", diff --git a/src/store/getDefaultState.ts b/src/store/getDefaultState.ts index cc571f83..05339e5e 100644 --- a/src/store/getDefaultState.ts +++ b/src/store/getDefaultState.ts @@ -2,11 +2,12 @@ import isChromium from "../common/ts/isChromium"; import type { State } from "./types"; export default function getDefaultState() { - return { + const state: State = { adLog: [], adLogEnabled: true, adLogLastSent: 0, normalProxies: isChromium ? ["chrome.api.cdn-perfprod.com:4023"] : [], + openedTwitchTabs: [], optimizedProxies: isChromium ? [] : ["firefox.api.cdn-perfprod.com:2023"], optimizedProxiesEnabled: !isChromium, proxyTwitchWebpage: false, @@ -14,5 +15,6 @@ export default function getDefaultState() { streamStatuses: {}, videoWeaverUrlsByChannel: {}, whitelistedChannels: [], - } as State; + }; + return state; } diff --git a/src/store/types.ts b/src/store/types.ts index 8b172e53..2d624cff 100644 --- a/src/store/types.ts +++ b/src/store/types.ts @@ -9,6 +9,7 @@ export interface State { adLogEnabled: boolean; adLogLastSent: number; normalProxies: string[]; + openedTwitchTabs: number[]; optimizedProxies: string[]; optimizedProxiesEnabled: boolean; proxyTwitchWebpage: boolean; From efea9f0902bab474a2af641908043ef7337c636e Mon Sep 17 00:00:00 2001 From: Younes Aassila <47226184+younesaassila@users.noreply.github.com> Date: Thu, 15 Jun 2023 14:46:20 +0200 Subject: [PATCH 04/19] =?UTF-8?q?=F0=9F=9A=9A=20Rename=20rules=20to=20rule?= =?UTF-8?q?sets?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/manifest.chromium.json | 4 ++-- src/{rules/rules.json => rulesets/ruleset.json} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename src/{rules/rules.json => rulesets/ruleset.json} (100%) diff --git a/src/manifest.chromium.json b/src/manifest.chromium.json index dbc7ae22..53ee37a7 100644 --- a/src/manifest.chromium.json +++ b/src/manifest.chromium.json @@ -10,9 +10,9 @@ "declarative_net_request": { "rule_resources": [ { - "id": "rules", + "id": "ruleset", "enabled": true, - "path": "rules/rules.json" + "path": "rulesets/ruleset.json" } ] }, diff --git a/src/rules/rules.json b/src/rulesets/ruleset.json similarity index 100% rename from src/rules/rules.json rename to src/rulesets/ruleset.json From 0589679a1e007391d0bb0730439c5dca2a28343f Mon Sep 17 00:00:00 2001 From: Younes Aassila <47226184+younesaassila@users.noreply.github.com> Date: Thu, 15 Jun 2023 16:56:19 +0200 Subject: [PATCH 05/19] =?UTF-8?q?=F0=9F=90=9B=20Fix=20Twitch=20tab=20never?= =?UTF-8?q?=20marked=20as=20closed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../handlers/checkForOpenedTwitchTabs.ts | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/background/handlers/checkForOpenedTwitchTabs.ts b/src/background/handlers/checkForOpenedTwitchTabs.ts index 10bc08cb..14c9740d 100644 --- a/src/background/handlers/checkForOpenedTwitchTabs.ts +++ b/src/background/handlers/checkForOpenedTwitchTabs.ts @@ -7,16 +7,18 @@ export default function checkForOpenedTwitchTabs() { if (store.readyState !== "complete") return store.addEventListener("load", checkForOpenedTwitchTabs); - browser.tabs.query({ url: ["https://*.twitch.tv/*"] }).then(tabs => { - if (tabs.length === 0) return; - console.log( - `πŸ” Found ${tabs.length} opened Twitch tabs: ${tabs - .map(tab => tab.id) - .join(", ")}` - ); - if (isChromium) { - updateProxySettings(); - } - tabs.forEach(tab => store.state.openedTwitchTabs.push(tab.id)); - }); + browser.tabs + .query({ url: ["https://www.twitch.tv/*", "https://m.twitch.tv/*"] }) + .then(tabs => { + if (tabs.length === 0) return; + console.log( + `πŸ” Found ${tabs.length} opened Twitch tabs: ${tabs + .map(tab => tab.id) + .join(", ")}` + ); + if (isChromium) { + updateProxySettings(); + } + tabs.forEach(tab => store.state.openedTwitchTabs.push(tab.id)); + }); } From b9eff7e95c586941a8f19a0a37f22aac3f36852b Mon Sep 17 00:00:00 2001 From: Younes Aassila <47226184+younesaassila@users.noreply.github.com> Date: Thu, 15 Jun 2023 21:17:34 +0200 Subject: [PATCH 06/19] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20Inject=20script=20on?= =?UTF-8?q?ly=20on=20www=20and=20m=20subdomains?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/manifest.firefox.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/manifest.firefox.json b/src/manifest.firefox.json index 901944c1..572cc2b0 100644 --- a/src/manifest.firefox.json +++ b/src/manifest.firefox.json @@ -22,7 +22,7 @@ }, "content_scripts": [ { - "matches": ["https://*.twitch.tv/*"], + "matches": ["https://www.twitch.tv/*", "https://m.twitch.tv/*"], "js": ["content/content.ts"], "run_at": "document_start" } From f9f2e5fe0e38f7e32040dbebc10be4a14b3d7847 Mon Sep 17 00:00:00 2001 From: Younes Aassila <47226184+younesaassila@users.noreply.github.com> Date: Sat, 17 Jun 2023 15:49:17 +0200 Subject: [PATCH 07/19] =?UTF-8?q?=F0=9F=94=8A=20Improve=20Chrome=20debuggi?= =?UTF-8?q?ng=20logs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/background.ts | 12 ++-- ...eadersReceived.ts => onResponseStarted.ts} | 36 ++++++++-- .../handlers/onStartupStoreCleanup.ts | 1 + src/common/ts/updateDnsResponses.ts | 70 +++++++++++++++++++ src/common/ts/updateProxySettings.ts | 2 + src/store/getDefaultState.ts | 1 + src/store/types.ts | 3 +- src/types.ts | 7 ++ 8 files changed, 118 insertions(+), 14 deletions(-) rename src/background/handlers/{onHeadersReceived.ts => onResponseStarted.ts} (73%) create mode 100644 src/common/ts/updateDnsResponses.ts diff --git a/src/background/background.ts b/src/background/background.ts index 4eb4077f..8dca5a77 100644 --- a/src/background/background.ts +++ b/src/background/background.ts @@ -5,8 +5,8 @@ import onAuthRequired from "./handlers/onAuthRequired"; import onBeforeSendHeaders from "./handlers/onBeforeSendHeaders"; import onBeforeUsherRequest from "./handlers/onBeforeUsherRequest"; import onBeforeVideoWeaverRequest from "./handlers/onBeforeVideoWeaverRequest"; -import onHeadersReceived from "./handlers/onHeadersReceived"; import onProxyRequest from "./handlers/onProxyRequest"; +import onResponseStarted from "./handlers/onResponseStarted"; import onStartupStoreCleanup from "./handlers/onStartupStoreCleanup"; import onTabCreated from "./handlers/onTabCreated"; import onTabRemoved from "./handlers/onTabRemoved"; @@ -24,6 +24,11 @@ browser.webRequest.onAuthRequired.addListener( ["blocking"] ); +// Monitor proxied status of requests. +browser.webRequest.onResponseStarted.addListener(onResponseStarted, { + urls: ["https://*.ttvnw.net/*", "https://*.twitch.tv/*"], +}); + if (isChromium) { // Check if there are any opened Twitch tabs on startup. checkForOpenedTwitchTabs(); @@ -74,9 +79,4 @@ if (isChromium) { }, ["blocking"] ); - - // Monitor responses of proxied requests. - browser.webRequest.onHeadersReceived.addListener(onHeadersReceived, { - urls: ["https://*.ttvnw.net/*", "https://*.twitch.tv/*"], - }); } diff --git a/src/background/handlers/onHeadersReceived.ts b/src/background/handlers/onResponseStarted.ts similarity index 73% rename from src/background/handlers/onHeadersReceived.ts rename to src/background/handlers/onResponseStarted.ts index 5b3a6fb8..03d2bd93 100644 --- a/src/background/handlers/onHeadersReceived.ts +++ b/src/background/handlers/onResponseStarted.ts @@ -1,6 +1,8 @@ import { WebRequest } from "webextension-polyfill"; import findChannelFromVideoWeaverUrl from "../../common/ts/findChannelFromVideoWeaverUrl"; import getHostFromUrl from "../../common/ts/getHostFromUrl"; +import getProxyInfoFromUrl from "../../common/ts/getProxyInfoFromUrl"; +import isChromium from "../../common/ts/isChromium"; import { passportHostRegex, twitchGqlHostRegex, @@ -11,11 +13,11 @@ import { import store from "../../store"; import type { ProxyInfo, StreamStatus } from "../../types"; -export default function onHeadersReceived( - details: WebRequest.OnHeadersReceivedDetailsType & { +export default function onResponseStarted( + details: WebRequest.OnResponseStartedDetailsType & { proxyInfo?: ProxyInfo; } -): void | WebRequest.BlockingResponseOrPromise { +): void { const host = getHostFromUrl(details.url); if (!host) return; @@ -77,13 +79,33 @@ export default function onHeadersReceived( } function getProxyFromDetails( - details: WebRequest.OnHeadersReceivedDetailsType & { + details: WebRequest.OnResponseStartedDetailsType & { proxyInfo?: ProxyInfo; } ): string | null { - const proxyInfo = details.proxyInfo; // Firefox only. - if (!proxyInfo || proxyInfo.type === "direct") return null; - return `${proxyInfo.host}:${proxyInfo.port}`; + if (isChromium) { + 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 `${possibleProxies[0].host}:${possibleProxies[0].port}`; + return dnsResponse.host; + } else { + const proxyInfo = details.proxyInfo; // Firefox only. + if (!proxyInfo || proxyInfo.type === "direct") return null; + return `${proxyInfo.host}:${proxyInfo.port}`; + } } function getStreamStatus(channelName: string | null): StreamStatus | null { diff --git a/src/background/handlers/onStartupStoreCleanup.ts b/src/background/handlers/onStartupStoreCleanup.ts index af9db4b8..15c88c1e 100644 --- a/src/background/handlers/onStartupStoreCleanup.ts +++ b/src/background/handlers/onStartupStoreCleanup.ts @@ -12,6 +12,7 @@ export default function onStartupStoreCleanup(): void { if (store.readyState !== "complete") return store.addEventListener("load", onStartupStoreCleanup); + store.state.dnsResponses = []; store.state.openedTwitchTabs = []; store.state.streamStatuses = {}; store.state.videoWeaverUrlsByChannel = {}; diff --git a/src/common/ts/updateDnsResponses.ts b/src/common/ts/updateDnsResponses.ts new file mode 100644 index 00000000..dcc1b1c7 --- /dev/null +++ b/src/common/ts/updateDnsResponses.ts @@ -0,0 +1,70 @@ +import ip from "ip"; +import store from "../../store"; +import type { DnsResponse } from "../../types"; +import getProxyInfoFromUrl from "./getProxyInfoFromUrl"; + +export default async function updateDnsResponses() { + const proxies = [ + ...store.state.optimizedProxies, + ...store.state.normalProxies, + ]; + const proxyInfoArray = proxies.map(getProxyInfoFromUrl); + + for (const proxyInfo of proxyInfoArray) { + const { host } = proxyInfo; + + const dnsResponseIndex = store.state.dnsResponses.findIndex( + dnsResponse => dnsResponse.host === host + ); + const dnsResponse = + dnsResponseIndex !== -1 + ? store.state.dnsResponses[dnsResponseIndex] + : null; + if ( + dnsResponse != null && + Date.now() - dnsResponse.timestamp < dnsResponse.ttl * 1000 + ) { + continue; + } + + if (ip.isV4Format(host) || ip.isV6Format(host)) { + if (dnsResponseIndex !== -1) { + store.state.dnsResponses.splice(dnsResponseIndex, 1); + } + store.state.dnsResponses.push({ + host, + ips: [host], + timestamp: Date.now(), + ttl: Infinity, + } as DnsResponse); + continue; + } + + try { + const response = await fetch(`https://dns.google/resolve?name=${host}`); + const json = await response.json(); + const { Answer } = json; + if (!Array.isArray(Answer)) { + console.error("Answer is not an array:", Answer); + continue; + } + const ips = Answer.map((answer: any) => answer.data); + const ttl = + Number(response.headers.get("Cache-Control").split("=")[1]) || 0; + if (dnsResponseIndex !== -1) { + store.state.dnsResponses.splice(dnsResponseIndex, 1); + } + store.state.dnsResponses.push({ + host, + ips, + timestamp: Date.now(), + ttl, + } as DnsResponse); + } catch (error) { + console.error(error); + } + } + + console.log("πŸ” DNS responses updated:"); + console.log(store.state.dnsResponses); +} diff --git a/src/common/ts/updateProxySettings.ts b/src/common/ts/updateProxySettings.ts index b8cde82e..1f1bd3b2 100644 --- a/src/common/ts/updateProxySettings.ts +++ b/src/common/ts/updateProxySettings.ts @@ -7,6 +7,7 @@ import { usherHostRegex, videoWeaverHostRegex, } from "./regexes"; +import updateDnsResponses from "./updateDnsResponses"; export default function updateProxySettings() { const { proxyTwitchWebpage, proxyUsherRequests } = store.state; @@ -43,6 +44,7 @@ export default function updateProxySettings() { console.log( `βš™οΈ Proxying requests through one of: ${proxies.toString() || ""}` ); + updateDnsResponses(); }); } diff --git a/src/store/getDefaultState.ts b/src/store/getDefaultState.ts index 05339e5e..828c60a0 100644 --- a/src/store/getDefaultState.ts +++ b/src/store/getDefaultState.ts @@ -6,6 +6,7 @@ export default function getDefaultState() { adLog: [], adLogEnabled: true, adLogLastSent: 0, + dnsResponses: [], normalProxies: isChromium ? ["chrome.api.cdn-perfprod.com:4023"] : [], openedTwitchTabs: [], optimizedProxies: isChromium ? [] : ["firefox.api.cdn-perfprod.com:2023"], diff --git a/src/store/types.ts b/src/store/types.ts index 2d624cff..8062c83f 100644 --- a/src/store/types.ts +++ b/src/store/types.ts @@ -1,4 +1,4 @@ -import type { AdLogEntry, StreamStatus } from "../types"; +import type { AdLogEntry, DnsResponse, StreamStatus } from "../types"; export type EventType = "load" | "change"; export type ReadyState = "loading" | "complete"; @@ -8,6 +8,7 @@ export interface State { adLog: AdLogEntry[]; adLogEnabled: boolean; adLogLastSent: number; + dnsResponses: DnsResponse[]; normalProxies: string[]; openedTwitchTabs: number[]; optimizedProxies: string[]; diff --git a/src/types.ts b/src/types.ts index 0e62dce2..b2d3a7dd 100644 --- a/src/types.ts +++ b/src/types.ts @@ -43,3 +43,10 @@ export interface StreamStatus { notProxied: number; }; } + +export interface DnsResponse { + host: string; + ips: string[]; + timestamp: number; + ttl: number; +} From b43ee45aa61a244a3e8391a286a209cdd0ecc86a Mon Sep 17 00:00:00 2001 From: Younes Aassila <47226184+younesaassila@users.noreply.github.com> Date: Sat, 17 Jun 2023 15:50:30 +0200 Subject: [PATCH 08/19] =?UTF-8?q?=F0=9F=8E=A8=20Code=20improvements?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/background.ts | 3 ++- src/background/handlers/onProxyRequest.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/background/background.ts b/src/background/background.ts index 8dca5a77..41b8dc6b 100644 --- a/src/background/background.ts +++ b/src/background/background.ts @@ -15,7 +15,7 @@ import onTabUpdated from "./handlers/onTabUpdated"; console.info("πŸš€ Background script loaded."); // Cleanup the session-related data in the store on startup. -browser.runtime.onStartup.addListener(onStartupStoreCleanup); +onStartupStoreCleanup(); // Handle proxy authentication. browser.webRequest.onAuthRequired.addListener( @@ -32,6 +32,7 @@ browser.webRequest.onResponseStarted.addListener(onResponseStarted, { if (isChromium) { // Check if there are any opened Twitch tabs on startup. checkForOpenedTwitchTabs(); + // Keep track of opened Twitch tabs to enable/disable the PAC script. browser.tabs.onCreated.addListener(onTabCreated); browser.tabs.onUpdated.addListener(onTabUpdated); diff --git a/src/background/handlers/onProxyRequest.ts b/src/background/handlers/onProxyRequest.ts index 812caecb..a73bf9f2 100644 --- a/src/background/handlers/onProxyRequest.ts +++ b/src/background/handlers/onProxyRequest.ts @@ -108,7 +108,7 @@ export default async function onProxyRequest( function getProxyInfoArrayFromUrls(urls: string[]): ProxyInfo[] { return [ - ...urls.map(url => getProxyInfoFromUrl(url)), + ...urls.map(getProxyInfoFromUrl), { type: "direct" } as ProxyInfo, // Fallback to direct connection if all proxies fail. ]; } From ad02f23ddfec667f0d40472d75fb6d5b31cf6392 Mon Sep 17 00:00:00 2001 From: Younes Aassila <47226184+younesaassila@users.noreply.github.com> Date: Sat, 17 Jun 2023 16:46:14 +0200 Subject: [PATCH 09/19] =?UTF-8?q?=E2=9C=A8=20Add=20#170?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/background.ts | 12 +--- .../handlers/onBeforeUsherRequest.ts | 61 ------------------- src/background/handlers/onResponseStarted.ts | 17 +----- src/common/ts/streamStatus.ts | 29 +++++++++ src/content/content.ts | 18 ++++++ src/manifest.chromium.json | 7 +++ src/page/getFetch.ts | 48 ++++++++++++--- src/page/page.ts | 7 ++- src/page/worker.ts | 2 +- src/popup/popup.ts | 3 + 10 files changed, 108 insertions(+), 96 deletions(-) delete mode 100644 src/background/handlers/onBeforeUsherRequest.ts create mode 100644 src/common/ts/streamStatus.ts diff --git a/src/background/background.ts b/src/background/background.ts index 41b8dc6b..1c401e84 100644 --- a/src/background/background.ts +++ b/src/background/background.ts @@ -3,7 +3,6 @@ import isChromium from "../common/ts/isChromium"; import checkForOpenedTwitchTabs from "./handlers/checkForOpenedTwitchTabs"; import onAuthRequired from "./handlers/onAuthRequired"; import onBeforeSendHeaders from "./handlers/onBeforeSendHeaders"; -import onBeforeUsherRequest from "./handlers/onBeforeUsherRequest"; import onBeforeVideoWeaverRequest from "./handlers/onBeforeVideoWeaverRequest"; import onProxyRequest from "./handlers/onProxyRequest"; import onResponseStarted from "./handlers/onResponseStarted"; @@ -15,7 +14,7 @@ import onTabUpdated from "./handlers/onTabUpdated"; console.info("πŸš€ Background script loaded."); // Cleanup the session-related data in the store on startup. -onStartupStoreCleanup(); +onStartupStoreCleanup(); // FIXME: Might be cleared every time background script is reloaded. // Handle proxy authentication. browser.webRequest.onAuthRequired.addListener( @@ -45,15 +44,6 @@ if (isChromium) { ["blocking"] ); - // 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, diff --git a/src/background/handlers/onBeforeUsherRequest.ts b/src/background/handlers/onBeforeUsherRequest.ts deleted file mode 100644 index ecd6c804..00000000 --- a/src/background/handlers/onBeforeUsherRequest.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { WebRequest } from "webextension-polyfill"; -import filterResponseDataWrapper from "../../common/ts/filterResponseDataWrapper"; -import { - twitchApiChannelNameRegex, - videoWeaverUrlRegex, -} from "../../common/ts/regexes"; -import store from "../../store"; -import type { StreamStatus } from "../../types"; - -export default function onBeforeUsherRequest( - details: WebRequest.OnBeforeRequestDetailsType -): void | WebRequest.BlockingResponseOrPromise { - const match = twitchApiChannelNameRegex.exec(details.url); - if (!match) return; - const channelName = match[1]?.toLowerCase(); - if (!channelName) return; - - filterResponseDataWrapper(details, text => { - const videoWeaverUrls = text.match(videoWeaverUrlRegex); - if (!videoWeaverUrls) return text; - console.log( - `πŸ“Ί Found ${videoWeaverUrls.length} video-weaver URLs for ${channelName}.` - ); - const existingVideoWeaverUrls = - store.state.videoWeaverUrlsByChannel[channelName] ?? []; - const newVideoWeaverUrls = videoWeaverUrls.filter( - url => !existingVideoWeaverUrls.includes(url) - ); - store.state.videoWeaverUrlsByChannel[channelName] = [ - ...existingVideoWeaverUrls, - ...newVideoWeaverUrls, - ]; - const streamStatus = getStreamStatus(channelName); - setStreamStatus(channelName, { - ...(streamStatus ?? { proxied: false, reason: "" }), - proxyCountry: extractProxyCountryFromManifest(text), - }); - return text; - }); -} - -function getStreamStatus(channelName: string | null): StreamStatus | null { - if (!channelName) return null; - return store.state.streamStatuses[channelName] ?? null; -} - -function setStreamStatus( - channelName: string | null, - streamStatus: StreamStatus -): boolean { - if (!channelName) return false; - store.state.streamStatuses[channelName] = streamStatus; - return true; -} - -function extractProxyCountryFromManifest(text: string): string | undefined { - const match = /USER-COUNTRY="([A-Z]+)"/i.exec(text); - if (!match) return; - const [, proxyCountry] = match; - return proxyCountry; -} diff --git a/src/background/handlers/onResponseStarted.ts b/src/background/handlers/onResponseStarted.ts index 03d2bd93..df121a76 100644 --- a/src/background/handlers/onResponseStarted.ts +++ b/src/background/handlers/onResponseStarted.ts @@ -10,8 +10,9 @@ import { usherHostRegex, videoWeaverHostRegex, } from "../../common/ts/regexes"; +import { getStreamStatus, setStreamStatus } from "../../common/ts/streamStatus"; import store from "../../store"; -import type { ProxyInfo, StreamStatus } from "../../types"; +import type { ProxyInfo } from "../../types"; export default function onResponseStarted( details: WebRequest.OnResponseStartedDetailsType & { @@ -107,17 +108,3 @@ function getProxyFromDetails( return `${proxyInfo.host}:${proxyInfo.port}`; } } - -function getStreamStatus(channelName: string | null): StreamStatus | null { - if (!channelName) return null; - return store.state.streamStatuses[channelName] ?? null; -} - -function setStreamStatus( - channelName: string | null, - streamStatus: StreamStatus -): boolean { - if (!channelName) return false; - store.state.streamStatuses[channelName] = streamStatus; - return true; -} diff --git a/src/common/ts/streamStatus.ts b/src/common/ts/streamStatus.ts new file mode 100644 index 00000000..b13d7727 --- /dev/null +++ b/src/common/ts/streamStatus.ts @@ -0,0 +1,29 @@ +import store from "../../store"; +import type { StreamStatus } from "../../types"; + +/** + * Safely get the stream status for a channel. + * @param channelName + * @returns + */ +export function getStreamStatus( + channelName: string | null +): StreamStatus | null { + if (!channelName) return null; + return store.state.streamStatuses[channelName] ?? null; +} + +/** + * Safely set the stream status for a channel. + * @param channelName + * @param streamStatus + * @returns + */ +export function setStreamStatus( + channelName: string | null, + streamStatus: StreamStatus +): boolean { + if (!channelName) return false; + store.state.streamStatuses[channelName] = streamStatus; + return true; +} diff --git a/src/content/content.ts b/src/content/content.ts index 1aaf13ed..39490917 100644 --- a/src/content/content.ts +++ b/src/content/content.ts @@ -1,6 +1,7 @@ import pageScript from "url:../page/page.ts"; import workerScript from "url:../page/worker.ts"; import { twitchChannelNameRegex } from "../common/ts/regexes"; +import { getStreamStatus, setStreamStatus } from "../common/ts/streamStatus"; import store from "../store"; console.info("[TTV LOL PRO] πŸš€ Content script running."); @@ -45,3 +46,20 @@ function clearStats() { }; } } + +window.addEventListener("message", onMessage); + +function onMessage(event: MessageEvent) { + if (event.source !== window) return; + if (event.data?.type === "UsherResponse") { + const { channel, videoWeaverUrls, proxyCountry } = event.data; + // Update Video Weaver URLs. + store.state.videoWeaverUrlsByChannel[channel] = videoWeaverUrls; + // Update proxy country. + const streamStatus = getStreamStatus(channel); + setStreamStatus(channel, { + ...(streamStatus ?? { proxied: false, reason: "" }), + proxyCountry, + }); + } +} diff --git a/src/manifest.chromium.json b/src/manifest.chromium.json index 53ee37a7..b2fefd25 100644 --- a/src/manifest.chromium.json +++ b/src/manifest.chromium.json @@ -23,6 +23,13 @@ "default_title": "TTV LOL PRO", "default_popup": "popup/menu.html" }, + "content_scripts": [ + { + "matches": ["https://www.twitch.tv/*", "https://m.twitch.tv/*"], + "js": ["content/content.ts"], + "run_at": "document_start" + } + ], "icons": { "128": "images/brand/icon.png" }, diff --git a/src/page/getFetch.ts b/src/page/getFetch.ts index 40be9157..e0691770 100644 --- a/src/page/getFetch.ts +++ b/src/page/getFetch.ts @@ -1,16 +1,21 @@ import acceptFlag from "../common/ts/acceptFlag"; +import findChannelFromUsherUrl from "../common/ts/findChannelFromUsherUrl"; import getHostFromUrl from "../common/ts/getHostFromUrl"; import { twitchGqlHostRegex, usherHostRegex, videoWeaverHostRegex, + videoWeaverUrlRegex, } from "../common/ts/regexes"; const NATIVE_FETCH = self.fetch; +const IS_CHROMIUM = !!self.chrome; -export interface FetchOptions {} +export interface FetchOptions { + scope: "page" | "worker"; +} -export function getFetch(options: FetchOptions = {}): typeof fetch { +export function getFetch(options: FetchOptions): typeof fetch { const knownVideoWeaverUrls = new Set(); const videoWeaverUrlsToFlag = new Map(); // Video Weaver URLs to flag -> number of times flagged. const videoWeaverUrlsToIgnore = new Set(); // No response check. @@ -121,12 +126,24 @@ export function getFetch(options: FetchOptions = {}): typeof fetch { if (host != null && usherHostRegex.test(host)) { await readResponseBody(); console.debug("[TTV LOL PRO] πŸ₯… Caught Usher response."); + const videoWeaverUrls = responseBody + .split("\n") + .filter(line => videoWeaverUrlRegex.test(line)); + // Send Video Weaver URLs to content script. + sendMessageToContentScript( + options.scope, + JSON.parse( + JSON.stringify({ + type: "UsherResponse", + channel: findChannelFromUsherUrl(url), + videoWeaverUrls, + proxyCountry: + /USER-COUNTRY="([A-Z]+)"/i.exec(responseBody)?.[1] ?? null, + }) + ) + ); // Remove all Video Weaver URLs from known URLs. - responseBody.split("\n").forEach(line => { - if (line.includes("video-weaver.")) { - knownVideoWeaverUrls.delete(line.trim()); - } - }); + videoWeaverUrls.forEach(url => knownVideoWeaverUrls.delete(url)); } // Video Weaver responses. @@ -252,7 +269,24 @@ function removeHeaderFromMap(headersMap: Map, name: string) { } } +function sendMessageToContentScript(scope: "page" | "worker", message: any) { + if (scope === "page") { + self.postMessage(message); + } else { + self.postMessage({ + type: "ContentScriptMessage", + message, + }); + } +} + function flagRequest(headersMap: Map) { + if (IS_CHROMIUM) { + console.debug( + "[TTV LOL PRO] 🚩 Request flagging is not supported on Chromium. Ignoring…" + ); + return; + } const accept = getHeaderFromMap(headersMap, "Accept"); setHeaderToMap(headersMap, "Accept", `${accept || ""}${acceptFlag}`); } diff --git a/src/page/page.ts b/src/page/page.ts index 4d886386..24003bc9 100644 --- a/src/page/page.ts +++ b/src/page/page.ts @@ -4,7 +4,7 @@ console.info("[TTV LOL PRO] πŸš€ Page script running."); const params = JSON.parse(document.currentScript.dataset.params); -window.fetch = getFetch(); +window.fetch = getFetch({ scope: "page" }); // Inject custom worker script to intercept fetch requests made from workers and // decide whether to proxy them or not. @@ -37,6 +37,11 @@ window.Worker = class Worker extends window.Worker { new Blob([newScript], { type: "text/javascript" }) ); super(newScriptURL, options); + this.addEventListener("message", event => { + if (event.data?.type === "ContentScriptMessage") { + window.postMessage(event.data.message); + } + }); } }; diff --git a/src/page/worker.ts b/src/page/worker.ts index 82d3a4da..ceab7053 100644 --- a/src/page/worker.ts +++ b/src/page/worker.ts @@ -2,4 +2,4 @@ import { getFetch } from "./getFetch"; console.info("[TTV LOL PRO] πŸš€ Worker script running."); -self.fetch = getFetch(); +self.fetch = getFetch({ scope: "worker" }); diff --git a/src/popup/popup.ts b/src/popup/popup.ts index 55f7db21..20cf91b6 100644 --- a/src/popup/popup.ts +++ b/src/popup/popup.ts @@ -67,6 +67,9 @@ function setStreamStatusElement(channelName: string) { setProxyStatus(channelNameLower, status); setWhitelistStatus(channelNameLower); streamStatusElement.style.display = "flex"; + if (isChromium) { + whitelistStatusElement.style.display = "none"; + } } else { streamStatusElement.style.display = "none"; } From 2db7fbdb0059c6ccc9c7dab17bc92bb5d7a7342c Mon Sep 17 00:00:00 2001 From: Younes Aassila <47226184+younesaassila@users.noreply.github.com> Date: Sat, 17 Jun 2023 16:53:57 +0200 Subject: [PATCH 10/19] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Bump=20version=20num?= =?UTF-8?q?ber=20to=202.1.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 4 ++-- package.json | 2 +- src/manifest.chromium.json | 2 +- src/manifest.firefox.json | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1ac5edb7..cffddc8e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ttv-lol-pro", - "version": "2.0.3", + "version": "2.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ttv-lol-pro", - "version": "2.0.3", + "version": "2.1.0", "license": "GPL-3.0", "dependencies": { "bowser": "^2.11.0", diff --git a/package.json b/package.json index c7edb431..3c25768f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ttv-lol-pro", - "version": "2.0.3", + "version": "2.1.0", "description": "TTV LOL PRO removes most livestream ads from Twitch.", "@parcel/bundler-default": { "minBundles": 10000000, diff --git a/src/manifest.chromium.json b/src/manifest.chromium.json index b2fefd25..a520cd0d 100644 --- a/src/manifest.chromium.json +++ b/src/manifest.chromium.json @@ -2,7 +2,7 @@ "manifest_version": 3, "name": "TTV LOL PRO", "description": "TTV LOL PRO removes most livestream ads from Twitch.", - "version": "2.0.3", + "version": "2.1.0", "background": { "service_worker": "background/background.ts", "type": "module" diff --git a/src/manifest.firefox.json b/src/manifest.firefox.json index 572cc2b0..7e7db52a 100644 --- a/src/manifest.firefox.json +++ b/src/manifest.firefox.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "TTV LOL PRO", "description": "TTV LOL PRO removes most livestream ads from Twitch.", - "version": "2.0.3", + "version": "2.1.0", "background": { "scripts": ["background/background.ts"], "persistent": false From ef8f76e577e4a8f74546199cb2b589c154a40c73 Mon Sep 17 00:00:00 2001 From: Younes Aassila <47226184+younesaassila@users.noreply.github.com> Date: Sat, 17 Jun 2023 17:10:59 +0200 Subject: [PATCH 11/19] =?UTF-8?q?=F0=9F=8E=A8=20Minor=20code=20improvement?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/background.ts | 2 +- src/background/handlers/checkForOpenedTwitchTabs.ts | 8 ++++++-- src/popup/popup.ts | 1 + 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/background/background.ts b/src/background/background.ts index 1c401e84..abcde524 100644 --- a/src/background/background.ts +++ b/src/background/background.ts @@ -14,7 +14,7 @@ import onTabUpdated from "./handlers/onTabUpdated"; console.info("πŸš€ Background script loaded."); // Cleanup the session-related data in the store on startup. -onStartupStoreCleanup(); // FIXME: Might be cleared every time background script is reloaded. +browser.runtime.onStartup.addListener(onStartupStoreCleanup); // Handle proxy authentication. browser.webRequest.onAuthRequired.addListener( diff --git a/src/background/handlers/checkForOpenedTwitchTabs.ts b/src/background/handlers/checkForOpenedTwitchTabs.ts index 14c9740d..46a49d79 100644 --- a/src/background/handlers/checkForOpenedTwitchTabs.ts +++ b/src/background/handlers/checkForOpenedTwitchTabs.ts @@ -1,4 +1,5 @@ import browser from "webextension-polyfill"; +import clearProxySettings from "../../common/ts/clearProxySettings"; import isChromium from "../../common/ts/isChromium"; import updateProxySettings from "../../common/ts/updateProxySettings"; import store from "../../store"; @@ -10,7 +11,10 @@ export default function checkForOpenedTwitchTabs() { browser.tabs .query({ url: ["https://www.twitch.tv/*", "https://m.twitch.tv/*"] }) .then(tabs => { - if (tabs.length === 0) return; + if (tabs.length === 0) { + if (isChromium) clearProxySettings(); + return; + } console.log( `πŸ” Found ${tabs.length} opened Twitch tabs: ${tabs .map(tab => tab.id) @@ -19,6 +23,6 @@ export default function checkForOpenedTwitchTabs() { if (isChromium) { updateProxySettings(); } - tabs.forEach(tab => store.state.openedTwitchTabs.push(tab.id)); + store.state.openedTwitchTabs = tabs.map(tab => tab.id); }); } diff --git a/src/popup/popup.ts b/src/popup/popup.ts index 20cf91b6..4930262a 100644 --- a/src/popup/popup.ts +++ b/src/popup/popup.ts @@ -83,6 +83,7 @@ function setProxyStatus(channelNameLower: string, status: StreamStatus) { proxiedElement.classList.add("success"); } else if ( !status.proxied && + status.proxyHost && store.state.optimizedProxiesEnabled && store.state.optimizedProxies.length > 0 ) { From 65ff8ea5204a0ec685c7557f9872b6d0fe5a3658 Mon Sep 17 00:00:00 2001 From: Younes Aassila <47226184+younesaassila@users.noreply.github.com> Date: Sat, 17 Jun 2023 17:33:43 +0200 Subject: [PATCH 12/19] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../handlers/checkForOpenedTwitchTabs.ts | 6 ++- .../handlers/onBeforeVideoWeaverRequest.ts | 2 +- src/background/handlers/onProxyRequest.ts | 6 ++- src/background/handlers/onResponseStarted.ts | 2 +- src/background/handlers/onTabCreated.ts | 2 +- src/background/handlers/onTabRemoved.ts | 2 +- src/background/handlers/onTabUpdated.ts | 6 ++- src/common/ts/clearProxySettings.ts | 5 --- src/common/ts/file.ts | 37 +++++++++++++++++++ src/common/ts/findChannel.ts | 29 +++++++++++++++ src/common/ts/findChannelFromUsherUrl.ts | 10 ----- .../ts/findChannelFromVideoWeaverUrl.ts | 9 ----- ...pdateProxySettings.ts => proxySettings.ts} | 8 +++- src/common/ts/readFile.ts | 15 -------- src/common/ts/saveFile.ts | 10 ----- src/options/options.ts | 5 +-- src/page/getFetch.ts | 2 +- 17 files changed, 92 insertions(+), 64 deletions(-) delete mode 100644 src/common/ts/clearProxySettings.ts create mode 100644 src/common/ts/file.ts create mode 100644 src/common/ts/findChannel.ts delete mode 100644 src/common/ts/findChannelFromUsherUrl.ts delete mode 100644 src/common/ts/findChannelFromVideoWeaverUrl.ts rename src/common/ts/{updateProxySettings.ts => proxySettings.ts} (89%) delete mode 100644 src/common/ts/readFile.ts delete mode 100644 src/common/ts/saveFile.ts diff --git a/src/background/handlers/checkForOpenedTwitchTabs.ts b/src/background/handlers/checkForOpenedTwitchTabs.ts index 46a49d79..ee899578 100644 --- a/src/background/handlers/checkForOpenedTwitchTabs.ts +++ b/src/background/handlers/checkForOpenedTwitchTabs.ts @@ -1,7 +1,9 @@ import browser from "webextension-polyfill"; -import clearProxySettings from "../../common/ts/clearProxySettings"; import isChromium from "../../common/ts/isChromium"; -import updateProxySettings from "../../common/ts/updateProxySettings"; +import { + clearProxySettings, + updateProxySettings, +} from "../../common/ts/proxySettings"; import store from "../../store"; export default function checkForOpenedTwitchTabs() { diff --git a/src/background/handlers/onBeforeVideoWeaverRequest.ts b/src/background/handlers/onBeforeVideoWeaverRequest.ts index 9768f893..9080b9cf 100644 --- a/src/background/handlers/onBeforeVideoWeaverRequest.ts +++ b/src/background/handlers/onBeforeVideoWeaverRequest.ts @@ -1,6 +1,6 @@ import { WebRequest } from "webextension-polyfill"; import filterResponseDataWrapper from "../../common/ts/filterResponseDataWrapper"; -import findChannelFromVideoWeaverUrl from "../../common/ts/findChannelFromVideoWeaverUrl"; +import { findChannelFromVideoWeaverUrl } from "../../common/ts/findChannel"; import getHostFromUrl from "../../common/ts/getHostFromUrl"; import { videoWeaverHostRegex } from "../../common/ts/regexes"; import store from "../../store"; diff --git a/src/background/handlers/onProxyRequest.ts b/src/background/handlers/onProxyRequest.ts index a73bf9f2..33d57998 100644 --- a/src/background/handlers/onProxyRequest.ts +++ b/src/background/handlers/onProxyRequest.ts @@ -1,6 +1,8 @@ import { Proxy } from "webextension-polyfill"; -import findChannelFromUsherUrl from "../../common/ts/findChannelFromUsherUrl"; -import findChannelFromVideoWeaverUrl from "../../common/ts/findChannelFromVideoWeaverUrl"; +import { + findChannelFromUsherUrl, + findChannelFromVideoWeaverUrl, +} from "../../common/ts/findChannel"; import getHostFromUrl from "../../common/ts/getHostFromUrl"; import getProxyInfoFromUrl from "../../common/ts/getProxyInfoFromUrl"; import isChannelWhitelisted from "../../common/ts/isChannelWhitelisted"; diff --git a/src/background/handlers/onResponseStarted.ts b/src/background/handlers/onResponseStarted.ts index df121a76..16437307 100644 --- a/src/background/handlers/onResponseStarted.ts +++ b/src/background/handlers/onResponseStarted.ts @@ -1,5 +1,5 @@ import { WebRequest } from "webextension-polyfill"; -import findChannelFromVideoWeaverUrl from "../../common/ts/findChannelFromVideoWeaverUrl"; +import { findChannelFromVideoWeaverUrl } from "../../common/ts/findChannel"; import getHostFromUrl from "../../common/ts/getHostFromUrl"; import getProxyInfoFromUrl from "../../common/ts/getProxyInfoFromUrl"; import isChromium from "../../common/ts/isChromium"; diff --git a/src/background/handlers/onTabCreated.ts b/src/background/handlers/onTabCreated.ts index c7b00616..8fdb39b7 100644 --- a/src/background/handlers/onTabCreated.ts +++ b/src/background/handlers/onTabCreated.ts @@ -1,8 +1,8 @@ import { Tabs } from "webextension-polyfill"; import getHostFromUrl from "../../common/ts/getHostFromUrl"; import isChromium from "../../common/ts/isChromium"; +import { updateProxySettings } from "../../common/ts/proxySettings"; import { twitchTvHostRegex } from "../../common/ts/regexes"; -import updateProxySettings from "../../common/ts/updateProxySettings"; import store from "../../store"; export default function onTabCreated(tab: Tabs.Tab): void { diff --git a/src/background/handlers/onTabRemoved.ts b/src/background/handlers/onTabRemoved.ts index 6d644507..26c81d66 100644 --- a/src/background/handlers/onTabRemoved.ts +++ b/src/background/handlers/onTabRemoved.ts @@ -1,5 +1,5 @@ -import clearProxySettings from "../../common/ts/clearProxySettings"; import isChromium from "../../common/ts/isChromium"; +import { clearProxySettings } from "../../common/ts/proxySettings"; import store from "../../store"; export default function onTabRemoved(tabId: number): void { diff --git a/src/background/handlers/onTabUpdated.ts b/src/background/handlers/onTabUpdated.ts index 0a0f1e79..26527f00 100644 --- a/src/background/handlers/onTabUpdated.ts +++ b/src/background/handlers/onTabUpdated.ts @@ -1,9 +1,11 @@ import { Tabs } from "webextension-polyfill"; -import clearProxySettings from "../../common/ts/clearProxySettings"; import getHostFromUrl from "../../common/ts/getHostFromUrl"; import isChromium from "../../common/ts/isChromium"; +import { + clearProxySettings, + updateProxySettings, +} from "../../common/ts/proxySettings"; import { twitchTvHostRegex } from "../../common/ts/regexes"; -import updateProxySettings from "../../common/ts/updateProxySettings"; import store from "../../store"; export default function onTabUpdated( diff --git a/src/common/ts/clearProxySettings.ts b/src/common/ts/clearProxySettings.ts deleted file mode 100644 index 3d6293e5..00000000 --- a/src/common/ts/clearProxySettings.ts +++ /dev/null @@ -1,5 +0,0 @@ -export default function clearProxySettings() { - chrome.proxy.settings.clear({ scope: "regular" }, function () { - console.log("βš™οΈ Proxy settings cleared"); - }); -} diff --git a/src/common/ts/file.ts b/src/common/ts/file.ts new file mode 100644 index 00000000..64a556ca --- /dev/null +++ b/src/common/ts/file.ts @@ -0,0 +1,37 @@ +/** + * Read a file from the user's computer. + * @param accept + * @returns + */ +export async function readFile(accept = "text/plain;charset=utf-8") { + return new Promise((resolve, reject) => { + const input = document.createElement("input"); + input.type = "file"; + input.accept = accept; + input.addEventListener("change", async e => { + const input = e.target as HTMLInputElement; + const file = input.files?.[0]; + if (!file) return reject("No file selected"); + const data = await file.text(); + return resolve(data); + }); + input.click(); + }); +} + +/** + * Save a file to the user's computer. + * @param filename + * @param content + * @param type + */ +export function saveFile( + filename: string, + content: string, + type = "text/plain;charset=utf-8" +) { + const a = document.createElement("a"); + a.setAttribute("href", `data:${type},` + encodeURIComponent(content)); + a.setAttribute("download", filename); + a.click(); +} diff --git a/src/common/ts/findChannel.ts b/src/common/ts/findChannel.ts new file mode 100644 index 00000000..caa114fd --- /dev/null +++ b/src/common/ts/findChannel.ts @@ -0,0 +1,29 @@ +import store from "../../store"; +import { twitchApiChannelNameRegex } from "./regexes"; + +/** + * Returns the channel name from a Twitch Usher URL. + * Returns `null` if the URL is not a valid Usher URL. + * @param usherUrl + * @returns + */ +export function findChannelFromUsherUrl(usherUrl: string): string | null { + const match = twitchApiChannelNameRegex.exec(usherUrl); + if (!match) return null; + const [, channelName] = match; + return channelName; +} + +/** + * Returns the channel name from a Video Weaver URL. + * Returns `null` if the URL is not a valid Video Weaver URL. + * @param videoWeaverUrl + * @returns + */ +export function findChannelFromVideoWeaverUrl(videoWeaverUrl: string) { + const channelName = Object.keys(store.state.videoWeaverUrlsByChannel).find( + channelName => + store.state.videoWeaverUrlsByChannel[channelName].includes(videoWeaverUrl) + ); + return channelName ?? null; +} diff --git a/src/common/ts/findChannelFromUsherUrl.ts b/src/common/ts/findChannelFromUsherUrl.ts deleted file mode 100644 index 4fb94621..00000000 --- a/src/common/ts/findChannelFromUsherUrl.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { twitchApiChannelNameRegex } from "./regexes"; - -export default function findChannelFromUsherUrl( - usherUrl: string -): string | null { - const match = twitchApiChannelNameRegex.exec(usherUrl); - if (!match) return null; - const [, channelName] = match; - return channelName; -} diff --git a/src/common/ts/findChannelFromVideoWeaverUrl.ts b/src/common/ts/findChannelFromVideoWeaverUrl.ts deleted file mode 100644 index 0cfcfdf7..00000000 --- a/src/common/ts/findChannelFromVideoWeaverUrl.ts +++ /dev/null @@ -1,9 +0,0 @@ -import store from "../../store"; - -export default function findChannelFromVideoWeaverUrl(videoWeaverUrl: string) { - const channelName = Object.keys(store.state.videoWeaverUrlsByChannel).find( - channelName => - store.state.videoWeaverUrlsByChannel[channelName].includes(videoWeaverUrl) - ); - return channelName ?? null; -} diff --git a/src/common/ts/updateProxySettings.ts b/src/common/ts/proxySettings.ts similarity index 89% rename from src/common/ts/updateProxySettings.ts rename to src/common/ts/proxySettings.ts index 1f1bd3b2..e6fa360f 100644 --- a/src/common/ts/updateProxySettings.ts +++ b/src/common/ts/proxySettings.ts @@ -9,7 +9,7 @@ import { } from "./regexes"; import updateDnsResponses from "./updateDnsResponses"; -export default function updateProxySettings() { +export function updateProxySettings() { const { proxyTwitchWebpage, proxyUsherRequests } = store.state; const proxies = store.state.optimizedProxiesEnabled @@ -57,3 +57,9 @@ function getProxyInfoStringFromUrls(urls: string[]): string { "DIRECT", ].join("; "); } + +export function clearProxySettings() { + chrome.proxy.settings.clear({ scope: "regular" }, function () { + console.log("βš™οΈ Proxy settings cleared"); + }); +} diff --git a/src/common/ts/readFile.ts b/src/common/ts/readFile.ts deleted file mode 100644 index 754aa14c..00000000 --- a/src/common/ts/readFile.ts +++ /dev/null @@ -1,15 +0,0 @@ -export default async function readFile(accept = "text/plain;charset=utf-8") { - return new Promise((resolve, reject) => { - const input = document.createElement("input"); - input.type = "file"; - input.accept = accept; - input.addEventListener("change", async e => { - const input = e.target as HTMLInputElement; - const file = input.files?.[0]; - if (!file) return reject("No file selected"); - const data = await file.text(); - return resolve(data); - }); - input.click(); - }); -} diff --git a/src/common/ts/saveFile.ts b/src/common/ts/saveFile.ts deleted file mode 100644 index 0188a2fd..00000000 --- a/src/common/ts/saveFile.ts +++ /dev/null @@ -1,10 +0,0 @@ -export default function saveFile( - filename: string, - content: string, - type = "text/plain;charset=utf-8" -) { - const a = document.createElement("a"); - a.setAttribute("href", `data:${type},` + encodeURIComponent(content)); - a.setAttribute("download", filename); - a.click(); -} diff --git a/src/options/options.ts b/src/options/options.ts index a245c6e3..0c69891e 100644 --- a/src/options/options.ts +++ b/src/options/options.ts @@ -1,10 +1,9 @@ import $ from "../common/ts/$"; +import { readFile, saveFile } from "../common/ts/file"; import getProxyInfoFromUrl from "../common/ts/getProxyInfoFromUrl"; import isChromium from "../common/ts/isChromium"; -import readFile from "../common/ts/readFile"; -import saveFile from "../common/ts/saveFile"; +import { updateProxySettings } from "../common/ts/proxySettings"; import sendAdLog from "../common/ts/sendAdLog"; -import updateProxySettings from "../common/ts/updateProxySettings"; import store from "../store"; import getDefaultState from "../store/getDefaultState"; import type { State } from "../store/types"; diff --git a/src/page/getFetch.ts b/src/page/getFetch.ts index e0691770..ab2a512e 100644 --- a/src/page/getFetch.ts +++ b/src/page/getFetch.ts @@ -1,5 +1,5 @@ import acceptFlag from "../common/ts/acceptFlag"; -import findChannelFromUsherUrl from "../common/ts/findChannelFromUsherUrl"; +import { findChannelFromUsherUrl } from "../common/ts/findChannel"; import getHostFromUrl from "../common/ts/getHostFromUrl"; import { twitchGqlHostRegex, From 87951c9c7dac20070d8608feb4c0e9e6df570727 Mon Sep 17 00:00:00 2001 From: Younes Aassila <47226184+younesaassila@users.noreply.github.com> Date: Sat, 17 Jun 2023 17:34:06 +0200 Subject: [PATCH 13/19] =?UTF-8?q?=F0=9F=94=A7=20Add=20`--noEmit`=20to=20ts?= =?UTF-8?q?config?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tsconfig.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index 788ccd22..9b779d80 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,6 +2,7 @@ "compilerOptions": { "target": "ES2020", "moduleResolution": "node", - "allowSyntheticDefaultImports": true + "allowSyntheticDefaultImports": true, + "noEmit": true } } From 808ce32de9c242e383a5e31223c481eefcee76ed Mon Sep 17 00:00:00 2001 From: Younes Aassila <47226184+younesaassila@users.noreply.github.com> Date: Sat, 17 Jun 2023 21:36:31 +0200 Subject: [PATCH 14/19] =?UTF-8?q?=F0=9F=90=9B=20Fix=20fetch=20error?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../handlers/onBeforeVideoWeaverRequest.ts | 2 +- src/background/handlers/onProxyRequest.ts | 6 ++-- src/background/handlers/onResponseStarted.ts | 2 +- src/common/ts/findChannel.ts | 29 ------------------- src/common/ts/findChannelFromUsherUrl.ts | 16 ++++++++++ .../ts/findChannelFromVideoWeaverUrl.ts | 15 ++++++++++ src/page/getFetch.ts | 21 +++++--------- 7 files changed, 43 insertions(+), 48 deletions(-) delete mode 100644 src/common/ts/findChannel.ts create mode 100644 src/common/ts/findChannelFromUsherUrl.ts create mode 100644 src/common/ts/findChannelFromVideoWeaverUrl.ts diff --git a/src/background/handlers/onBeforeVideoWeaverRequest.ts b/src/background/handlers/onBeforeVideoWeaverRequest.ts index 9080b9cf..9768f893 100644 --- a/src/background/handlers/onBeforeVideoWeaverRequest.ts +++ b/src/background/handlers/onBeforeVideoWeaverRequest.ts @@ -1,6 +1,6 @@ import { WebRequest } from "webextension-polyfill"; import filterResponseDataWrapper from "../../common/ts/filterResponseDataWrapper"; -import { findChannelFromVideoWeaverUrl } from "../../common/ts/findChannel"; +import findChannelFromVideoWeaverUrl from "../../common/ts/findChannelFromVideoWeaverUrl"; import getHostFromUrl from "../../common/ts/getHostFromUrl"; import { videoWeaverHostRegex } from "../../common/ts/regexes"; import store from "../../store"; diff --git a/src/background/handlers/onProxyRequest.ts b/src/background/handlers/onProxyRequest.ts index 33d57998..a73bf9f2 100644 --- a/src/background/handlers/onProxyRequest.ts +++ b/src/background/handlers/onProxyRequest.ts @@ -1,8 +1,6 @@ import { Proxy } from "webextension-polyfill"; -import { - findChannelFromUsherUrl, - findChannelFromVideoWeaverUrl, -} from "../../common/ts/findChannel"; +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"; diff --git a/src/background/handlers/onResponseStarted.ts b/src/background/handlers/onResponseStarted.ts index 16437307..df121a76 100644 --- a/src/background/handlers/onResponseStarted.ts +++ b/src/background/handlers/onResponseStarted.ts @@ -1,5 +1,5 @@ import { WebRequest } from "webextension-polyfill"; -import { findChannelFromVideoWeaverUrl } from "../../common/ts/findChannel"; +import findChannelFromVideoWeaverUrl from "../../common/ts/findChannelFromVideoWeaverUrl"; import getHostFromUrl from "../../common/ts/getHostFromUrl"; import getProxyInfoFromUrl from "../../common/ts/getProxyInfoFromUrl"; import isChromium from "../../common/ts/isChromium"; diff --git a/src/common/ts/findChannel.ts b/src/common/ts/findChannel.ts deleted file mode 100644 index caa114fd..00000000 --- a/src/common/ts/findChannel.ts +++ /dev/null @@ -1,29 +0,0 @@ -import store from "../../store"; -import { twitchApiChannelNameRegex } from "./regexes"; - -/** - * Returns the channel name from a Twitch Usher URL. - * Returns `null` if the URL is not a valid Usher URL. - * @param usherUrl - * @returns - */ -export function findChannelFromUsherUrl(usherUrl: string): string | null { - const match = twitchApiChannelNameRegex.exec(usherUrl); - if (!match) return null; - const [, channelName] = match; - return channelName; -} - -/** - * Returns the channel name from a Video Weaver URL. - * Returns `null` if the URL is not a valid Video Weaver URL. - * @param videoWeaverUrl - * @returns - */ -export function findChannelFromVideoWeaverUrl(videoWeaverUrl: string) { - const channelName = Object.keys(store.state.videoWeaverUrlsByChannel).find( - channelName => - store.state.videoWeaverUrlsByChannel[channelName].includes(videoWeaverUrl) - ); - return channelName ?? null; -} diff --git a/src/common/ts/findChannelFromUsherUrl.ts b/src/common/ts/findChannelFromUsherUrl.ts new file mode 100644 index 00000000..096d35da --- /dev/null +++ b/src/common/ts/findChannelFromUsherUrl.ts @@ -0,0 +1,16 @@ +import { twitchApiChannelNameRegex } from "./regexes"; + +/** + * Returns the channel name from a Twitch Usher URL. + * Returns `null` if the URL is not a valid Usher URL. + * @param usherUrl + * @returns + */ +export default function findChannelFromUsherUrl( + usherUrl: string +): string | null { + const match = twitchApiChannelNameRegex.exec(usherUrl); + if (!match) return null; + const [, channelName] = match; + return channelName; +} diff --git a/src/common/ts/findChannelFromVideoWeaverUrl.ts b/src/common/ts/findChannelFromVideoWeaverUrl.ts new file mode 100644 index 00000000..53eb2f2f --- /dev/null +++ b/src/common/ts/findChannelFromVideoWeaverUrl.ts @@ -0,0 +1,15 @@ +import store from "../../store"; + +/** + * Returns the channel name from a Video Weaver URL. + * Returns `null` if the URL is not a valid Video Weaver URL. + * @param videoWeaverUrl + * @returns + */ +export default function findChannelFromVideoWeaverUrl(videoWeaverUrl: string) { + const channelName = Object.keys(store.state.videoWeaverUrlsByChannel).find( + channelName => + store.state.videoWeaverUrlsByChannel[channelName].includes(videoWeaverUrl) + ); + return channelName ?? null; +} diff --git a/src/page/getFetch.ts b/src/page/getFetch.ts index ab2a512e..600e506c 100644 --- a/src/page/getFetch.ts +++ b/src/page/getFetch.ts @@ -1,5 +1,5 @@ import acceptFlag from "../common/ts/acceptFlag"; -import { findChannelFromUsherUrl } from "../common/ts/findChannel"; +import findChannelFromUsherUrl from "../common/ts/findChannelFromUsherUrl"; import getHostFromUrl from "../common/ts/getHostFromUrl"; import { twitchGqlHostRegex, @@ -130,18 +130,13 @@ export function getFetch(options: FetchOptions): typeof fetch { .split("\n") .filter(line => videoWeaverUrlRegex.test(line)); // Send Video Weaver URLs to content script. - sendMessageToContentScript( - options.scope, - JSON.parse( - JSON.stringify({ - type: "UsherResponse", - channel: findChannelFromUsherUrl(url), - videoWeaverUrls, - proxyCountry: - /USER-COUNTRY="([A-Z]+)"/i.exec(responseBody)?.[1] ?? null, - }) - ) - ); + sendMessageToContentScript(options.scope, { + type: "UsherResponse", + channel: findChannelFromUsherUrl(url), + videoWeaverUrls, + proxyCountry: + /USER-COUNTRY="([A-Z]+)"/i.exec(responseBody)?.[1] || null, + }); // Remove all Video Weaver URLs from known URLs. videoWeaverUrls.forEach(url => knownVideoWeaverUrls.delete(url)); } From 2d59686712c579cff4cbae474c6ef68a2cb8cfec Mon Sep 17 00:00:00 2001 From: Younes Aassila <47226184+younesaassila@users.noreply.github.com> Date: Mon, 19 Jun 2023 13:47:44 +0200 Subject: [PATCH 15/19] =?UTF-8?q?=F0=9F=8E=A8=20Improve=20clarity=20of=20c?= =?UTF-8?q?ode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/content/content.ts | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/src/content/content.ts b/src/content/content.ts index 39490917..5e74e29d 100644 --- a/src/content/content.ts +++ b/src/content/content.ts @@ -6,33 +6,34 @@ import store from "../store"; console.info("[TTV LOL PRO] πŸš€ Content script running."); -injectScript(pageScript); +injectPageScript(); -function injectScript(src: string) { +if (store.readyState === "complete") clearStats(); +else store.addEventListener("load", clearStats); + +window.addEventListener("message", onMessage); + +function injectPageScript() { // From https://stackoverflow.com/a/9517879 const script = document.createElement("script"); - script.src = src; + script.src = pageScript; script.dataset.params = JSON.stringify({ workerScriptURL: workerScript, }); script.onload = () => script.remove(); - // ------------------------------------------ - // 🦊🦊🦊 DEAR FIREFOX ADDON REVIEWER 🦊🦊🦊 - // ------------------------------------------ - // This is NOT remote code execution. The script being injected is - // bundled with the extension (look at the `url:` imports above provided by - // the Parcel bundler). By the way, no custom CSP is used. + // --------------------------------------- + // 🦊 Attention Firefox Addon Reviewer 🦊 + // --------------------------------------- + // Please note that this does NOT involve remote code execution. The injected script is bundled + // with the extension. The `url:` imports above are used to load the respective scripts by the Parcel bundler. + // Additionally, there is no custom Content Security Policy (CSP) in use. (document.head || document.documentElement).append(script); // Note: Despite what the TS types say, `document.head` can be `null`. } -if (store.readyState === "complete") onStoreReady(); -else store.addEventListener("load", onStoreReady); - -function onStoreReady() { - // Clear stats for stream on page load/reload. - clearStats(); -} - +/** + * Clear stats for stream on page load/reload. + * @returns + */ function clearStats() { const match = twitchChannelNameRegex.exec(location.href); if (!match) return; @@ -47,8 +48,6 @@ function clearStats() { } } -window.addEventListener("message", onMessage); - function onMessage(event: MessageEvent) { if (event.source !== window) return; if (event.data?.type === "UsherResponse") { From b1204d5ac640b3eebe4f86984b760b3bfc5705af Mon Sep 17 00:00:00 2001 From: Younes Aassila <47226184+younesaassila@users.noreply.github.com> Date: Mon, 19 Jun 2023 13:49:27 +0200 Subject: [PATCH 16/19] =?UTF-8?q?=F0=9F=92=A1=20Update=20comments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/content/content.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/content/content.ts b/src/content/content.ts index 5e74e29d..2547bcd9 100644 --- a/src/content/content.ts +++ b/src/content/content.ts @@ -16,9 +16,9 @@ window.addEventListener("message", onMessage); function injectPageScript() { // From https://stackoverflow.com/a/9517879 const script = document.createElement("script"); - script.src = pageScript; + script.src = pageScript; // src/page/page.ts script.dataset.params = JSON.stringify({ - workerScriptURL: workerScript, + workerScriptURL: workerScript, // src/page/worker.ts }); script.onload = () => script.remove(); // --------------------------------------- From c0171f352eb47f6ace6d9c0726f82efe4c4ea2d2 Mon Sep 17 00:00:00 2001 From: Younes Aassila <47226184+younesaassila@users.noreply.github.com> Date: Mon, 19 Jun 2023 13:51:52 +0200 Subject: [PATCH 17/19] =?UTF-8?q?=F0=9F=8E=A8=20Improve=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/content/content.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/content/content.ts b/src/content/content.ts index 2547bcd9..a7ffa359 100644 --- a/src/content/content.ts +++ b/src/content/content.ts @@ -1,5 +1,5 @@ -import pageScript from "url:../page/page.ts"; -import workerScript from "url:../page/worker.ts"; +import pageScriptURL from "url:../page/page.ts"; +import workerScriptURL from "url:../page/worker.ts"; import { twitchChannelNameRegex } from "../common/ts/regexes"; import { getStreamStatus, setStreamStatus } from "../common/ts/streamStatus"; import store from "../store"; @@ -16,9 +16,9 @@ window.addEventListener("message", onMessage); function injectPageScript() { // From https://stackoverflow.com/a/9517879 const script = document.createElement("script"); - script.src = pageScript; // src/page/page.ts + script.src = pageScriptURL; // (src/page/page.ts) script.dataset.params = JSON.stringify({ - workerScriptURL: workerScript, // src/page/worker.ts + workerScriptURL: workerScriptURL, // (src/page/worker.ts) }); script.onload = () => script.remove(); // --------------------------------------- From 25ae3104ec165fb3ff513c9ffbcb74c06a6de102 Mon Sep 17 00:00:00 2001 From: Younes Aassila <47226184+younesaassila@users.noreply.github.com> Date: Mon, 19 Jun 2023 13:56:10 +0200 Subject: [PATCH 18/19] =?UTF-8?q?=F0=9F=92=A1=20Update=20comment?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/content/content.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/content/content.ts b/src/content/content.ts index a7ffa359..ee03b4dc 100644 --- a/src/content/content.ts +++ b/src/content/content.ts @@ -24,8 +24,8 @@ function injectPageScript() { // --------------------------------------- // 🦊 Attention Firefox Addon Reviewer 🦊 // --------------------------------------- - // Please note that this does NOT involve remote code execution. The injected script is bundled - // with the extension. The `url:` imports above are used to load the respective scripts by the Parcel bundler. + // Please note that this does NOT involve remote code execution. The injected scripts are bundled + // with the extension. The `url:` imports above are used to get the runtime URLs of the respective scripts. // Additionally, there is no custom Content Security Policy (CSP) in use. (document.head || document.documentElement).append(script); // Note: Despite what the TS types say, `document.head` can be `null`. } From 6b9e3ba0c43158825e2dd6ef77de1c9bb1323989 Mon Sep 17 00:00:00 2001 From: Younes Aassila <47226184+younesaassila@users.noreply.github.com> Date: Mon, 19 Jun 2023 14:02:56 +0200 Subject: [PATCH 19/19] =?UTF-8?q?=F0=9F=92=A1=20Improve=20comments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/content/content.ts | 4 ++-- src/page/page.ts | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/content/content.ts b/src/content/content.ts index ee03b4dc..26233598 100644 --- a/src/content/content.ts +++ b/src/content/content.ts @@ -16,9 +16,9 @@ window.addEventListener("message", onMessage); function injectPageScript() { // From https://stackoverflow.com/a/9517879 const script = document.createElement("script"); - script.src = pageScriptURL; // (src/page/page.ts) + script.src = pageScriptURL; // src/page/page.ts script.dataset.params = JSON.stringify({ - workerScriptURL: workerScriptURL, // (src/page/worker.ts) + workerScriptURL: workerScriptURL, // src/page/worker.ts }); script.onload = () => script.remove(); // --------------------------------------- diff --git a/src/page/page.ts b/src/page/page.ts index 24003bc9..d400409d 100644 --- a/src/page/page.ts +++ b/src/page/page.ts @@ -6,8 +6,6 @@ const params = JSON.parse(document.currentScript.dataset.params); window.fetch = getFetch({ scope: "page" }); -// Inject custom worker script to intercept fetch requests made from workers and -// decide whether to proxy them or not. window.Worker = class Worker extends window.Worker { constructor(scriptURL: string | URL, options?: WorkerOptions) { const url = scriptURL.toString(); @@ -25,6 +23,11 @@ window.Worker = class Worker extends window.Worker { ); script = `importScripts("${url}");`; // Will fail on Firefox Nightly. } + // --------------------------------------- + // 🦊 Attention Firefox Addon Reviewer 🦊 + // --------------------------------------- + // Please note that this does NOT involve remote code execution. The injected script is bundled + // with the extension. Additionally, there is no custom Content Security Policy (CSP) in use. const newScript = ` try { importScripts("${params.workerScriptURL}");