The Relationship Between Magnet Links and Torrent Files: Practical Implementation of Torrent to Magnet Conversion in the Frontend (Angular)

中文版:https://tuki.moe/angular-magnet-and-torrent/

Recently, I encountered the issue of converting torrent files to magnet links in a project related to online torrent downloading. Since the backend only accepts magnet links as input, we initially tried using public services for the conversion. However, we found that these services were easily restricted, and sending user requests to external sites did not align with privacy standards.

Subsequently, we attempted to host our own conversion service using https://github.com/likebeta/torrent2magnet. However, it still felt a bit cumbersome to use. Therefore, we researched the relationship between torrent files and magnet links and attempted to perform the conversion directly in the frontend, reducing reliance on external resources (and intermediaries profiting from the price difference). Additionally, this provided an opportunity for us to learn more about the related concepts.

What is a torrent file?

We often hear people mention torrent files, but what exactly are they? Generally, when we refer to a torrent file, we mean a .torrent file that contains metadata for files that need to be shared. For example, according to Wikipedia, a torrent file typically includes the following information (though there may be additional information):

  • announce: The URL of the main tracker used for interaction between peers and seeders.
  • info: A dictionary mapping to a dictionary of file properties. The keys in this dictionary depend on whether a single file or multiple files are being shared.
  • files: A list of dictionaries, where each dictionary corresponds to a file, with the following keys:
    • length: The size of the file in bytes.
    • path: A list of strings representing the subdirectory names, with the last item being the actual filename.
  • name: The name of the resource or folder.
  • piece length: The number of bytes per file piece. Typically 2^8 = 256 KiB = 262,144 bytes.
  • pieces: A concatenation of the SHA-1 hashes of each file piece. Since SHA-1 returns a 160-bit hash, the pieces value will be a string that is a multiple of 160 bits. It corresponds to either a length (for a single file being shared) or files (for multiple files being shared).

This information is encoded in the torrent file using Bencode. For example, to decode the contents of a torrent file like ubuntu-22.04-desktop-amd64.torrent, you can use the following simple code:

const fs = require('fs');
const bencode = require('bencode');
const parsed = bencode.decode(fs.readFileSync('./ubuntu-22.04-desktop-amd64.torrent'));
console.log(parsed);

Magnet links

According to Wikipedia, a magnet link is a unique identifier for a file generated based on its metadata. It is used in distributed databases to identify and search for documents based on their hash values.

A magnet link consists of a set of parameters, with the most commonly used parameter being xt, which is typically a URN (Uniform Resource Name) formed from the content hash of a specific file (e.g., magnet:?xt=urn:btih:2DAD5FF88A845EFE729FD87A26B529C5712BAFC7).

The parameters in a magnet link include:

  • dn (display name): The filename.
  • xl (exact length): The file size in bytes.
  • xt (exact topic): The URN containing the hash value of the file.
  • as (acceptable sources): Network links to online files.
  • xs (exact source): P2P links.
  • kt (keywords): Keywords used for searching.
  • mt (manifest): Links to a metadata file containing magnet links (MAGMA – MAGnet MAnifest).

Relationship between magnet links and torrent files

By using the file hash value in the magnet link, it is possible to uniquely locate the torrent file in a distributed hash table (DHT) and then download the file by connecting to a tracker.

The xt (exact topic) parameter in the magnet link is calculated by decoding the entire info parameter from the torrent file, which is then hashed using SHA-1. Finally, the magnet link is constructed by concatenating magnet:? with the parameters. Here’s an example in JavaScript:

const fs = require('fs');
const sha1 = require('js-sha1');
const bencode = require('bencode');
const parsed = bencode.decode(fs.readFileSync('./ubuntu-22.04-desktop-amd64.torrent'));
console.log(parsed);
const infohash = sha1(bencode.encode(parsed.info));
console.log(infohash); // magnet:?xt=urn:btih:2DAD5FF88A845EFE729FD87A26B529C5712BAFC7

Conversion between magnet links and torrent parameters:

  • dn: Use info.name.
  • xl: Use info.length.
  • tr: You can directly use announce, or you can concatenate all the URLs from announce-list.

Converting in Angular

To convert a torrent file to a magnet link in Angular, you can use reader.readAsArrayBuffer to read the uploaded torrent file and obtain the info from it. Then, you can calculate the corresponding infohash using the method described above.

Here’s an example code snippet:

<input 
  type="file"
  accept=".torrent"
  (change)="uploadTorrent($event)"
/>

import * as buffer from 'buffer';
(window as any).Buffer = buffer.Buffer;

uploadTorrent(event: any) {
  const file = event.target.files[0];
  const reader = new FileReader();
  // Read the file as ArrayBuffer since bencode.decode requires it
  reader.readAsArrayBuffer(file);
  const sha1 = require('js-sha1');
  const bencode = require('bencode');
  reader.onload = async (file: any) => {
    const buffer_content = Buffer.from(file.target.result);
    // Use Buffer for bencode, but since Buffer is not available natively, you need to import it and set it as a global variable (polyfills.ts)
    const torrent = bencode.decode(buffer_content);
    const infohash = sha1(bencode.encode(torrent.info)).toUpperCase(); // 2DAD5FF88A845EFE729FD87A26B529C5712BAFC7
  }
}

polyfills.ts:

import * as buffer from 'buffer';
(window as any).Buffer = buffer.Buffer;

torrent2magnet-js

Based on the experiences gathered from the project, I created a small tool for conversion and published it on npm:

https://github.com/tukideng/torrent2magnet-js

It accepts the buffer of a torrent file and outputs certain information contained within it, as well as the magnet URI. Additionally, there is an online conversion tool available:

https://t2m.tuki.moe/

I hope this can help fellow developers with similar needs. Have fun! 🥳

Leave a Reply

Your email address will not be published. Required fields are marked *