npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ctrl/torrent-file

v4.5.1

Published

Parse a torrent file (name, hash, files, pieces)

Downloads

14,159

Readme

torrent-file npm

Parse, inspect, and encode BitTorrent metainfo files.

Supports BitTorrent v1 (BEP 3), v2 (BEP 52), and hybrid torrent files containing both formats.

This project is based on parse-torrent and node-bencode. It includes a strict bencode encoder and decoder built around Uint8Array rather than Node.js Buffer.

Demo: https://torrent-file.pages.dev

Install

npm install @ctrl/torrent-file

API

info(file)

Returns display metadata, trackers, web seeds, and the detected version: 'v1', 'v2', or 'hybrid'.

import fs from 'fs';

import { info } from '@ctrl/torrent-file';

const torrentInfo = info(fs.readFileSync('myfile'));
console.log({ torrentInfo });

files(file)

Returns file and piece information:

  • length is the sum of the listed file lengths.
  • offset is the file's byte offset in the protocol piece space. BEP 52 aligns every non-empty v2 file to a piece boundary, so v2 offsets can contain gaps that are not included in length.
  • pieces contains hexadecimal SHA-1 hashes for v1/hybrid torrents and is absent for v2-only torrents.
  • piecesRoot is a hexadecimal SHA-256 Merkle root on v2/hybrid files.
  • pieceLayers maps hexadecimal pieces roots to arrays of hexadecimal SHA-256 hashes.

Paths use the host platform's path separator. For v2 torrents, the advisory torrent name is prepended to the paths returned from the BEP 52 file tree.

import fs from 'fs';

import { files } from '@ctrl/torrent-file';

const torrentFiles = files(fs.readFileSync('myfile'));
console.log({ torrentFiles });

hash(file)

Returns the v1 SHA-1 info hash. It throws for a v2-only torrent because that torrent does not have a v1 identity.

import fs from 'fs';

import { hash } from '@ctrl/torrent-file';

const torrentHash = hash(fs.readFileSync('myfile'));
console.log({ torrentHash });

hashV2(file)

Returns the full 32-byte v2 SHA-256 info hash as hexadecimal. It throws for a v1-only torrent.

import fs from 'fs';

import { hashV2 } from '@ctrl/torrent-file';

const torrentHashV2 = hashV2(fs.readFileSync('myfile'));
console.log({ torrentHashV2 });

hashes(file)

Returns the available v1 (SHA-1) and v2 (SHA-256) info hashes along with the detected torrent version. infoHash is present for v1 and hybrid torrents; infoHashV2 is present for v2 and hybrid torrents.

import fs from 'fs';

import { hashes } from '@ctrl/torrent-file';

const h = hashes(fs.readFileSync('myfile'));
console.log(h.version); // 'v1', 'v2', or 'hybrid'
console.log(h.infoHash); // SHA-1 (v1/hybrid only)
console.log(h.infoHashV2); // SHA-256 (v2/hybrid only)

Info hashes are computed from the exact bencoded info dictionary bytes found in the file, as required by BEP 3 and BEP 52. They are not computed from a decode/encode round trip.

Low-level bencode

decode() returns bencoded byte strings as Uint8Array, including byte strings used as dictionary values. Decoded dictionaries have a null prototype, so keys such as __proto__, constructor, and toString are ordinary own properties rather than inherited JavaScript behavior. encode() accepts strings, Uint8Array, safe integers, arrays, and dictionaries.

The decoder rejects noncanonical or incomplete bencode, including unordered/duplicate dictionary keys, leading-zero integers, negative zero, unsafe integers, truncated values, and trailing data. The higher-level parsing and hashing functions use the same strict decoder and therefore throw on malformed input.

Dictionary keys can be arbitrary bytes. Decoded dictionaries retain enough internal information for encode(decode(data)) to preserve binary keys such as BEP 52 pieces roots. Newly constructed textual dictionary keys are UTF-8 encoded.

Encode

Convert a parsed torrent object back into a .torrent file buffer.

import fs from 'fs';

import { toTorrentFile } from '@ctrl/torrent-file';

// Minimal example: create a .torrent buffer from fields
const buf = toTorrentFile({
  info: {
    // Required info fields
    'piece length': 16384,
    pieces: new Uint8Array(/* 20-byte SHA1 hashes concatenated */),
    name: 'example.txt',
    length: 12345,
  },
  // Optional fields
  announce: ['udp://tracker.publicbt.com:80/announce'],
  urlList: ['https://example.com/example.txt'],
  private: false,
  created: new Date(),
  createdBy: 'my-app/1.0.0',
  comment: 'Generated by @ctrl/torrent-file',
});

fs.writeFileSync('example.torrent', buf);

toTorrentFile() serializes supplied metadata; it does not read file contents, calculate piece hashes, build v2 Merkle trees, or insert hybrid padding files.

For v2 pieceLayers, dictionary keys are raw 32-byte pieces roots rather than hexadecimal text. When constructing them manually, represent each byte with one JavaScript character:

const rawDictionaryKey = (bytes: Uint8Array) => String.fromCharCode(...bytes);

const buf = toTorrentFile({
  info: v2Info,
  pieceLayers: {
    [rawDictionaryKey(piecesRoot)]: concatenatedLayerHashes,
  },
});

The pieceLayers returned by files() is a display-friendly hexadecimal representation and is not the raw input shape accepted by toTorrentFile().

Validation scope

The parser validates canonical bencoding and the v1/v2 file-information structure it consumes, including v2 metadata version, piece size, file-tree shape, file lengths, pieces roots, and hash byte lengths.

It does not recompute file hashes, cryptographically verify piece layers against Merkle roots, or prove that the v1 and v2 sides of a hybrid torrent describe identical content. Applications downloading or trusting content must perform those integrity checks separately.

Demo

Run a local demo UI to drop a .torrent file and view parsed output:

pnpm install
pnpm demo:watch

To build the demo for static hosting:

pnpm demo:build

Specifications and related projects