Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

magnetizer

Package Overview
Dependencies
Maintainers
1
Versions
15
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

magnetizer - npm Package Compare versions

Comparing version 1.1.3 to 2.0.0

29

package.json
{
"name": "magnetizer",
"version": "1.1.3",
"description": "Library for decoding and encoding magnet links.",
"version": "2.0.0",
"description": "Library for decoding and encoding magnet links",
"main": "src/index.js",
"scripts": {
"build": "./node_modules/.bin/tsc",
"lint": "./node_modules/tslint/bin/tslint ./src/**/*.ts",
"test": "./node_modules/.bin/jest --runInBand",
"lint": "eslint src/**/*.ts",
"test": "npm run lint && ./node_modules/.bin/jest --runInBand",
"prepare": "npm run build",

@@ -15,3 +15,3 @@ "prepublishOnly": "npm run lint && npm test"

"type": "git",
"url": "git+https://github.com/IvanSolomakhin/magnetizer.git"
"url": "git+https://github.com/isolomak/magnetizer.git"
},

@@ -37,5 +37,5 @@ "keywords": [

"bugs": {
"url": "https://github.com/IvanSolomakhin/magnetizer/issues"
"url": "https://github.com/isolomak/magnetizer/issues"
},
"homepage": "https://github.com/IvanSolomakhin/magnetizer#readme",
"homepage": "https://github.com/isolomak/magnetizer#readme",
"dependencies": {

@@ -45,8 +45,11 @@ "hi-base32": "^0.5.0"

"devDependencies": {
"@types/jest": "^25.1.4",
"@types/node": "^13.9.8",
"jest": "^25.2.4",
"ts-jest": "^25.3.0",
"tslint": "^6.1.1",
"typescript": "^3.8.3"
"@types/jest": "^29.2.3",
"@types/node": "^18.11.10",
"@typescript-eslint/eslint-plugin": "^5.45.0",
"@typescript-eslint/parser": "^5.45.0",
"eslint": "^8.29.0",
"eslint-plugin-jest": "^27.1.6",
"jest": "^29.3.1",
"ts-jest": "^29.0.3",
"typescript": "^4.9.3"
},

@@ -53,0 +56,0 @@ "types": "./lib/index.d.ts",

@@ -0,3 +1,4 @@

# Magnetizer
[![NPM](https://nodei.co/npm/magnetizer.png)](https://npmjs.org/package/magnetizer)
<!-- [![NPM](https://nodei.co/npm/magnetizer.png)](https://npmjs.org/package/magnetizer) -->

@@ -9,67 +10,61 @@ ![ci](https://github.com/IvanSolomakhin/magnetizer/workflows/ci/badge.svg)

Library for decoding and encoding [magnet links](https://en.wikipedia.org/wiki/Magnet_URI_scheme).
Fast and easy to use.
Written in TypeScript.
Fully tested with 100% code coverage.
Library for decoding and encoding [magnet links](https://en.wikipedia.org/wiki/Magnet_URI_scheme)
Fast and easy to use
Written in TypeScript
Fully tested with 100% code coverage
----
## Installation
| npm | yarn |
|---|---|
| `npm install --save magnetizer` | `yarn add magnetizer` |
``` bash
npm install --save magnetizer
```
----
## Getting Started
##### Import library
| typescript | javascript |
|---|---|
| ` import magnetizer from 'magnetizer' ` | ` const magnetizer = require('magnetizer') `|
### **Decode** magnet link
##### Decode magnet link
``` typescript
import magnetizer from 'magnetizer';
const magnetUri = magnetizer.decode(magnetLink);
```
magnetizer.decode('magnet:?dn=test-name_for_magnet-link.tar.gz&xl=100500&xt=urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a&tr=http%3A%2F%2Ftracker.example.org%2Fannounce.php%3Fua%3D1111111111&tr=wss%3A%2F%2Ftracker.webtorrent.io&kt=martin+luther+king+mp3&ws=http%3A%2F%2Fdownload.wikimedia.org%2Fmediawiki%2F1.15%2Fmediawiki-1.15.1.tar.gz&as=http%3A%2F%2Fdownload.wikimedia.org%2Fmediawiki%2F1.15%2Fmediawiki-1.15.1.tar.gz&xs=http%3A%2F%2Fcache.example.org%2FXRX2PEFXOOEJFRVUCX6HMZMKS5TWG4K5&mt=http%3A%2F%2Fweblog.foo%2Fall-my-favorites.rss');
// {
// displayNames: [ 'test-name_for_magnet-link.tar.gz' ],
// length: 100500,
// infoHashes: [ 'urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a' ],
// webSeeds: [ 'http://download.wikimedia.org/mediawiki/1.15/mediawiki-1.15.1.tar.gz' ],
// acceptableSources: [ 'http://download.wikimedia.org/mediawiki/1.15/mediawiki-1.15.1.tar.gz' ],
// sources: [ 'http://cache.example.org/XRX2PEFXOOEJFRVUCX6HMZMKS5TWG4K5' ],
// keywords: [ 'martin', 'luther', 'king', 'mp3' ],
// manifest: 'http://weblog.foo/all-my-favorites.rss',
// trackers: [
// 'http://tracker.example.org/announce.php?ua=1111111111',
// 'wss://tracker.webtorrent.io',
// ],
// }
```
##### Encode magnet link
```
magnetizer.encode({
displayNames: [ 'test-name_for_magnet-link.tar.gz' ],
### **Encode** magnet link
``` typescript
import magnetizer from 'magnetizer';
const magnetLink = magnetizer.encode({
displayName: 'test-name_for_magnet-link.tar.gz',
length: 100500,
infoHashes: [ 'urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a' ],
webSeeds: [ 'http://download.wikimedia.org/mediawiki/1.15/mediawiki-1.15.1.tar.gz' ],
acceptableSources: [ 'http://download.wikimedia.org/mediawiki/1.15/mediawiki-1.15.1.tar.gz' ],
sources: [ 'http://cache.example.org/XRX2PEFXOOEJFRVUCX6HMZMKS5TWG4K5' ],
keywords: [ 'martin', 'luther', 'king', 'mp3' ],
manifest: 'http://weblog.foo/all-my-favorites.rss',
infoHashes: [ 'c12fe1c06bba254a9dc9f519b335aa7c1367a88a' ],
webSeeds: [ 'http://example.com/test-name_for_magnet-link.tar.gz' ],
acceptableSources: [ 'http://example.com/test-name_for_magnet-link.tar.gz' ],
sources: [ 'http://cache.example.com/c12fe1c06bba254a9dc9f519b335aa7c1367a88a' ],
keywords: [ 'test-name_for_magnet-link', 'tar', 'gz' ],
manifest: 'http://example.com/manifest',
trackers: [
'http://tracker.example.org/announce.php?ua=1111111111',
'http://tracker.example.com/announce.php?ua=1111111111',
'wss://tracker.webtorrent.io',
],
});
// 'magnet:?dn=test-name_for_magnet-link.tar.gz&xl=100500&xt=urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a&tr=http%3A%2F%2Ftracker.example.org%2Fannounce.php%3Fua%3D1111111111&tr=wss%3A%2F%2Ftracker.webtorrent.io&kt=martin+luther+king+mp3&ws=http%3A%2F%2Fdownload.wikimedia.org%2Fmediawiki%2F1.15%2Fmediawiki-1.15.1.tar.gz&as=http%3A%2F%2Fdownload.wikimedia.org%2Fmediawiki%2F1.15%2Fmediawiki-1.15.1.tar.gz&xs=http%3A%2F%2Fcache.example.org%2FXRX2PEFXOOEJFRVUCX6HMZMKS5TWG4K5&mt=http%3A%2F%2Fweblog.foo%2Fall-my-favorites.rss'
```
----
## Tests
```
npm test
```
``` bash
npm test
```
----
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
import * as base32 from 'hi-base32';
import { HASH, MAGNET_PARAMETER, MagnetURI } from '../types';
import { IMagnetURI, MAGNET_INFO_HASH_TYPE, MAGNET_PARAMETER } from '../types';
/*
TODO: The standard also allows for multiple parameters of the same type to be used by appending ".1", ".2", etc. to the parameter name,
e.g.: magnet:?xt.1=urn:sha1:YNCKHTQCWBTRNJIV4WNAE52SJUQCZO5C&xt.2=urn:sha1:TXGCZQTH26NL6OUQAJJPFALHG2LTGBC7
*/
interface IMagnetDecodeURI {
displayName: string | null;
length: number | null;
manifest: string | null;
infoHashes: Set<string>;
webSeeds: Set<string>;
acceptableSources: Set<string>;
sources: Set<string>;
keywords: Set<string>;
trackers: Set<string>;
}
export default class MagnetDecoder {
/**
* Split megnet uri
*/
private static _splitMagnetURI(magnetURI: string): Array<string> {
return magnetURI.replace('magnet:?', '').split('&');
}
private _decodedMagnetURI: IMagnetDecodeURI;
private readonly _decodedMagnetURI: MagnetURI;
/**

@@ -24,15 +24,16 @@ * Constructor

constructor() {
this._decodedMagnetURI = { };
this._decodedMagnetURI = MagnetDecoder._reset();
}
/**
* Decode magnet uri
*/
public decode(magnetURI: string): MagnetURI {
public decode(magnetURI: string): IMagnetURI {
this._decodedMagnetURI = MagnetDecoder._reset();
if (!magnetURI.startsWith('magnet:?')) {
return this._decodedMagnetURI;
return this._getResult();
}
const parametersList = MagnetDecoder._splitMagnetURI(magnetURI);
const parametersList = magnetURI.replace('magnet:?', '').split('&');

@@ -43,3 +44,3 @@ for (const parameter of parametersList) {

return this._decodedMagnetURI;
return this._getResult();
}

@@ -50,3 +51,3 @@

*/
private _decodeParameter(param: string) {
private _decodeParameter(param: string): void {
const [ key, value ] = param.split('=');

@@ -85,7 +86,4 @@

*/
private _addDisplayName(file: string) {
this._decodedMagnetURI.displayNames = this._decodedMagnetURI.displayNames || [];
this._decodedMagnetURI.displayNames.push(
decodeURIComponent(file).replace(/\+/g, ' ')
);
private _addDisplayName(file: string): void {
this._decodedMagnetURI.displayName = decodeURIComponent(file).replace(/\+/g, ' ');
}

@@ -96,3 +94,3 @@

*/
private _addLength(length: string) {
private _addLength(length: string): void {
this._decodedMagnetURI.length = parseInt(length, 10);

@@ -104,5 +102,3 @@ }

*/
private _addInfoHash(urnValue: string) {
this._decodedMagnetURI.infoHashes = this._decodedMagnetURI.infoHashes || [];
private _addInfoHash(urnValue: string): void {
const [ urn, type, hash ] = urnValue.split(':');

@@ -114,5 +110,5 @@

if (type === HASH.BIT_TORRENT_INFO_HASH) {
if (type === MAGNET_INFO_HASH_TYPE.BIT_TORRENT_INFO_HASH) {
if (hash.length === 40) {
this._decodedMagnetURI.infoHashes.push(
this._decodedMagnetURI.infoHashes.add(
`${urn}:${type}:${hash.toLowerCase()}`

@@ -122,3 +118,3 @@ );

if (hash.length === 32) {
this._decodedMagnetURI.infoHashes.push(
this._decodedMagnetURI.infoHashes.add(
`${urn}:${type}:${Buffer.from(base32.decode.asBytes(hash)).toString('hex')}`

@@ -135,5 +131,4 @@ );

*/
private _addTracker(tracker: string) {
this._decodedMagnetURI.trackers = this._decodedMagnetURI.trackers || [];
this._decodedMagnetURI.trackers.push(
private _addTracker(tracker: string): void {
this._decodedMagnetURI.trackers.add(
decodeURIComponent(tracker)

@@ -146,4 +141,8 @@ );

*/
private _addKeywords(keywords: string) {
this._decodedMagnetURI.keywords = decodeURIComponent(keywords).split('+');
private _addKeywords(keywords: string): void {
const decodedKeywords = decodeURIComponent(keywords).split('+');
for (const keyword of decodedKeywords) {
this._decodedMagnetURI.keywords.add(keyword);
}
}

@@ -154,5 +153,4 @@

*/
private _addWebSeed(webSeed: string) {
this._decodedMagnetURI.webSeeds = this._decodedMagnetURI.webSeeds || [];
this._decodedMagnetURI.webSeeds.push(
private _addWebSeed(webSeed: string): void {
this._decodedMagnetURI.webSeeds.add(
decodeURIComponent(webSeed)

@@ -165,5 +163,4 @@ );

*/
private _addAcceptableSource(source: string) {
this._decodedMagnetURI.acceptableSources = this._decodedMagnetURI.acceptableSources || [];
this._decodedMagnetURI.acceptableSources.push(
private _addAcceptableSource(source: string): void {
this._decodedMagnetURI.acceptableSources.add(
decodeURIComponent(source)

@@ -176,5 +173,4 @@ );

*/
private _addSource(source: string) {
this._decodedMagnetURI.sources = this._decodedMagnetURI.sources || [];
this._decodedMagnetURI.sources.push(
private _addSource(source: string): void {
this._decodedMagnetURI.sources.add(
decodeURIComponent(source)

@@ -187,6 +183,34 @@ );

*/
private _addManifest(manifest: string) {
private _addManifest(manifest: string): void {
this._decodedMagnetURI.manifest = decodeURIComponent(manifest);
}
private static _reset(): IMagnetDecodeURI {
return {
displayName: null,
length: null,
infoHashes: new Set(),
webSeeds: new Set(),
acceptableSources: new Set(),
sources: new Set(),
keywords: new Set(),
manifest: null,
trackers: new Set(),
};
}
private _getResult(): IMagnetURI {
return {
displayName: this._decodedMagnetURI.displayName,
length: this._decodedMagnetURI.length,
manifest: this._decodedMagnetURI.manifest,
infoHashes: Array.from(this._decodedMagnetURI.infoHashes),
webSeeds: Array.from(this._decodedMagnetURI.webSeeds),
acceptableSources: Array.from(this._decodedMagnetURI.acceptableSources),
sources: Array.from(this._decodedMagnetURI.sources),
keywords: Array.from(this._decodedMagnetURI.keywords),
trackers: Array.from(this._decodedMagnetURI.trackers),
};
}
}

@@ -1,6 +0,5 @@

import { MAGNET_PARAMETER, MagnetURI } from '../types';
import { IMagnetURI, MAGNET_PARAMETER } from '../types';
export default class MagnetEncoder {
private _data: MagnetURI;
private _encodedParameters: Array<string>;

@@ -11,4 +10,3 @@

*/
constructor(data: MagnetURI) {
this._data = data;
constructor() {
this._encodedParameters = [];

@@ -20,12 +18,12 @@ }

*/
public encode() {
this._encodeDisplayName();
this._encodeLength();
this._encodeInfoHash();
this._encodeTracker();
this._encodeKeywords();
this._encodeWebSeed();
this._encodeAcceptableSource();
this._encodeSource();
this._encodeManifest();
public encode(data: Partial<IMagnetURI>): string {
this._encodeDisplayName(data);
this._encodeLength(data);
this._encodeInfoHashes(data);
this._encodeTrackers(data);
this._encodeKeywords(data);
this._encodeWebSeeds(data);
this._encodeAcceptableSources(data);
this._encodeSources(data);
this._encodeManifest(data);

@@ -38,12 +36,12 @@ return `magnet:?${this._encodedParameters.join('&')}`;

*/
private _encodeDisplayName() {
if (!this._data.displayNames) {
private _encodeDisplayName(data: Partial<IMagnetURI>): void {
const displayName = data.displayName?.trim();
if (!displayName) {
return ;
}
for (const displayName of this._data.displayNames) {
this._encodedParameters.push(
`${MAGNET_PARAMETER.DISPLAY_NAME}=${encodeURIComponent(displayName)}`
);
}
this._encodedParameters.push(
`${MAGNET_PARAMETER.DISPLAY_NAME}=${encodeURIComponent(displayName)}`
);
}

@@ -54,4 +52,6 @@

*/
private _encodeLength() {
if (this._data.length === null || this._data.length === undefined) {
private _encodeLength(data: Partial<IMagnetURI>): void {
const length = data.length;
if (length === null || length === undefined) {
return ;

@@ -61,3 +61,3 @@ }

this._encodedParameters.push(
`${MAGNET_PARAMETER.LENGTH}=${this._data.length}`
`${MAGNET_PARAMETER.LENGTH}=${length}`
);

@@ -67,29 +67,40 @@ }

/**
* Encode info hash
* Encode info hashes
*/
private _encodeInfoHash() {
if (!this._data.infoHashes) {
return ;
private _encodeInfoHashes(data: Partial<IMagnetURI>): void {
const encodedHashesSet = new Set<string>();
for (const infoHash of data.infoHashes || []) {
const providedInfoHash = Buffer.isBuffer(infoHash)
? infoHash.toString('hex')
: infoHash;
if (!providedInfoHash.startsWith('urn:')) {
encodedHashesSet.add(
`${MAGNET_PARAMETER.INFO_HASH}=urn:btih:${providedInfoHash}`
);
}
else {
encodedHashesSet.add(
`${MAGNET_PARAMETER.INFO_HASH}=${providedInfoHash}`
);
}
}
for (const infoHash of this._data.infoHashes) {
this._encodedParameters.push(
`${MAGNET_PARAMETER.INFO_HASH}=${infoHash}`
);
}
this._encodedParameters.push(...Array.from(encodedHashesSet));
}
/**
* Encode encode tracker
* Encode trackers
*/
private _encodeTracker() {
if (!this._data.trackers) {
return ;
}
private _encodeTrackers(data: Partial<IMagnetURI>): void {
const encodedTrackersSet = new Set<string>();
for (const tracker of this._data.trackers) {
this._encodedParameters.push(
`${MAGNET_PARAMETER.TRACKER}=${encodeURIComponent(tracker)}`
for (const trackerUrl of data.trackers || []) {
encodedTrackersSet.add(
`${MAGNET_PARAMETER.TRACKER}=${encodeURIComponent(trackerUrl)}`
);
}
this._encodedParameters.push(...Array.from(encodedTrackersSet));
}

@@ -100,55 +111,61 @@

*/
private _encodeKeywords() {
if (!this._data.keywords || !this._data.keywords.length) {
return ;
private _encodeKeywords(data: Partial<IMagnetURI>): void {
const encodedKeywords = new Set<string>();
for (const keyword of data.keywords || []) {
encodedKeywords.add(
encodeURIComponent(keyword)
);
}
this._encodedParameters.push(
`${MAGNET_PARAMETER.KEYWORD}=${this._data.keywords.map(encodeURIComponent).join('+')}`
);
if (encodedKeywords.size) {
this._encodedParameters.push(
`${MAGNET_PARAMETER.KEYWORD}=${Array.from(encodedKeywords).join('+')}`
);
}
}
/**
* Encode web seed
* Encode web seeds
*/
private _encodeWebSeed() {
if (!this._data.webSeeds) {
return ;
}
private _encodeWebSeeds(data: Partial<IMagnetURI>): void {
const encodedWebSeeds = new Set<string>();
for (const webSeed of this._data.webSeeds) {
this._encodedParameters.push(
for (const webSeed of data.webSeeds || []) {
encodedWebSeeds.add(
`${MAGNET_PARAMETER.WEB_SEED}=${encodeURIComponent(webSeed)}`
);
}
this._encodedParameters.push(...Array.from(encodedWebSeeds));
}
/**
* Encode acceptable source
* Encode acceptable sources
*/
private _encodeAcceptableSource() {
if (!this._data.acceptableSources) {
return ;
}
private _encodeAcceptableSources(data: Partial<IMagnetURI>): void {
const encodedAcceptableSources = new Set<string>();
for (const source of this._data.acceptableSources) {
this._encodedParameters.push(
for (const source of data.acceptableSources || []) {
encodedAcceptableSources.add(
`${MAGNET_PARAMETER.ACCEPTABLE_SOURCE}=${encodeURIComponent(source)}`
);
}
this._encodedParameters.push(...Array.from(encodedAcceptableSources));
}
/**
* Encode source
* Encode sources
*/
private _encodeSource() {
if (!this._data.sources) {
return ;
}
private _encodeSources(data: Partial<IMagnetURI>): void {
const encodedSources = new Set<string>();
for (const source of this._data.sources) {
this._encodedParameters.push(
for (const source of data.sources || []) {
encodedSources.add(
`${MAGNET_PARAMETER.SOURCE}=${encodeURIComponent(source)}`
);
}
this._encodedParameters.push(...Array.from(encodedSources));
}

@@ -159,4 +176,6 @@

*/
private _encodeManifest() {
if (!this._data.manifest) {
private _encodeManifest(data: Partial<IMagnetURI>): void {
const manifest = data.manifest?.trim();
if (!manifest) {
return ;

@@ -166,3 +185,3 @@ }

this._encodedParameters.push(
`${MAGNET_PARAMETER.MANIFEST}=${encodeURIComponent(this._data.manifest)}`
`${MAGNET_PARAMETER.MANIFEST}=${encodeURIComponent(manifest)}`
);

@@ -169,0 +188,0 @@ }

@@ -1,14 +0,16 @@

import { MagnetURI } from './types';
import MagnetDecoder from './decode/MagnetDecoder';
import MagnetEncoder from './encode/MagnetEncoder';
import { IMagnetURI } from './types';
function decode(magnetURI: string) {
return new MagnetDecoder().decode(magnetURI);
function decode(magnetURI: string): IMagnetURI {
const magnetDecoder = new MagnetDecoder();
return magnetDecoder.decode(magnetURI);
}
function encode(data: MagnetURI) {
return new MagnetEncoder(data).encode();
function encode(data: Partial<IMagnetURI>): string {
const magnetEncoder = new MagnetEncoder();
return magnetEncoder.encode(data);
}
export { decode, encode };
export { decode, encode, IMagnetURI };
export default { decode, encode };

@@ -1,6 +0,3 @@

/**
* https://en.wikipedia.org/wiki/Magnet_URI_scheme
*/
export enum HASH {
export enum MAGNET_INFO_HASH_TYPE {
TIGER_TREE_HASH = 'tree', // TODO

@@ -28,12 +25,43 @@ SECURE_HASH_ALGORITHM_1 = 'sha1', // TODO

export interface MagnetURI {
displayNames?: Array<string>; // a filename to display to the user, for convenience
length?: number; // size in bytes
infoHashes?: Array<string>; // URN containing file hash
webSeeds?: Array<string>; // the payload data served over HTTP(S)
acceptableSources?: Array<string>; // web link to the file online
sources?: Array<string>; // P2P link identified by a content-hash
keywords?: Array<string>; // a more general search, specifying keywords, rather than a particular file
manifest?: string; // link to the metafile that contains a list of magneto (MAGMA – MAGnet MAnifest)
trackers?: Array<string>; // tracker URL for BitTorrent downloads
/**
* Magnet URI scheme
* wiki https://en.wikipedia.org/wiki/Magnet_URI_scheme
*/
export interface IMagnetURI {
/**
* A filename to display to the user, for convenience
*/
displayName: string | null;
/**
* Size in bytes
*/
length: number | null;
/**
* URN containing file hash
*/
infoHashes: Array<string | Buffer>;
/**
* the payload data served over HTTP(S)
*/
webSeeds: Array<string>;
/**
* Web link to the file online
*/
acceptableSources: Array<string>;
/**
* P2P link identified by a content-hash
*/
sources: Array<string>;
/**
* A more general search, specifying keywords, rather than a particular file
*/
keywords: Array<string>;
/**
* Link to the metafile that contains a list of magneto (MAGMA – MAGnet MAnifest)
*/
manifest: string | null;
/**
* Tracker URL for BitTorrent downloads
*/
trackers: Array<string>;
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc