api.ts 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. import { Resolver, presence } from "../util";
  2. import { getVideoID as getDefaultVideoID } from "./default";
  3. import { getVideoID as getShortsVideoID } from "./shorts";
  4. const videoCache = new Map<string, YouTubeAPIResponse>(),
  5. videoCacheLoading = new Set<string>();
  6. interface YouTubeAPIResponse {
  7. videoDetails: {
  8. title: string;
  9. lengthSeconds: string;
  10. channelId: string;
  11. shortDescription: string;
  12. thumbnail: {
  13. thumbnails: {
  14. url: string;
  15. }[];
  16. };
  17. allowRatings: boolean;
  18. viewCount: string;
  19. author: string;
  20. isPrivate: boolean;
  21. isLiveContent: boolean;
  22. };
  23. }
  24. async function fetchVideoData(id: string) {
  25. const data = await presence.getPageVariable(
  26. "yt.config_.INNERTUBE_API_KEY",
  27. "yt.config_.INNERTUBE_CLIENT_NAME",
  28. "yt.config_.INNERTUBE_CLIENT_VERSION"
  29. ),
  30. request = fetch(
  31. `https://www.youtube.com/youtubei/v1/player?key=${data["yt.config_.INNERTUBE_API_KEY"]}`,
  32. {
  33. method: "POST",
  34. headers: {
  35. "Content-Type": "application/json",
  36. },
  37. body: JSON.stringify({
  38. videoId: id,
  39. context: {
  40. client: {
  41. clientName: data["yt.config_.INNERTUBE_CLIENT_NAME"],
  42. clientVersion: data["yt.config_.INNERTUBE_CLIENT_VERSION"],
  43. },
  44. },
  45. }),
  46. }
  47. ).then(res => res.json() as Promise<YouTubeAPIResponse>);
  48. videoCacheLoading.add(id);
  49. videoCache.set(id, await request);
  50. videoCacheLoading.delete(id);
  51. }
  52. function isActive(): boolean {
  53. const currentVideoID = getVideoID();
  54. if (
  55. !videoCache.has(currentVideoID) &&
  56. !videoCacheLoading.has(currentVideoID)
  57. ) {
  58. fetchVideoData(currentVideoID).catch(() => {
  59. presence.error("Failed to fetch video data through API");
  60. });
  61. return false;
  62. } else if (videoCacheLoading.has(currentVideoID)) return false;
  63. return (
  64. !!getTitle() && !!getUploader() && !!currentVideoID && !!getChannelURL()
  65. );
  66. }
  67. function getTitle(): string {
  68. return videoCache.get(getVideoID())?.videoDetails?.title;
  69. }
  70. function getUploader(): string {
  71. return videoCache.get(getVideoID())?.videoDetails?.author;
  72. }
  73. export function getVideoID(): string {
  74. return getDefaultVideoID() ?? getShortsVideoID();
  75. }
  76. export function getChannelURL(): string {
  77. return `https://www.youtube.com/channel/${
  78. videoCache.get(getVideoID())?.videoDetails?.channelId
  79. }`;
  80. }
  81. const resolver: Resolver = {
  82. isActive,
  83. getTitle,
  84. getUploader,
  85. getChannelURL,
  86. getVideoID,
  87. };
  88. export default resolver;