Skip to main content

Overview

Use cases are factory functions that encapsulate specific business operations. Each factory accepts service dependencies and returns an async function that executes the operation. This pattern enables dependency injection and testability.

Factory pattern

All use cases follow this pattern:
export const useCaseFactory = (dependencies) => {
  const run = async (...params): Promise<Result> => {
    // Implementation
  };
  return run;
};

Fragment operations

downloadSingleFactory

Downloads a single media fragment from the network.
loader
ILoader
required
Network loader service for fetching data
Returns: (fragment: Fragment, fetchAttempts: number) => Promise<ArrayBuffer>
export const downloadSingleFactory = (loader: ILoader) => {
  const run = async (
    fragment: Fragment,
    fetchAttempts: number
  ): Promise<ArrayBuffer> => {
    const { data } = await fetchWithFallback(
      fragment.uri,
      fragment.fallbackUri,
      fetchAttempts,
      loader.fetchArrayBuffer
    );
    return data;
  };
  return run;
};
Parameters:
  • fragment: Fragment object with URI and fallback URI
  • fetchAttempts: Maximum number of retry attempts

decryptSingleFragmentFactory

Decrypts an encrypted media fragment using AES encryption keys.
loader
ILoader
required
Network loader service for fetching encryption keys
decryptor
IDecryptor
required
Decryption service for AES operations
Returns: (key: Key, data: ArrayBuffer, fetchAttempts: number) => Promise<ArrayBuffer>
export const decryptSingleFragmentFactory = (
  loader: ILoader,
  decryptor: IDecryptor
) => {
  const run = async (
    key: Key,
    data: ArrayBuffer,
    fetchAttempts: number
  ): Promise<ArrayBuffer> => {
    if (!key.uri || !key.iv) {
      return data;
    }
    const { data: keyArrayBuffer } = await fetchWithFallback(
      key.uri,
      key.fallbackUri,
      fetchAttempts,
      loader.fetchArrayBuffer
    );
    const decryptedData = await decryptor.decrypt(data, keyArrayBuffer, key.iv);
    return decryptedData;
  };
  return run;
};
If the fragment has no encryption key, the original data is returned unchanged.

getFragmentsDetailsFactory

Parses a level playlist and extracts all fragment URIs with encryption details.
loader
ILoader
required
Network loader for fetching playlist text
parser
IParser
required
M3U8 parser service
Returns: (playlist: Level, fetchAttempts: number, options?: GetFragmentsDetailsOptions) => Promise<Fragment[]>
export interface GetFragmentsDetailsOptions {
  baseUri?: string;
}

export const getFragmentsDetailsFactory = (
  loader: ILoader,
  parser: IParser
) => {
  const run = async (
    playlist: Level,
    fetchAttempts: number,
    options: GetFragmentsDetailsOptions = {}
  ): Promise<Fragment[]> => {
    const baseUri = options.baseUri ?? playlist.playlistID ?? playlist.uri;
    const primaryPlaylistUri = appendQueryParams(baseUri, playlist.uri);
    const fallbackPlaylistUri =
      primaryPlaylistUri !== playlist.uri ? playlist.uri : null;

    const { uri: usedPlaylistUri, data: levelPlaylistText } =
      await fetchWithFallback(
        primaryPlaylistUri,
        fallbackPlaylistUri,
        fetchAttempts,
        loader.fetchText
      );
    const fragments = parser.parseLevelPlaylist(
      levelPlaylistText,
      usedPlaylistUri
    );

    return fragments.map((fragment) => {
      const primaryUri = appendQueryParams(baseUri, fragment.uri);
      const fallbackUri = primaryUri !== fragment.uri ? fragment.uri : null;
      const keyPrimaryUri = fragment.key.uri
        ? appendQueryParams(baseUri, fragment.key.uri)
        : fragment.key.uri;
      const keyFallbackUri =
        fragment.key.uri && keyPrimaryUri !== fragment.key.uri
          ? fragment.key.uri
          : null;

      return new Fragment(
        new Key(keyPrimaryUri, fragment.key.iv, keyFallbackUri),
        primaryUri,
        fragment.index,
        fallbackUri
      );
    });
  };
  return run;
};

Playlist operations

getLevelsFactory

