Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add caching module for YouTube video info #96

Merged
merged 15 commits into from
Jan 25, 2025

Conversation

mitsuki31
Copy link
Owner

Overview

This pull request introduces a new caching module to optimize the retrieval of YouTube video information. By implementing a local caching mechanism, the system avoids repetitive fetch requests to YouTube servers, resulting in significant performance improvements.

Changes Made

Features

  • Added a new VInfoCache class to handle caching of video information, including creation, validation, and retrieval.
  • Introduced utility classes CacheBase64 and CacheZLib for encoding, decoding, compressing, and decompressing cache objects.
  • Defined constants for cache directory paths (CACHE_PATH, VINFO_CACHE_PATH).
  • Implemented utility functions for cache path generation and ID validation.
  • Introduced a humanReadable option to VInfoCache.getCache and VInfoCache.getAllCaches for better presentation of cache data.
  • Added a createdDate property to cache objects to indicate the creation timestamp.
  • Implemented VInfoCache.deleteCache for cache deletion.
  • Introduced a new error class called CacheValidationError to handle invalid cache objects with detailed contextual information (e.g., cache ID, type, and path).

Implementation

VInfoCache:

class VInfoCache {
  static function createCache(
    videoInfo: ytdl.VideoInfo,
    cacheOptions?: { cacheDir?: string }
  ): Promise<string>;
  static function getCache(
    videoId: string,
    cacheOptions?: {
      cacheDir?: string,
      humanReadable?: boolean,
      validate?: boolean
    }
  ): Promise<ExtractedVideoInfoCacheObject | string | null>;
  static function getAllCaches(
    cacheOptions?: {
      cacheDir?: string,
      humanReadable?: boolean,
      validate?: boolean
    }
  ): Promise<ExtractedVideoInfoCacheObject[] | string>;
  static function deleteCache(
    videoId: string,
    cacheOptions?: { cacheDir?: string }
  ): Promise<boolean>;
}

CacheBase64:

class CacheBase64 {
  static function encodeCacheObject(
    obj: Record<string, unknown>
  ): string;
  static function decodeCacheObject(
    encodedStr: string
  ): Record<string, unknown>;
}

CacheZLib:

class CacheZLib {
  static function deflateCacheObject(
    obj: Record<string, unknown>
  ): Promise<Buffer>;
  static function inflateCacheObject(
    deflatedObj: Record<'type' | 'data', string>
  ): Promise<Record<string, unknown>>;
}

Testing

  • Comprehensive unit tests for CacheBase64, CacheZLib, VInfoCache, and utility functions.
  • Validation scenarios for handling invalid caches, including the new CacheValidationError.
  • Added test coverage for new features like cache deletion and the humanReadable option.

Documentation

  • Updated JSDoc for all new and modified methods.
  • Declared a new typedef, VideoInfoCacheObject, to represent the cache data structure.
  • Declared a new typedef, ExtractedVideoInfoCacheObject, to represent parsed and extracted cache data.

Cache Structure

The cache structure is as follows:

{
  "id": "<videoId>",
  "encoding": "binary",
  "createdDate": "<timestamp>",
  "title": "<videoTitle>",
  "authorName": "<authorName>",
  "videoUrl": "https://www.youtube.com/watch?v=<videoId>",
  "authorUrl": "https://www.youtube.com/<@username>",
  "videoInfo": {
    "type": "zlib/bin",
    "data": "<compressedBinaryCacheData>"
  }
}

TypeScript:

interface VideoInfoCacheObject {
  id: string;
  encoding: 'binary';
  createdDate: number;
  title: string;
  authorName: string;
  videoUrl: string;
  authorUrl: string;
  videoInfo: {
    type: 'zlib/bin';
    data: string;
  };
}
Details
Property Description
id Unique identifier for the video.
encoding Encoding format of the cache data, typically "binary".
createdDate Timestamp indicating when the cache was created (returned from Date.now()).
title Title of the video or "<unknown>" if not available.
authorName Name of the author of the video or "<unknown>" if not available.
videoUrl URL of the video.
authorUrl URL of the author of the video (refers to author profile).
videoInfo Object containing the compressed and binary encoded video information.
videoInfo.type Type of the video information, typically "zlib/bin".
videoInfo.data Compressed and binary encoded video information data.

