Skip to content

Library for "unpacking" and reading files inside rar archives as node Readable streams.

License

Notifications You must be signed in to change notification settings

doom-fish/rar-stream

Repository files navigation

rar-stream

TESTS

Library for "unpacking" and reading files inside rar archives as node Readable streams.

Note: Requires node version >= 18.0.0

Note: Decompression is not implemented at the moment

Getting Started

Below example shows how to unpack local rar files by piping the inner files to the file system.

import fs from "fs";
import path from "path";
import { RarFilesPackage, LocalFileMedia } from "rar-stream";
const CWD = process.cwd();

const localRarFiles = [
  path.resolve(CWD, "./file.r00"),
  path.resolve(CWD, "./file.r01"),
  path.resolve(CWD, "./file.r02"),
  path.resolve(CWD, "./file.rar"),
].map((p) => new LocalFileMedia(p));

const rarFilesPackage = new RarFilesPackage(localRarFiles);

async function writeInnerRarFilesToDisk() {
  const innerFiles = await rarFilesPackage.parse();
  for (const innerFile of innerFiles) {
    const stream = await innerFile.createReadStream({ start: 0, end: innerFile.length - 1 })
    stream.pipe(fs.createWriteStream(innerFile.name));
  }
}

await writeInnerRarFilesToDisk();

See example/webtorrent.js for a more advanced example.

Installing

Install from npm repo with:

npm i rar-stream

API

RarFilesPackage Api

Methods:

Method Description
constructor Takes an array of local file paths as strings or instances that satifies the FileMedia interface mentioned below.
parse Parses all rar files and returns a Promise with InnerFiles.

Filtering:

The RarFilesPackage.parse() method accepts one optional parameter that should be an object of options.

As parsing is done sequencially, opts.filter and opts.maxFiles can be used to filter and limit results.

Here is an example:

{
  filter: (fileName, fileIdx) => {
    if (fileName.includes('Lorem Ipsum')) {
      return true;
    } else if (fileIdx === 9) {
      return true;
    }
    return false;
  },
  maxFiles: 1
}

The filter function will be called on each file entry from within the RAR archive and it will always include the name of the file and the file index. The filter function is expected to return a boolean value, if the returned value is true the file will be included in the parser's results.

The parser will stop processing the file list once it reaches the maxFiles limit of returned files.

Events:

Event Description
parsing-start Emitted when the parsing is started, happens when you call parse. Event args are a bundle represntation of all the rar files passed to the constructor.
file-parsed Emitted each time a rar file is parsed. The event argument is the RarFile just parsed, i.e .rxx in the chain.
parsing-complete Emitted when the parsing is completed. The event argument is an array of all the parsed InnerFiles.

Example

const rarFilesPackage = new RarFilesPackage(localRarFiles);
rarFilesPackage.on('parsing-start', rarFiles => console.log(rarFiles))
rarFilesPackage.on('file-parsed', rarFile => console.log(rarFile.name))
rarFilesPackage.on('parsing-end', innerFiles => console.log(innerFiles))
const innerFiles = await rarFilesPackage.parse();

InnerFile Api

Implements the FileMedia interface.

Methods:

Method Description
createReadStream({start: number, end: number}) Returns a Promise with a Readable stream. The start and end interval is inclusive.
readToEnd Returns a Promise with a Buffer containing all the content of the file.

Properties:

Property Description
name The name of the file
length Returns the total number of bytes of the file

Example

const innerFiles = await rarStreamPackage.parse();
const innerFileStream = await innerFiles[0].createReadStream({ start: 0, end: 30});

FileMedia Interface

This is loosely enforced interface that makes this module interoptable with other node modules such as torrent-stream or webtorrent.

Should have the following shape:

 // FileMedia
 {
  createReadStream(interval: Interval): Promise<Readable>,
  name: string,
  length: number // Length or size of the file in bytes
 }

 // Interval
 // start and end should be inclusive.
 {
  start: number,
  end: number
 }

Development

Running the tests

Run tests with:

npm test

Contributing

Post a new issue if you'd like to contribute in any way.

Versioning

We use SemVer for versioning. For the versions available, see the tags on this repository.

License

This project is licensed under the MIT License - see the LICENSE.md file for details