Skip to content

Commit

Permalink
Add WIP implementation of #177
Browse files Browse the repository at this point in the history
  • Loading branch information
younesaassila committed Jan 31, 2025
1 parent aaf1961 commit 3536898
Show file tree
Hide file tree
Showing 8 changed files with 206 additions and 55 deletions.
12 changes: 12 additions & 0 deletions src/common/ts/wasChannelSubscriber.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import store from "../../store";

export default function wasChannelSubscriber(
channelName: string | null
): boolean {
if (!channelName) return false;
const activeChannelSubscriptionsLower =
store.state.activeChannelSubscriptions.map(channel =>
channel.toLowerCase()
);
return activeChannelSubscriptionsLower.includes(channelName.toLowerCase());
}
167 changes: 113 additions & 54 deletions src/content/content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import pageScriptURL from "url:../page/page.ts";
import workerScriptURL from "url:../page/worker.ts";
import browser, { Storage } from "webextension-polyfill";
import findChannelFromTwitchTvUrl from "../common/ts/findChannelFromTwitchTvUrl";
import isChannelWhitelisted from "../common/ts/isChannelWhitelisted";
import isChromium from "../common/ts/isChromium";
import { getStreamStatus, setStreamStatus } from "../common/ts/streamStatus";
import wasChannelSubscriber from "../common/ts/wasChannelSubscriber";
import store from "../store";
import type { State } from "../store/types";
import { MessageType } from "../types";
Expand Down Expand Up @@ -64,6 +66,7 @@ function onStoreChange(changes: Record<string, Storage.StorageChange>) {
// This is mainly to reduce the amount of messages sent to the page script.
// (Also to reduce the number of console logs.)
const ignoredKeys: (keyof State)[] = [
"activeChannelSubscriptions",
"adLog",
"dnsResponses",
"openedTwitchTabs",
Expand Down Expand Up @@ -102,61 +105,117 @@ function onPageMessage(event: MessageEvent) {
const message = event.data?.message;
if (!message) return;

switch (message.type) {
case MessageType.GetStoreState:
const sendStoreState = () => {
window.postMessage({
type: MessageType.PageScriptMessage,
message: {
type: MessageType.GetStoreStateResponse,
state: JSON.parse(JSON.stringify(store.state)),
},
});
};
if (store.readyState === "complete") sendStoreState();
else store.addEventListener("load", sendStoreState);
break;
case MessageType.EnableFullMode:
try {
browser.runtime.sendMessage(message);
} catch (error) {
console.error(
"[TTV LOL PRO] Failed to send EnableFullMode message",
error
);
}
break;
case MessageType.DisableFullMode:
try {
browser.runtime.sendMessage(message);
} catch (error) {
console.error(
"[TTV LOL PRO] Failed to send DisableFullMode message",
error
);
// GetStoreState
if (message.type === MessageType.GetStoreState) {
const sendStoreState = () => {
window.postMessage({
type: MessageType.PageScriptMessage,
message: {
type: MessageType.GetStoreStateResponse,
state: JSON.parse(JSON.stringify(store.state)),
},
});
};
if (store.readyState === "complete") sendStoreState();
else store.addEventListener("load", sendStoreState);
}
// EnableFullMode
else if (message.type === MessageType.EnableFullMode) {
try {
browser.runtime.sendMessage(message);
} catch (error) {
console.error(
"[TTV LOL PRO] Failed to send EnableFullMode message",
error
);
}
}
// DisableFullMode
else if (message.type === MessageType.DisableFullMode) {
try {
browser.runtime.sendMessage(message);
} catch (error) {
console.error(
"[TTV LOL PRO] Failed to send DisableFullMode message",
error
);
}
}
// ChannelSubscriptionStatus
else if (message.type === MessageType.ChannelSubscriptionStatus) {
const { channelName, isSubscribed, scope } = message;
const wasSubscribed = wasChannelSubscriber(channelName);
let isWhitelisted = isChannelWhitelisted(channelName);
console.log(
"[TTV LOL PRO] Received channel subscription status message. Current state:",
{
wasSubscribed,
isSubscribed,
isWhitelisted,
}
break;
case MessageType.UsherResponse:
try {
browser.runtime.sendMessage(message);
} catch (error) {
console.error(
"[TTV LOL PRO] Failed to send UsherResponse message",
error
);
);
if (store.state.whitelistChannelSubscriptions && channelName != null) {
if (!wasSubscribed && isSubscribed) {
store.state.activeChannelSubscriptions.push(channelName);
// Add to whitelist.
if (!isWhitelisted) {
console.log(`[TTV LOL PRO] Adding '${channelName}' to whitelist.`);
store.state.whitelistedChannels.push(channelName);
isWhitelisted = true;
}
} else if (wasSubscribed && !isSubscribed) {
store.state.activeChannelSubscriptions =
store.state.activeChannelSubscriptions.filter(
c => c.toLowerCase() !== channelName.toLowerCase()
);
// Remove from whitelist.
if (isWhitelisted) {
console.log(
`[TTV LOL PRO] Removing '${channelName}' from whitelist.`
);
store.state.whitelistedChannels =
store.state.whitelistedChannels.filter(
c => c.toLowerCase() !== channelName.toLowerCase()
);
isWhitelisted = false;
}
}
break;
case MessageType.MultipleAdBlockersInUse:
const channelName = findChannelFromTwitchTvUrl(location.href);
if (!channelName) break;
const streamStatus = getStreamStatus(channelName);
setStreamStatus(channelName, {
...(streamStatus ?? { proxied: false }),
reason: "Another Twitch ad blocker is in use",
});
break;
case MessageType.ClearStats:
clearStats(message.channelName);
break;
}
console.log("[TTV LOL PRO] Sending channel subscription status response.");
window.postMessage({
type:
scope === "page" // TODO: Is this necessary? Isn't the scope always "worker"?
? MessageType.PageScriptMessage
: MessageType.WorkerScriptMessage,
message: {
type: MessageType.ChannelSubscriptionStatusResponse,
isWhitelisted: isWhitelisted,
},
});
}
// UsherResponse
else if (message.type === MessageType.UsherResponse) {
try {
browser.runtime.sendMessage(message);
} catch (error) {
console.error(
"[TTV LOL PRO] Failed to send UsherResponse message",
error
);
}
}
// MultipleAdBlockersInUse
else if (message.type === MessageType.MultipleAdBlockersInUse) {
const channelName = findChannelFromTwitchTvUrl(location.href);
if (!channelName) return;
const streamStatus = getStreamStatus(channelName);
setStreamStatus(channelName, {
...(streamStatus ?? { proxied: false }),
reason: "Another Twitch ad blocker is in use",
});
}
// ClearStats
else if (message.type === MessageType.ClearStats) {
clearStats(message.channelName);
}
}
10 changes: 10 additions & 0 deletions src/options/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ const passportLevelProxyUsageWwwElement = $(
const whitelistedChannelsListElement = $(
"#whitelisted-channels-list"
) as HTMLUListElement;
const whitelistSubscriptionsCheckboxElement = $(
"#whitelist-subscriptions-checkbox"
) as HTMLInputElement;
// Proxies
const optimizedProxiesInputElement = $("#optimized") as HTMLInputElement;
const optimizedProxiesListElement = $(
Expand Down Expand Up @@ -163,6 +166,12 @@ function main() {
return [true];
},
});
whitelistSubscriptionsCheckboxElement.checked =
store.state.whitelistChannelSubscriptions;
whitelistSubscriptionsCheckboxElement.addEventListener("change", () => {
store.state.whitelistChannelSubscriptions =
whitelistSubscriptionsCheckboxElement.checked;
});
// Proxies
if (store.state.optimizedProxiesEnabled)
optimizedProxiesInputElement.checked = true;
Expand Down Expand Up @@ -548,6 +557,7 @@ exportButtonElement.addEventListener("click", () => {
optimizedProxies: store.state.optimizedProxies,
optimizedProxiesEnabled: store.state.optimizedProxiesEnabled,
passportLevel: store.state.passportLevel,
whitelistChannelSubscriptions: store.state.whitelistChannelSubscriptions,
whitelistedChannels: store.state.whitelistedChannels,
};
saveFile(
Expand Down
17 changes: 17 additions & 0 deletions src/options/page.html
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,23 @@ <h2>Whitelisted channels</h2>
Twitch tabs are whitelisted channels.
</small>
<ul id="whitelisted-channels-list" class="store-list"></ul>
<ul class="options-list">
<li>
<input
type="checkbox"
name="whitelist-subscriptions-checkbox"
id="whitelist-subscriptions-checkbox"
/>
<label for="whitelist-subscriptions-checkbox">
Automatically whitelist channels you're subscribed to
</label>
<br />
<small>
This option will automatically add or remove channels from the
whitelist based on your subscriptions.
</small>
</li>
</ul>
</section>

<!-- Proxies -->
Expand Down
49 changes: 48 additions & 1 deletion src/page/getFetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,42 @@ export function getFetch(pageState: PageState): typeof fetch {
encodeURIComponent('"player_type":"frontpage"')
);
const channelName = findChannelFromUsherUrl(url);
const isWhitelisted = isChannelWhitelisted(channelName, pageState);
let isWhitelisted = isChannelWhitelisted(channelName, pageState);
if (
pageState.state?.whitelistChannelSubscriptions &&
channelName != null
) {
const wasSubscribed = wasChannelSubscriber(channelName, pageState);
const isSubscribed = url.includes(
encodeURIComponent('"subscriber":true')
);
// const isSubscribed = url.includes(
// encodeURIComponent("aminematue")
// );
const hasSubStatusChanged =
(wasSubscribed && !isSubscribed) || (!wasSubscribed && isSubscribed);
if (hasSubStatusChanged) {
console.log(
"[TTV LOL PRO] Channel subscription status changed. Sending message…"
);
try {
const response =
await pageState.sendMessageToContentScriptAndWaitForResponse(
pageState.scope,
{
type: MessageType.ChannelSubscriptionStatus,
scope: pageState.scope,
channelName,
isSubscribed,
},
MessageType.ChannelSubscriptionStatusResponse
);
if (typeof response.isWhitelisted === "boolean") {
isWhitelisted = response.isWhitelisted;
}
} catch {}
}
}
if (!isLivestream || isFrontpage || isWhitelisted) {
console.log(
"[TTV LOL PRO] Not flagging Usher request: not a livestream, is frontpage, or is whitelisted."
Expand Down Expand Up @@ -623,6 +658,18 @@ function isChannelWhitelisted(
return whitelistedChannelsLower.includes(channelName.toLowerCase());
}

function wasChannelSubscriber(
channelName: string | null | undefined,
pageState: PageState
): boolean {
if (!channelName) return false;
const activeChannelSubscriptionsLower =
pageState.state?.activeChannelSubscriptions.map(channel =>
channel.toLowerCase()
) ?? [];
return activeChannelSubscriptionsLower.includes(channelName.toLowerCase());
}

async function flagRequest(
request: Request,
requestType: ProxyRequestType,
Expand Down
2 changes: 2 additions & 0 deletions src/store/getDefaultState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { State } from "./types";

export default function getDefaultState() {
const state: State = {
activeChannelSubscriptions: [],
adLog: [],
adLogEnabled: true,
adLogLastSent: 0,
Expand All @@ -18,6 +19,7 @@ export default function getDefaultState() {
passportLevel: 0,
streamStatuses: {},
videoWeaverUrlsByChannel: {},
whitelistChannelSubscriptions: true,
whitelistedChannels: [],
};
return state;
Expand Down
2 changes: 2 additions & 0 deletions src/store/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export type ReadyState = "loading" | "complete";
export type StorageAreaName = "local" | "managed" | "sync";

export interface State {
activeChannelSubscriptions: string[];
adLog: AdLogEntry[];
adLogEnabled: boolean;
adLogLastSent: number;
Expand All @@ -19,6 +20,7 @@ export interface State {
passportLevel: number;
streamStatuses: Record<string, StreamStatus>;
videoWeaverUrlsByChannel: Record<string, string[]>;
whitelistChannelSubscriptions: boolean;
whitelistedChannels: string[];
}

Expand Down
2 changes: 2 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ export const enum MessageType {
EnableFullMode = "TLP_EnableFullMode",
EnableFullModeResponse = "TLP_EnableFullModeResponse",
DisableFullMode = "TLP_DisableFullMode",
ChannelSubscriptionStatus = "TLP_ChannelSubscriptionStatus",
ChannelSubscriptionStatusResponse = "TLP_ChannelSubscriptionStatusResponse",
UsherResponse = "TLP_UsherResponse",
NewPlaybackAccessToken = "TLP_NewPlaybackAccessToken",
NewPlaybackAccessTokenResponse = "TLP_NewPlaybackAccessTokenResponse",
Expand Down

0 comments on commit 3536898

Please sign in to comment.