Usage Examples

Please take note first, that the cache utilities are intended for internal use.

// ...

const videoInfo = // assume you have a valid `videoInfo` object

// Create a cache object
const cachePath = await VInfoCache.createCache(videoInfo, {
  cachePath: undefined // path to custom cache directory, if needed
});
console.log(`Cache created at: ${cachePath}`);

// Get a cache object
const cachedInfo = await VInfoCache.getCache(videoId, {
  cachePath: undefined // custom directory path to locate the cache, if needed
});
if (cachedInfo) {
  console.log('Cached video info:', cachedInfo);
} else {
  console.log('No cache found for video ID:', videoId);
}

Impact

  • Improves system performance by reducing repetitive server fetches through caching.
  • Provides better error handling and validation for cache data integrity.
  • Enhances developer usability with detailed documentation, flexible options, and human-readable formats for cache data.
  • Introduces efficient cache management through deletion and comprehensive test coverage.

Summary

This PR establishes the foundation for a robust caching mechanism that enhances both performance and usability for video information handling. The changes ensure better scalability and reliability while adhering to high standards of code quality and maintainability.

- Introduced `VInfoCache` class for creating, checking, and retrieving cached video information.
- Added `CacheBase64` class for encoding and decoding cache objects using Base64.
- Added `CacheZLib` class for compressing and decompressing cache objects using zlib.
- Defined `CACHE_PATH` and `VINFO_CACHE_PATH` constants for cache directory paths.
- Implemented utility functions for cache path generation and ID validation.
- Included comprehensive JSDoc comments for better understanding and usage examples.
- Enhanced `VInfoCache.getCache` to support `humanReadable` option, allowing cache data to be returned as a formatted string.
- Updated `VInfoCache.getAllCaches` to include `humanReadable` option for retrieving all caches in a readable format.
- Improved JSDoc comments to reflect the new functionality and provide detailed descriptions of the new options.
- Added unit tests for `getCachePath` to verify correct cache path generation.
- Added unit tests for `CacheBase64` to test encoding and decoding of objects.
- Added unit tests for `CacheZLib` to test compression and decompression of objects.
- Added unit tests for `VInfoCache` to test cache creation, validation, retrieval, and error handling.
- Included tests for the new `humanReadable` option in cache retrieval functions.
- Optimized the `VInfoCache.createCache` function to return the cache path early if the cache file already exists.
- Introduced a new error class, called `CacheValidationError` to handle invalid cache objects with detailed contextual information (e.g., cache ID, type, and path).
- Removed the `VInfoCache.checkCache` function to consolidated cache validation logic into relevant methods, due to unreliable method to check the cache object while it also unnecessary retrieves and parses the cache object.
- Added the `cacheOptions.validate` option to validate the cache object exact before returning it, implemented to `VInfoCache.getCache` and `VInfoCache.getAllCaches` functions.
- Enhanced the `VInfoCache.getCache` and `VInfoCache.getAllCaches` by throwing the `CacheValidationError` when the cache object does not meet the expected format.
- Refactored the `VInfoCache.getAllCaches` parameters (such as `cachePath` and `humanReadable`) into a single parameter that accepts an object called `cacheOptions`, will be passed to `VInfoCache.getCache` function.
This commit introduces tests for handling invalid cache objects, focusing on scenarios where validation is enabled.

