Syakhisk / partyplaylist

Monorepo of PartyPlaylist
https://partyplaylist-yt.vercel.app
0 stars 0 forks source link

[BE] Cache ytdl-core getInfo query #36

Open Syakhisk opened 1 year ago

Syakhisk commented 1 year ago

Description

getInfo function from ytdl-core can take up to 10s, when user jumps it will call that function again. caching will at least make the subsequent request faster.

probably use node-cache

Example

Expand
let ytdlCache: any = {};
export class ResourceController {
  @Get(':id/stream')
  async getStream(
    @Param('id') id: string,
    @Req() req: FastifyRequest,
    @Res({ passthrough: true }) reply: FastifyReply,
  ): Promise {
    const headers = req.headers;
    const videoUrl = `https://www.youtube.com/watch?v=${id}`;

    const time = Date.now();
    Logger.log(`Fetching video info for ${videoUrl}`);

    // check for cache, return info from it
    let info = ytdlCache[id];
    if (!info) {
      Logger.log(`No cache found for ${videoUrl}`);
      // const info = await ytdl.getInfo(videoUrl).catch((e) => {
      info = await ytdl.getInfo(videoUrl).catch((e) => {
        if (e.message.includes('Video unavailable')) {
          throw new NotFoundException(e.message);
        }

        if (e.message.includes('does not match expected format')) {
          throw new BadRequestException(e.message);
        }

        throw new InternalServerErrorException(e, e.message);
      });

      ytdlCache[id] = info;
      console.log('cache', ytdlCache);
    } else {
      Logger.log(`Cache found for ${videoUrl}`);
    }

    Logger.log(`Fetched video info for ${videoUrl} in ${Date.now() - time}ms`);

    // Find the best available audio-only format with an mp4 container
    const audioFormats = ytdl.filterFormats(info.formats, 'audioonly');
    const audioFormat = audioFormats.find(
      (format) => format.container === 'mp4',
    );

    // Get the content length of the chosen format
    const contentLength = Number(audioFormat.contentLength);

    const range = headers.range || `bytes=0-${contentLength - 1}`;

    const [rangeStart, rangeEnd] = range.replace(/bytes=/, '').split('-');
    const start = parseInt(rangeStart, 10);
    const end = rangeEnd ? parseInt(rangeEnd, 10) : contentLength - 1;
    // const end = rangeEnd ? parseInt(rangeEnd, 10) : start + 10000;
    const chunksize = end - start + 1;

    // Create a readable stream for the requested byte range
    // const audioStream = ytdl(videoUrl, {
    //   quality: 'highestaudio',
    //   filter: 'audioonly',
    //   range: { start, end },
    // });
    const audioStream = ytdl.downloadFromInfo(info, {
      format: audioFormat,
    });
    // .on('data', (chunk) => {});

    // Send the appropriate headers for a partial content response
    reply.raw.writeHead(206, {
      'Content-Range': `bytes ${start}-${end}/${contentLength}`,
      'Accept-Ranges': 'bytes',
      'Content-Length': chunksize,
      'Content-Type': 'audio/mp4',
    });

    // Pipe the audio stream to the response
    return new StreamableFile(audioStream);
  }
}
github-actions[bot] commented 1 year ago

Create this branch locally

  git checkout master && git checkout -b PLAY-036
Syakhisk commented 1 year ago

better yet, optimze the query on getInfo