Fetches and parses a master playlist to extract available quality levels and tracks.
loader
ILoader
required
Network loader for fetching playlist
parser
IParser
required
M3U8 parser service
Returns: (masterPlaylistURI: string, fetchAttempts: number) => Promise<Level[]>
export const getLevelsFactory = (loader: ILoader, parser: IParser) => {
  const run = async (
    masterPlaylistURI: string,
    fetchAttempts: number
  ): Promise<Level[]> => {
    try {
      const masterPlaylistText = await loader.fetchText(
        masterPlaylistURI,
        fetchAttempts
      );
      return parser.parseMasterPlaylist(masterPlaylistText, masterPlaylistURI);
    } catch (error) {
      throw Error("LevelManifest");
    }
  };
  return run;
};

Bucket operations

Buckets are temporary storage containers for downloaded fragments before they’re merged.

createBucketFactory

Creates a new bucket to store video and audio fragments.
fs
IFS
required
File system service
Returns: (bucketID: string, videoLength: number, audioLength: number) => Promise<void>
export const createBucketFactory = (fs: IFS) => {
  const run = async (
    bucketID: string,
    videoLength: number,
    audioLength: number
  ): Promise<void> => {
    await fs.createBucket(bucketID, videoLength, audioLength);
  };
  return run;
};

writeToBucketFactory

Writes a downloaded and decrypted fragment to a bucket at a specific index.
fs
IFS
required
File system service
Returns: (bucketID: string, index: number, data: ArrayBuffer) => Promise<void>
export const writeToBucketFactory = (fs: IFS) => {
  const run = async (
    bucketID: string,
    index: number,
    data: ArrayBuffer
  ): Promise<void> => {
    const bucket = await fs.getBucket(bucketID);
    await bucket.write(index, data);
  };
  return run;
};

Subtitle operations

downloadSubtitleTrackFactory

Downloads a complete subtitle track and saves it as a WebVTT file.
loader
ILoader
required
Network loader for fetching subtitle data
parser
IParser
required
M3U8 parser service
fs
IFS
required
File system service for saving files
Returns: (level: Level, playlist: Playlist, fetchAttempts: number, dialog: boolean, options?: { baseUri?: string }) => Promise<string>
export const downloadSubtitleTrackFactory = (
  loader: ILoader,
  parser: IParser,
  fs: IFS
) => {
  const run = async (
    level: Level,
    playlist: Playlist,
    fetchAttempts: number,
    dialog: boolean,
    options: { baseUri?: string } = {}
  ): Promise<string> => {
    const baseUri = options.baseUri ?? playlist.uri;
    const fragments = await getFragmentsDetailsFactory(loader, parser)(
      level,
      fetchAttempts,
      { baseUri }
    );

    const hasFragments = fragments.length > 0;
    const textParts: string[] = [];

    if (hasFragments) {
      for (const fragment of fragments) {
        const { data: fragmentText } = await fetchWithFallback(
          fragment.uri,
          fragment.fallbackUri,
          fetchAttempts,
          loader.fetchText
        );
        textParts.push(fragmentText.trim());
      }
    } else {
      const levelUri = appendQueryParams(baseUri, level.uri);
      const { data: subtitleText } = await fetchWithFallback(
        levelUri,
        level.uri,
        fetchAttempts,
        loader.fetchText
      );
      textParts.push(subtitleText.trim());
    }

    const fileName = generateSubtitleFileName()(playlist, level);
    const link = URL.createObjectURL(
      new Blob([textParts.join("\n\n")], { type: "text/vtt" })
    );

    try {
      await fs.saveAs(fileName, link, { dialog });
    } finally {
      URL.revokeObjectURL(link);
    }
    return fileName;
  };

  return run;
};
Subtitles can be either fragmented (multiple VTT segments) or single-file. This use case handles both formats automatically.

Complete use case list

The core package exports these use cases:
  • createBucketFactory: Create fragment storage bucket
  • decryptSingleFragmentFactory: Decrypt encrypted fragment
  • downloadSingleFactory: Download single fragment
  • getFragmentsDetailsFactory: Parse playlist for fragments
  • getLevelsFactory: Parse master playlist for levels
  • getLinkBucketFactory: Get download link from bucket
  • writeToBucketFactory: Write fragment to bucket
  • writeToFileFactory: Write data to file
  • generateFileNameFactory: Generate video filename
  • generateSubtitleFileNameFactory: Generate subtitle filename
  • deleteBucketFactory: Delete fragment bucket
  • fsCleanupFactory: Clean up file system
  • downloadSubtitleTrackFactory: Download subtitle track
  • getSubtitleTextFactory: Get subtitle text content
  • storeSubtitleTextFactory: Store subtitle in state
  • inspectLevelEncryptionFactory: Inspect encryption details

Build docs developers (and LLMs) love