JSPM

  • Created
  • Published
  • Downloads 140
  • Score
    100M100P100Q55603F
  • License MIT

Fetch YouTube live chat without API using NextJS + React Hook which bypass CORS.

Package Exports

  • next-youtube-livechat
  • next-youtube-livechat/dist/index.js
  • next-youtube-livechat/dist/index.mjs

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (next-youtube-livechat) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

next-youtube-livechat

Fetch YouTube live chat without API using NextJS which bypass CORS.

🚨 You will need to take full responsibility for your action 🚨

Demo site: https://next-youtube-livechat.vercel.app/

Getting started

Source Code

Hook

React hook for getting livestream message data, event handling

  • src/hooks/useLiveChat.tsx

Youtube related functions

Use to parse chat message / emoji
API functions contain payload for fetching live chatroom (without using YouTube Data API)

  • src/lib/yt-api-parser.ts
  • src/lib/yt-api-requests.ts

Next Backend API

Usage

NextJS API

Add the follow API code to bypass CORS, otherwise this library will not work.
Here is the example for NextJS 14 app directory:

app\api\yt-api\[...slug]\route.ts

import { NextRequest, NextResponse } from 'next/server';

const YOUTUBE_URL = 'https://www.youtube.com';

const buildYouTubeEndpoint = (req: NextRequest) => {
  const path = req.nextUrl.pathname.replace('/api/yt-api', '');
  const suffix = `${path}${req.nextUrl.search}`;
  const url = `${YOUTUBE_URL}${suffix}`;
  return url;
};

const GET = async (req: NextRequest) => {
  const res = await fetch(buildYouTubeEndpoint(req), { cache: 'no-store' })
    .then((d) => d.text())
    .then((d) => {
      return d;
    })
    .catch((error) => {
      throw new Error(`Server Action Failed: ${error.message}`);
    });
  return NextResponse.json(res, { status: 200 });
};

const POST = async (req: NextRequest) => {
  const postData = await req.json();
  const data = await fetch(buildYouTubeEndpoint(req), {
    method: 'POST',
    cache: 'no-store',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(postData),
  })
    .then((d) => d.json())
    .catch((error) => {
      throw new Error(`Server Action Failed: ${error.message}`);
    });
  return NextResponse.json(data, { status: 200 });
};

export { GET, POST };

Example

import useLiveChat from '@/hooks/useLiveChat';
import { ChatItem } from '@/types/yt-data';

const Demo = () => {
  /** loading state, optional. Can use to control show spinner before fetching start & hide spinner after fetching start **/
  const [isLoading, setIsLoading] = useState<boolean>();

  /** ready state, required. Use to control start/termination of chat message polling **/
  const [isReady, setIsReady] = useState<boolean>();

  /** url state, required. Use to provide YouTube livestream target to fetch chat message **/
  const [url, setUrl] = useState<string | undefined>();

  const onBeforeStart = useCallback(async () => {
    setIsLoading(true);
  }, [setIsLoading]);

  const onStart = useCallback(async () => {
    setIsLoading(false);
    setIsReady(true);
  }, [setIsLoading, setIsReady]);

  const onChatItemsReceive = useCallback(async (newChatItems: ChatItem[], existingChatItems: ChatItem[]) => {
    console.log('new chat items', newChatItems);
    console.log('received chat items', existingChatItems);
  }, []);

  const onError = useCallback(async () => {
    setIsLoading(false);
    setIsReady(false);
    setUrl();
  }, [setIsLoading, setIsReady, setUrl]);

  const { displayedMessage, rawChatItems, cleanUp } = useLiveChat({
    url,
    isReady,
    onBeforeStart,
    onStart,
    onChatItemsReceive,
    onError,
  });

return  (<div>
          {displayedMessage?.map((it, index) => (
            <span key={`${it.message}${index}`}>{it.message}</span>
          ))}
        </div>)
}

Reference

Part of the code is referenced from LinaTsukusu/youtube-chat, many thanks 🙌