- Added setup in `before` hook to create an invalid cache scenario for testing.
- Added `CacheValidationError` handling in validation-related tests.
- Removed the test cases for `VInfoCache.checkCache` function, which has been removed (1362df6).
- Added new test cases to validate cache objects and handle errors during retrieval and listing when `cacheOptions.validate` is enabled.
- Updated `testMessages.VInfoCache` with a new message for validation-specific testing.
This cause the `getAllCaches` function give a null of video info from the cache object. Now has been resolved by removing unnecessary check of video info keys in decrypted cache object.
This to indicate that cache object has been created at specific date. The value itself is a milliseconds returned from `Date.now()`.

- Added the `createdDate` property to cache object when creating cache.
- Exposed the `createdDate` value to human-readable string format in `getCache` function for adding detail information of cache.
- Added `VInfoCache.deleteCache` function to delete the cache with a given ID
- Added support for `deleteCache` to use the specified cache directory if provided
- Added a test case for testing the `VInfoCache.deleteCache` function
- A minor refactor to other test cases
- Declared a new typedef, `ExtractedVideoInfoCacheObject`, to represents the parsed and extracted cache of video information object
- Changed the returned type in `getCache` and `getAllCaches` functions
- Renamed `cacheOptions.cachePath` to `cacheOptions.cacheDir` for consistency and to avoid confusion.
- Updated all references and documentation to use `cacheDir` instead of `cachePath`.
- Removed unused import of `@distube/ytdl-core`.
@mitsuki31 mitsuki31 added this to the YTMP3-JS v2.0.0 milestone Jan 25, 2025
@mitsuki31 mitsuki31 self-assigned this Jan 25, 2025
Copy link

codecov bot commented Jan 25, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 83.16%. Comparing base (560887d) to head (de128b9).
Report is 16 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master      #96      +/-   ##
==========================================
+ Coverage   80.36%   83.16%   +2.80%     
==========================================
  Files          11       12       +1     
  Lines         611      713     +102     
==========================================
+ Hits          491      593     +102     
  Misses        120      120              
Flag Coverage Δ
Ubuntu 83.16% <100.00%> (+2.80%) ⬆️
Windows 82.88% <100.00%> (+2.85%) ⬆️
all 83.16% <100.00%> (+2.80%) ⬆️
macOS 83.16% <100.00%> (+2.80%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@mitsuki31
Copy link
Owner Author

These changes are only the cache module implementation. Integrating to the core module (ytmp3.js) is still in development and may need a huge refactor into existing features.

@mitsuki31 mitsuki31 added feature Bring new features minor Minor changes labels Jan 25, 2025
@mitsuki31 mitsuki31 merged commit 5f8213c into master Jan 25, 2025
26 checks passed
@mitsuki31 mitsuki31 deleted the feat/add-utilities-to-cache-video-info branch January 25, 2025 12:25
@mitsuki31
Copy link
Owner Author

Cache module recently updated and comes with a new feature to force the cache creation. Refer to #98 for more details.

cacheOptions: {
  cacheDir?: string,
  force?: boolean
}

mitsuki31 added a commit that referenced this pull request Jan 26, 2025
This pull request introduces the ability to force the creation of a cache even when an entry with the same video ID already exists. This feature ensures that developers or later implementations can overwrite outdated or incorrect cache objects with new video information, keeping the cache accurate and up to date.

- b72300e - test(unit): Add test case to verify the cache overwrite ability
- a3a11bb - feat(cache): Add new option to force the cache creation

Related changes: #96

---

Signed-off-by: Ryuu Mitsuki <dhefam31@gmail.com>
mitsuki31 added a commit that referenced this pull request Jan 26, 2025
The return logic of the `getAllCaches` function has been improved to better align with its intended behavior, offering more predictable and meaningful responses based on the `humanReadable` option. This change enhances usability and prepares the API for more diverse use cases.

- 1246a8a - test(unit): Add test case to verify the adjusted return value
- 9992268 - fix(cache): Change the return value if no caches found

Related changes: #96, #98

---

Signed-off-by: Ryuu Mitsuki <dhefam31@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature Bring new features minor Minor changes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant