Skip to content

Commit

Permalink
Merge remote-tracking branch 'hc/mv3'
Browse files Browse the repository at this point in the history
  • Loading branch information
KentoNishi committed Dec 16, 2024
2 parents 8e6c127 + 9f2563a commit af6a505
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 11 deletions.
71 changes: 71 additions & 0 deletions src/components/ChatSummary.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<script lang="ts">
import { slide, fade } from 'svelte/transition';
import MessageRun from './MessageRuns.svelte';
import Tooltip from './common/Tooltip.svelte';
import Icon from 'smelte/src/components/Icon';
import { Theme } from '../ts/chat-constants';
import { createEventDispatcher } from 'svelte';
export let summary: Ytc.ParsedSummary;
let dismissed = false;
let shorten = false;
const classes = 'rounded inline-flex flex-col overflow-visible ' +
'bg-secondary-900 p-2 w-full text-white z-10 shadow';
const onShorten = () => { shorten = !shorten; };
$: if (summary) {
dismissed = false;
shorten = false;
}
const dispatch = createEventDispatcher();
$: dismissed, shorten, dispatch('resize');
</script>

{#if !dismissed}
<div
class={classes}
transition:fade={{ duration: 250 }}
>
<div class="flex flex-row items-center cursor-pointer" on:click={onShorten}>
<div class="font-medium tracking-wide text-white flex-1">
<span class="mr-1 inline-block" style="transform: translateY(3px);">
<Icon small>
{#if shorten}
expand_more
{:else}
expand_less
{/if}
</Icon>
</span>
{#each summary.item.header as run}
{#if run.type === 'text'}
<span class="align-middle">{run.text}</span>
{/if}
{/each}
</div>
<div class="flex-none self-end" style="transform: translateY(3px);">
<Tooltip offsetY={0} small>
<Icon
slot="activator"
class="cursor-pointer text-lg"
on:click={() => { dismissed = true; }}
>
close
</Icon>
Dismiss
</Tooltip>
</div>
</div>
{#if !shorten && !dismissed}
<div class="mt-1 whitespace-pre-line" transition:slide|local={{ duration: 300 }}>
<MessageRun runs={summary.item.subheader} deleted forceDark forceTLColor={Theme.DARK}/>
</div>
<div class="mt-1 whitespace-pre-line" transition:slide|local={{ duration: 300 }}>
<MessageRun runs={summary.item.message} forceDark forceTLColor={Theme.DARK}/>
</div>
{/if}
</div>
{/if}
6 changes: 6 additions & 0 deletions src/components/Hyperchat.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import WelcomeMessage from './YtcFilterWelcome.svelte';
import Message from './Message.svelte';
// import PinnedMessage from './PinnedMessage.svelte';
// import ChatSummary from './ChatSummary.svelte';
import PaidMessage from './PaidMessage.svelte';
import MembershipItem from './MembershipItem.svelte';
import ReportBanDialog from './ReportBanDialog.svelte';
Expand Down Expand Up @@ -44,6 +45,7 @@
showUsernames,
showTimestamps,
showUserBadges,
// showChatSummary,
refreshScroll,
emojiRenderMode,
useSystemEmojis,
Expand Down Expand Up @@ -99,6 +101,7 @@
let overrideActions: (Chat.MessageAction | Welcome)[] = [];
const messageKeys = new Set<string>();
let pinned: Ytc.ParsedPinned | null;
let summary: Ytc.ParsedSummary | null;
let div: HTMLElement;
let isAtBottom = true;
// let truncateInterval: number;
Expand Down Expand Up @@ -281,6 +284,9 @@
setOverride();
}
break;
case 'summary':
summary = action;
break;
case 'pin':
pinned = action;
newMessages({
Expand Down
4 changes: 2 additions & 2 deletions src/components/changelog/Changelog.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
<ul class="list-disc list-inside">
<strong>Serving in our latest update:</strong>
<li class="ml-3.5">
Fixed broken chat rules dialog
YouTube AI chat summary integration
</li>
</ul>
<ul class="list-disc list-inside">
<strong>What's still cooking at KFP:</strong>
<li class="ml-3.5">
YouTube AI chat summary integrations
Live redirect (raid) notifications
</li>
</ul>
4 changes: 3 additions & 1 deletion src/components/settings/InterfaceSettings.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
useSystemEmojis,
isDark,
enableStickySuperchatBar,
enableHighlightedMentions
enableHighlightedMentions,
showChatSummary
} from '../../ts/storage';
import { themeItems, emojiRenderItems } from '../../ts/chat-constants';
import Card from '../common/Card.svelte';
Expand Down Expand Up @@ -59,6 +60,7 @@
<Checkbox name="Show timestamps" store={showTimestamps} />
<Checkbox name="Show usernames" store={showUsernames} />
<Checkbox name="Show user badges" store={showUserBadges} />
<Checkbox name="Show experimental chat summaries by YouTube" store={showChatSummary} />
<Checkbox name="Highlight mentions" store={enableHighlightedMentions} />
</Card>

Expand Down
58 changes: 53 additions & 5 deletions src/ts/chat-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,51 @@ export const parseMessageRuns = (runs?: Ytc.MessageRun[]): Ytc.ParsedRun[] => {
return parsedRuns;
};

// takes an array of runs, finds newline-only runs, and splits the array by them, up to maxSplit times
// final output will have maximum length of maxSplit + 1
// maxSplit = -1 will have no limit for splits
const splitRunsByNewline = (runs: Ytc.ParsedRun[], maxSplit: number = -1): Ytc.ParsedRun[][] =>
runs.reduce((acc: Ytc.ParsedRun[][], run: Ytc.ParsedRun) => {
if (run.type === 'text' && run.text === '\n' && (maxSplit == -1 || acc.length <= maxSplit)) {
acc.push([]);
} else {
acc[acc.length - 1].push(run);
}
return acc;
}, [[]]);

const parseChatSummary = (renderer: Ytc.AddChatItem, isEphemeral: boolean, bannerTimeoutMs: number): Ytc.ParsedSummary | undefined => {
if (!renderer.liveChatBannerChatSummaryRenderer) {
return;
}
const baseRenderer = renderer.liveChatBannerChatSummaryRenderer!;
const runs = parseMessageRuns(renderer.liveChatBannerChatSummaryRenderer?.chatSummary.runs);
const splitRuns = splitRunsByNewline(runs, 2);
if (splitRuns.length < 3) {
// YT probably changed the format, refuse to do anything to avoid breaking
return;
}
const subheader = splitRuns[1].map(run => {
if (run.type === 'text') {
// turn subheader into a link to YT's support page detailing the AI summary feature
return { type: 'link', text: run.text, url: 'https://support.google.com/youtube/thread/18138167?msgid=284199217' } as Ytc.ParsedLinkRun;
} else {
return run;
}
});
const item: Ytc.ParsedSummary = {
type: 'summary',
item: {
header: splitRuns[0],
subheader: subheader,
message: splitRuns[2],
},
id: baseRenderer.liveChatSummaryId,
showtime: isEphemeral ? (bannerTimeoutMs / 1000) : 0,
};
return item;
}

const parseAddChatItemAction = (action: Ytc.AddChatItemAction, isReplay = false, liveTimeoutOrReplayMs = 0): Ytc.ParsedMessage | undefined => {
const actionItem = action.item;
const renderer = actionItem.liveChatTextMessageRenderer ??
Expand Down Expand Up @@ -194,8 +239,11 @@ const parseMessageDeletedAction = (action: Ytc.MessageDeletedAction): Ytc.Parsed
};
};

const parsePinnedMessageAction = (action: Ytc.AddPinnedAction): Ytc.ParsedPinned | undefined => {
const parseBannerAction = (action: Ytc.AddPinnedAction): Ytc.ParsedPinned | Ytc.ParsedSummary | undefined => {
const baseRenderer = action.bannerRenderer.liveChatBannerRenderer;
if (baseRenderer.contents.liveChatBannerChatSummaryRenderer) {
return parseChatSummary(baseRenderer.contents, action.bannerProperties?.isEphemeral ?? false, action.bannerProperties?.bannerTimeoutMs ?? 0);
}
const parsedContents = parseAddChatItemAction(
{ item: baseRenderer.contents }, true
);
Expand Down Expand Up @@ -240,7 +288,7 @@ const processCommonAction = (
if (action.addChatItemAction) {
return parseAddChatItemAction(action.addChatItemAction, isReplay, liveTimeoutOrReplayMs);
} else if (action.addBannerToLiveChatCommand) {
return parsePinnedMessageAction(action.addBannerToLiveChatCommand);
return parseBannerAction(action.addBannerToLiveChatCommand);
} else if (action.removeBannerForLiveChatCommand) {
return { type: 'unpin' } as const;
} else if (action.addLiveChatTickerItemAction) {
Expand Down Expand Up @@ -297,9 +345,9 @@ export const parseChatResponse = (response: string, isReplay: boolean): Ytc.Pars
const miscArray: Ytc.ParsedMisc[] = [];

const liveTimeoutMs = 0;
// base.continuations[0].timedContinuationData?.timeoutMs ??
// base.continuations[0].invalidationContinuationData?.timeoutMs ?? 0;
// NOTE: this used to be a thing, but it seems to have been removed.
// base.continuations[0].timedContinuationData?.timeoutMs ??
// base.continuations[0].invalidationContinuationData?.timeoutMs ?? 0;
// NOTE: this used to be a thing, but it seems to have been removed.

actionsArray.forEach((action) => {
let parsedAction: Ytc.ParsedAction | undefined;
Expand Down
5 changes: 3 additions & 2 deletions src/ts/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,11 @@ export const translatorClient = readable(null as (null | IframeTranslatorClient)
});
export const refreshScroll = writable(false);
export const theme = stores.addSyncStore('ytcf.theme', Theme.YOUTUBE);
export const showProfileIcons = stores.addSyncStore('ytcf.messages.showProfileIcons', true);
export const showProfileIcons = stores.addSyncStore('ytcf.messages.showProfileIcons', false);
export const showUsernames = stores.addSyncStore('ytcf.messages.showUsernames', true);
export const showTimestamps = stores.addSyncStore('ytcf.messages.showTimestamps', false);
export const showUserBadges = stores.addSyncStore('ytcf.messages.showUserBadges', true);
export const showChatSummary = stores.addSyncStore('ytcf.messages.showChatSummary', true);
export const lastClosedVersion = stores.addSyncStore('ytcf.lastClosedVersion', '');
export const showOnlyMemberChat = stores.addSyncStore('ytcf.showOnlyMemberChat', false);
export const emojiRenderMode = stores.addSyncStore('ytcf.emojiRenderMode', YoutubeEmojiRenderMode.SHOW_ALL);
Expand Down Expand Up @@ -225,7 +226,7 @@ export const getPresetById = async (id: string): Promise<YtcF.FilterPreset | nul
return presets.find(preset => preset.id === id) ?? null;
};
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
export const bytesUsed = stores.addSyncStore('hc.bytes.used', 0);
export const bytesUsed = stores.addSyncStore('ytcf.bytes.used', 0);
export const filterInBackground = stores.addSyncStore('ytcf.startFilteringInBackground', true);
export const autoOpenFilterPanel = stores.addSyncStore('ytcf.autoOpenFilterPanel', false);
export const autoClear = stores.addSyncStore('ytcf.autoClear', {
Expand Down
28 changes: 27 additions & 1 deletion src/ts/typings/ytc.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ declare namespace Ytc {
};
};
};
bannerProperties?: {
isEphemeral: boolean;
bannerTimeoutMs: number;
}
}

interface AddTickerAction {
Expand Down Expand Up @@ -227,6 +231,15 @@ declare namespace Ytc {
};
}

interface ChatSummaryRenderer {
liveChatSummaryId: string;
chatSummary: RunsObj;
icon?: {
/** Unlocalized string */
iconType: string;
};
}

interface PlaceholderRenderer { // No idea what the purpose of this is
id: string;
timestampUsec: IntString;
Expand All @@ -248,6 +261,8 @@ declare namespace Ytc {
liveChatSponsorshipsGiftPurchaseAnnouncementRenderer?: MembershipGiftPurchaseRenderer;
/** Membership gift redemption */
liveChatSponsorshipsGiftRedemptionAnnouncementRenderer?: TextMessageRenderer;
/** AI Chat Summary */
liveChatBannerChatSummaryRenderer?: ChatSummaryRenderer;
/** ??? */
liveChatPlaceholderItemRenderer?: PlaceholderRenderer;
}
Expand Down Expand Up @@ -365,13 +380,24 @@ declare namespace Ytc {
};
}

interface ParsedSummary {
type: 'summary';
item: {
header: ParsedRun[];
subheader: ParsedRun[];
message: ParsedRun[];
};
id: string;
showtime: number;
}

interface ParsedTicker extends ParsedMessage {
type: 'ticker';
tickerDuration: number;
detailText?: string;
}

type ParsedMisc = ParsedPinned | { type: 'unpin'};
type ParsedMisc = ParsedPinned | ParsedSummary | { type: 'unpin' };

type ParsedTimedItem = ParsedMessage | ParsedTicker;

Expand Down

0 comments on commit af6a505

Please sign in to comment.