arokettu/torrent-file
A PHP library for reading, creating, and editing BitTorrent .torrent files. It handles bencoding, v1 and v2 (and hybrid) torrents, and metadata, making it easy to generate or inspect torrents in your PHP applications.
arokettu/torrent-file is a focused bencode/metainfo library, not a Laravel package. There is no service provider, config file, migration, or facade. You install it with Composer and use its classes directly:
composer require arokettu/torrent-file
Start with the README and the Arokettu\Torrent\TorrentFile factory API. The two entry points you will use daily are:
use Arokettu\Torrent\TorrentFile;
// Parse an existing .torrent
$torrent = TorrentFile::load($path);
$name = $torrent->getName();
$announce = $torrent->getAnnounce();
// Build a new v1 torrent
$torrent = TorrentFile::v1()
->setName('release.zip')
->setPieceLength(1024 * 1024)
->setAnnounce('https://tracker.example/announce')
->addFile(storage_path('app/releases/release.zip'), 'release.zip');
$bytes = $torrent->toString();
The first practical use case is either:
For a Laravel app, bind a small wrapper service in AppServiceProvider rather than injecting TorrentFile everywhere. Treat torrent parsing/creation as an application service with DTOs, not as model logic.
Parse uploaded torrents behind a FormRequest and service. Do not trust the file extension or MIME type. Validate size limits, then parse and catch library exceptions:
final class TorrentInspector
{
public function inspect(UploadedFile $file): TorrentMetadata
{
$torrent = TorrentFile::load($file->getRealPath());
return new TorrentMetadata(
name: $torrent->getName(),
announce: $torrent->getAnnounce(),
files: $torrent->getFiles(),
infoHash: $torrent->getInfoHash(),
);
}
}
Persist a normalized JSON representation if you need to query torrents later. Store the original .torrent bytes separately on a disk; do not rely on being able to reconstruct them byte-for-byte.
Generate torrents in a queued job. Hashing pieces is CPU- and I/O-heavy. A release-generation job should discover files, sort them deterministically, build the torrent, write it atomically, and only then update the database:
Storage::disk('torrents')->put(
"{$release->id}.torrent",
$torrent->toString()
);
For local disks use Storage::disk('local')->path($relativePath). For S3 or other remote disks, copy files to a temporary local file first because piece hashing needs stable random access.
Choose v1, v2, or hybrid deliberately. v1 has the broadest client support; v2 gives SHA-256 info hashes and more efficient verification; hybrid produces both hashes and is the safest compatibility choice when you can afford the extra metadata. Keep the strategy in configuration, not scattered through controllers.
Editing metadata is usually safe; editing content is not. Changing announce, comment, created by, web seeds, or tracker tiers does not normally change the info hash. Adding, removing, renaming, or reordering files changes it and invalidates existing magnets and peer references. If your app edits torrents, record the old and new info hashes and make the distinction explicit.
Build magnet links from the library’s hash accessors. For v1:
$magnet = sprintf(
'magnet:?xt=urn:btih:%s&dn=%s',
$infoHash,
rawurlencode($torrent->getName())
);
Append &tr= parameters with rawurlencode() per tracker. For hybrid torrents, expose both v1 and v2 hashes and let the UI display the appropriate one.
Use a service wrapper with narrow methods. A practical interface looks like:
interface TorrentService
{
public function inspect(string $path): TorrentMetadata;
public function createFromFiles(array $files, TorrentOptions $options): string;
public function updateTrackers(string $path, array $trackers): void;
}
This keeps Laravel code testable and makes a future package swap less painful.
Test with real fixture torrents. Keep small v1, v2, and hybrid .torrent fixtures in tests/Fixtures. Assert parsed names, file lists, tracker tiers, and hash values. Use Storage::fake() for creation tests, but note that piece hashing still needs real temporary files.
info dictionary bytes. If you decode and re-encode a non-canonical torrent, key ordering or integer formatting can change and produce a different hash. Use the library’s info-hash accessor rather than hashing a rebuilt string yourself.memory_limit and timeout.name.utf-8 or path.utf-8 unless the API exposes them.../, absolute-looking paths, or unusual characters. The library parses metadata; it does not make those names safe for your application..torrent that clients may fetch.announce alongside announce-list.composer.json. The package is actively maintained, but torrent metainfo details are compatibility-sensitive. Upgrade deliberately and rerun fixture tests.How can I help you explore Laravel packages today?