Skip to content

Commit

Permalink
Merge branch 'master' into mv3
Browse files Browse the repository at this point in the history
  • Loading branch information
KentoNishi committed Jan 3, 2025
2 parents 3cc60ba + a0a88c2 commit aba3511
Show file tree
Hide file tree
Showing 6 changed files with 222 additions and 15 deletions.
38 changes: 37 additions & 1 deletion src/components/Hyperchat.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import Message from './Message.svelte';
import PinnedMessage from './PinnedMessage.svelte';
import ChatSummary from './ChatSummary.svelte';
import RedirectBanner from './RedirectBanner.svelte';
import PaidMessage from './PaidMessage.svelte';
import MembershipItem from './MembershipItem.svelte';
import ReportBanDialog from './ReportBanDialog.svelte';
Expand Down Expand Up @@ -72,6 +73,33 @@
const messageKeys = new Set<string>();
let pinned: Ytc.ParsedPinned | null;
let summary: Ytc.ParsedSummary | null;
let redirect: Ytc.ParsedRedirect | null;
// = {
// type: 'redirect',
// item: {
// message: [
// {
// type: 'text',
// text: 'Don\'t miss out! People are going to watch something from someone',
// },
// ],
// profileIcon: {
// src: 'https://picsum.photos/32',
// alt: 'Redirect profile photo',
// },
// action: {
// url: 'https://example.com/',
// text: [
// {
// type: 'text',
// text: 'Go Now',
// },
// ],
// },
// },
// showtime: 5000,
// };
$: hasBanner = pinned || redirect || (summary && $showChatSummary);
let div: HTMLElement;
let isAtBottom = true;
let truncateInterval: number;
Expand Down Expand Up @@ -205,6 +233,9 @@
case 'summary':
summary = action;
break;
case 'redirect':
redirect = action;
break;
case 'pin':
pinned = action;
break;
Expand Down Expand Up @@ -432,13 +463,18 @@
</div>
{/each}
</div>
{#if (summary && $showChatSummary) || pinned}
{#if hasBanner}
<div class="absolute top-0 w-full" bind:this={topBar}>
{#if summary && $showChatSummary}
<div class="mx-1.5 mt-1.5">
<ChatSummary summary={summary} on:resize={topBarResized} />
</div>
{/if}
{#if redirect}
<div class="mx-1.5 mt-1.5">
<RedirectBanner redirect={redirect} on:resize={topBarResized} />
</div>
{/if}
{#if pinned}
<div class="mx-1.5 mt-1.5">
<PinnedMessage pinned={pinned} on:resize={topBarResized} />
Expand Down
8 changes: 7 additions & 1 deletion src/components/MessageRuns.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,13 @@
{#if deleted}
<span>{run.text}</span>
{:else}
<TranslatedMessage text={run.text} {forceTLColor} />
{#if run.styles?.includes('bold')}
<strong>
<TranslatedMessage text={run.text} {forceTLColor} />
</strong>
{:else}
<TranslatedMessage text={run.text} {forceTLColor} />
{/if}
{/if}
{:else if run.type === 'link'}
<a
Expand Down
88 changes: 88 additions & 0 deletions src/components/RedirectBanner.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<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';
import { showProfileIcons } from '../ts/storage';
import Button from 'smelte/src/components/Button';
export let redirect: Ytc.ParsedRedirect;
let dismissed = false;
let shorten = false;
let autoHideTimeout: NodeJS.Timeout | null = null;
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 (autoHideTimeout) {
clearTimeout(autoHideTimeout);
autoHideTimeout = null;
}
};
$: if (redirect) {
dismissed = false;
shorten = false;
if (redirect.showtime) {
autoHideTimeout = setTimeout(() => { shorten = true; }, redirect.showtime);
}
}
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>
<span class="align-middle">Live Redirect Notice</span>
</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 inline-flex flex-row gap-2 break-words w-full overflow-visible" transition:slide|local={{ duration: 300 }}>
{#if $showProfileIcons}
<img
class="h-5 w-5 inline align-middle rounded-full flex-none"
src={redirect.item.profileIcon.src}
alt={redirect.item.profileIcon.alt}
/>
{/if}
<MessageRun runs={redirect.item.message} forceDark forceTLColor={Theme.DARK}/>
</div>
<div class="mt-1 whitespace-pre-line flex justify-end" transition:slide|local={{ duration: 300 }}>
<Button href={redirect.item.action.url} target="_blank" small>
<MessageRun runs={redirect.item.action.text} forceDark forceTLColor={Theme.DARK} class="cursor-pointer" />
</Button>
</div>
{/if}
</div>
{/if}
34 changes: 33 additions & 1 deletion src/ts/chat-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const parseMessageRuns = (runs?: Ytc.MessageRun[]): Ytc.ParsedRun[] => {
} else if (run.text != null) {
parsedRuns.push({
type: 'text',
styles: (run.bold ? ['bold'] : []).concat(run.deemphasize ? ['deemphasize'] : []),
text: decodeURIComponent(escape(unescape(encodeURIComponent(
run.text
))))
Expand Down Expand Up @@ -107,6 +108,34 @@ const parseChatSummary = (renderer: Ytc.AddChatItem, showtime: number): Ytc.Pars
return item;
}

const parseRedirectBanner = (renderer: Ytc.AddChatItem, showtime: number): Ytc.ParsedRedirect | undefined => {
if (!renderer.liveChatBannerRedirectRenderer) {
return;
}
const baseRenderer = renderer.liveChatBannerRedirectRenderer!;
const profileIcon = {
src: fixUrl(baseRenderer.authorPhoto?.thumbnails[0].url ?? ''),
alt: 'Redirect profile icon'
};
const url = baseRenderer.inlineActionButton?.buttonRenderer.command.urlEndpoint?.url ||
(baseRenderer.inlineActionButton?.buttonRenderer.command.watchEndpoint?.videoId ?
"/watch?v=" + baseRenderer.inlineActionButton?.buttonRenderer.command.watchEndpoint?.videoId
: '');
const item: Ytc.ParsedRedirect = {
type: 'redirect',
item: {
message: parseMessageRuns(baseRenderer.bannerMessage.runs),
profileIcon: profileIcon,
action: {
url: fixUrl(url),
text: parseMessageRuns(baseRenderer.inlineActionButton?.buttonRenderer.text?.runs),
}
},
showtime: showtime,
};
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 @@ -229,7 +258,7 @@ const parseMessageDeletedAction = (action: Ytc.MessageDeletedAction): Ytc.Parsed
};
};

const parseBannerAction = (action: Ytc.AddPinnedAction): Ytc.ParsedPinned | Ytc.ParsedSummary | undefined => {
const parseBannerAction = (action: Ytc.AddPinnedAction): Ytc.ParsedMisc | undefined => {
const baseRenderer = action.bannerRenderer.liveChatBannerRenderer;

// fold both auto-disappear and auto-collapse into just collapse for showtime
Expand All @@ -240,6 +269,9 @@ const parseBannerAction = (action: Ytc.AddPinnedAction): Ytc.ParsedPinned | Ytc.
if (baseRenderer.contents.liveChatBannerChatSummaryRenderer) {
return parseChatSummary(baseRenderer.contents, showtime);
}
if (baseRenderer.contents.liveChatBannerRedirectRenderer) {
return parseRedirectBanner(baseRenderer.contents, showtime);
}
const parsedContents = parseAddChatItemAction(
{ item: baseRenderer.contents }, true
);
Expand Down
2 changes: 1 addition & 1 deletion src/ts/chat-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export const isValidFrameInfo = (f: Chat.UncheckedFrameInfo, port?: Chat.Port):
return check;
};

const actionTypes = new Set(['messages', 'bonk', 'delete', 'pin', 'unpin', 'summary', 'playerProgress', 'forceUpdate']);
const actionTypes = new Set(['messages', 'bonk', 'delete', 'pin', 'unpin', 'summary', 'redirect', 'playerProgress', 'forceUpdate']);
export const responseIsAction = (r: Chat.BackgroundResponse): r is Chat.Actions =>
actionTypes.has(r.type);

Expand Down
67 changes: 56 additions & 11 deletions src/ts/typings/ytc.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,16 +145,14 @@ declare namespace Ytc {
}

interface ThumbnailsWithLabel extends Thumbnails {
accessibility?: {
accessibilityData: {
label: string;
};
};
accessibility?: AccessibilityObj;
}

/** Message run object */
interface MessageRun {
text?: string;
bold?: boolean;
deemphasize?: boolean;
navigationEndpoint?: {
commandMetadata: {
webCommandMetadata: {
Expand Down Expand Up @@ -185,11 +183,7 @@ declare namespace Ytc {
/** Unlocalized string */
iconType: string;
};
accessibility?: {
accessibilityData: {
label: string;
};
};
accessibility?: AccessibilityObj;
};
}

Expand Down Expand Up @@ -248,6 +242,35 @@ declare namespace Ytc {
};
}

interface RedirectRenderer {
bannerMessage: RunsObj;
authorPhoto?: Thumbnails;
inlineActionButton?: {
buttonRenderer: ButtonRenderer;
}
contextMenuButton?: {
buttonRenderer: ButtonRenderer;
}
}

interface ButtonRenderer {
style?: string;
size?: string;
icon?: string;
accessibility?: AccessibilityObj;
isDisabled?: boolean;
text?: RunsObj;
command: {
urlEndpoint?: {
url: string;
target: string;
}
watchEndpoint?: {
videoId: string;
}
}
}

interface PlaceholderRenderer { // No idea what the purpose of this is
id: string;
timestampUsec: IntString;
Expand All @@ -271,6 +294,8 @@ declare namespace Ytc {
liveChatSponsorshipsGiftRedemptionAnnouncementRenderer?: TextMessageRenderer;
/** AI Chat Summary */
liveChatBannerChatSummaryRenderer?: ChatSummaryRenderer;
/** Redirects */
liveChatBannerRedirectRenderer?: RedirectRenderer;
/** ??? */
liveChatPlaceholderItemRenderer?: PlaceholderRenderer;
}
Expand Down Expand Up @@ -299,6 +324,12 @@ declare namespace Ytc {
runs: MessageRun[];
}

interface AccessibilityObj {
accessibilityData: {
label: string;
}
}

/*
* Parsed objects
*/
Expand All @@ -310,6 +341,7 @@ declare namespace Ytc {
interface ParsedTextRun {
type: 'text';
text: string;
styles?: string[];
}

interface ParsedLinkRun {
Expand Down Expand Up @@ -400,13 +432,26 @@ declare namespace Ytc {
showtime: number;
}

interface ParsedRedirect {
type: 'redirect';
item: {
message: ParsedRun[];
profileIcon: ParsedImage;
action: {
url: string;
text: ParsedRun[];
}
};
showtime: number;
}

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

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

type ParsedTimedItem = ParsedMessage | ParsedTicker;

Expand Down

0 comments on commit aba3511

Please sign in to comment.