Skip to content

Commit

Permalink
betterEmbedsYT: descriptions
Browse files Browse the repository at this point in the history
  • Loading branch information
Cynosphere committed Feb 6, 2025
1 parent 8f6eeb1 commit c81b766
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 6 deletions.
20 changes: 18 additions & 2 deletions src/betterEmbedsYT/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
import { Patch } from "@moonlight-mod/types";
import { Patch, ExtensionWebpackModule } from "@moonlight-mod/types";

export const patches: Patch[] = [];
export const patches: Patch[] = [
{
find: ".VIDEO_EMBED_PLAYBACK_STARTED,",
replace: {
match: /(case \i\.\i\.VIDEO:)(case \i\.\i\.GIFV:)break;(?=default:(\i)=)/,
replacement: (_, VIDEO, GIFV, description) =>
`${GIFV}break;${VIDEO}if(this.props.embed.provider?.name==="YouTube"){${description}=require("betterEmbedsYT_description").default(this.props);}break;`
},
prerequisite: () => moonlight.getConfigOption<boolean>("betterEmbedsYT", "description") ?? true
}
];

export const webpackModules: Record<string, ExtensionWebpackModule> = {
description: {
dependencies: [{ id: "react" }, { id: "discord/components/common/index" }, { ext: "spacepack", id: "spacepack" }]
}
};
26 changes: 22 additions & 4 deletions src/betterEmbedsYT/manifest.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,34 @@
{
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
"id": "betterEmbedsYT",
"version": "1.0.0",
"version": "1.1.0",
"meta": {
"name": "Better YouTube Embeds",
"tagline": "Bypass copyright blocks, block ads and trackers. Works with Watch Together.",
"tagline": "Bypass copyright blocks, descriptions, block ads and trackers (works with Watch Together).",
"description": "Ad blocking uses code from the [Iridium](https://github.com/ParticleCore/Iridium) web extension.",
"authors": ["Cynosphere"],
"tags": ["qol", "privacy"],
"source": "https://github.com/Cynosphere/moonlight-extensions"
"source": "https://github.com/Cynosphere/moonlight-extensions",
"changelog": "Added option for showing descriptions"
},
"settings": {
"description": {
"displayName": "Show video descriptions",
"type": "boolean",
"default": true
},
"fullDescription": {
"displayName": "Fetch full descriptions",
"description": "Fetches the full description through YouTube's API using one of Google's own API keys",
"type": "boolean",
"default": true
},
"expandDescription": {
"displayName": "Expand description by default",
"type": "boolean",
"default": false
}
},
"settings": {},
"apiLevel": 2,
"blocked": [
"https://play.google.com/log*",
Expand Down
14 changes: 14 additions & 0 deletions src/betterEmbedsYT/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.betterEmbedsYT-description-button {
text-align: center;
user-select: none;
cursor: pointer;
padding-top: 0.25rem;
}

.betterEmbedsYT-description-firstLine {
height: 1.125rem;
max-height: 1.125rem;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
84 changes: 84 additions & 0 deletions src/betterEmbedsYT/webpackModules/description.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import React from "@moonlight-mod/wp/react";
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
import { Clickable } from "@moonlight-mod/wp/discord/components/common/index";

const EmbedClasses = spacepack.findByCode("embedDescription:")[0].exports;

type RenderDescription = (embed: any, description: string, headings: boolean) => React.ReactNode;

const logger = moonlight.getLogger("Better YouTube Embeds - Description");
const descriptionCache = new Map<string, string>();

const API_KEY = "AIzaSyCpphGplamUhCCEIcum1VyDXBt0i1nOqac"; // one of Google's own
const FAKE_EMBED = { type: "rich" };

function YTDescription({
description,
renderDescription,
videoId
}: {
description: string;
renderDescription: RenderDescription;
videoId: string;
}) {
const [expanded, setExpanded] = React.useState(
moonlight.getConfigOption<boolean>("betterEmbedsYT", "expandDescription")
);
const [fullDescription, setFullDescription] = React.useState(
descriptionCache.has(videoId) ? descriptionCache.get(videoId) : description
);

React.useEffect(() => {
if (!descriptionCache.has(videoId))
if (
(moonlight.getConfigOption<boolean>("betterEmbedsYT", "fullDescription") ?? true) &&
description.endsWith("...") &&
description.length >= 300
) {
(async () => {
try {
const data = await fetch(
`https://www.googleapis.com/youtube/v3/videos?part=snippet&id=${videoId}&key=${API_KEY}`
).then((res) => res.json());
const newDesc = data?.items?.[0]?.snippet?.description ?? description;
descriptionCache.set(videoId, newDesc);
setFullDescription(newDesc);
} catch (err) {
logger.error(`Failed to get full description for "${videoId}":`, err);
descriptionCache.set(videoId, description);
}
})();
} else {
descriptionCache.set(videoId, description);
}
});

const lines = fullDescription!.split("\n");

const rendered = renderDescription(FAKE_EMBED, fullDescription!, false);
const firstLine = renderDescription(FAKE_EMBED, lines[0], false);

return lines.length === 1 && description.length <= 40 ? (
<div className={EmbedClasses.embedDescription + " " + EmbedClasses.embedMargin}>{rendered}</div>
) : (
<div className={EmbedClasses.embedDescription + " " + EmbedClasses.embedMargin}>
{expanded ? rendered : <div className="betterEmbedsYT-description-firstLine">{firstLine}</div>}
<Clickable className="betterEmbedsYT-description-button" onClick={() => setExpanded(!expanded)}>
{expanded ? "Show less" : "Show more"}
</Clickable>
</div>
);
}

export default function DescriptionWrapper({
embed,
renderDescription
}: {
embed: { rawDescription?: string; video: { url: string } };
renderDescription: RenderDescription;
}) {
const videoId = embed.video.url.split("/embed/").pop()!;
return embed.rawDescription == null ? null : (
<YTDescription description={embed.rawDescription} renderDescription={renderDescription} videoId={videoId} />
);
}

0 comments on commit c81b766

Please sign in to comment.