UNPKG

3.83 kBJavaScriptView Raw
1module.exports = magnetURIDecode
2module.exports.decode = magnetURIDecode
3module.exports.encode = magnetURIEncode
4
5var base32 = require('thirty-two')
6var extend = require('xtend')
7var uniq = require('uniq')
8
9/**
10 * Parse a magnet URI and return an object of keys/values
11 *
12 * @param {string} uri
13 * @return {Object} parsed uri
14 */
15function magnetURIDecode (uri) {
16 var result = {}
17 var data = uri.split('magnet:?')[1]
18
19 var params = (data && data.length >= 0)
20 ? data.split('&')
21 : []
22
23 params.forEach(function (param) {
24 var keyval = param.split('=')
25
26 // This keyval is invalid, skip it
27 if (keyval.length !== 2) return
28
29 var key = keyval[0]
30 var val = keyval[1]
31
32 // Clean up torrent name
33 if (key === 'dn') val = decodeURIComponent(val).replace(/\+/g, ' ')
34
35 // Address tracker (tr), exact source (xs), and acceptable source (as) are encoded
36 // URIs, so decode them
37 if (key === 'tr' || key === 'xs' || key === 'as' || key === 'ws') {
38 val = decodeURIComponent(val)
39 }
40
41 // Return keywords as an array
42 if (key === 'kt') val = decodeURIComponent(val).split('+')
43
44 // If there are repeated parameters, return an array of values
45 if (result[key]) {
46 if (Array.isArray(result[key])) {
47 result[key].push(val)
48 } else {
49 var old = result[key]
50 result[key] = [old, val]
51 }
52 } else {
53 result[key] = val
54 }
55 })
56
57 // Convenience properties for parity with `parse-torrent-file` module
58 var m
59 if (result.xt) {
60 var xts = Array.isArray(result.xt) ? result.xt : [ result.xt ]
61 xts.forEach(function (xt) {
62 if ((m = xt.match(/^urn:btih:(.{40})/))) {
63 result.infoHash = m[1].toLowerCase()
64 } else if ((m = xt.match(/^urn:btih:(.{32})/))) {
65 var decodedStr = base32.decode(m[1])
66 result.infoHash = new Buffer(decodedStr, 'binary').toString('hex')
67 }
68 })
69 }
70 if (result.infoHash) result.infoHashBuffer = new Buffer(result.infoHash, 'hex')
71
72 if (result.dn) result.name = result.dn
73 if (result.kt) result.keywords = result.kt
74
75 if (typeof result.tr === 'string') result.announce = [ result.tr ]
76 else if (Array.isArray(result.tr)) result.announce = result.tr
77 else result.announce = []
78
79 result.urlList = []
80 if (typeof result.as === 'string' || Array.isArray(result.as)) {
81 result.urlList = result.urlList.concat(result.as)
82 }
83 if (typeof result.ws === 'string' || Array.isArray(result.ws)) {
84 result.urlList = result.urlList.concat(result.ws)
85 }
86
87 uniq(result.announce)
88 uniq(result.urlList)
89
90 return result
91}
92
93function magnetURIEncode (obj) {
94 obj = extend(obj) // clone obj, so we can mutate it
95
96 // support using convenience names, in addition to spec names
97 // (example: `infoHash` for `xt`, `name` for `dn`)
98 if (obj.infoHashBuffer) obj.xt = 'urn:btih:' + obj.infoHashBuffer.toString('hex')
99 if (obj.infoHash) obj.xt = 'urn:btih:' + obj.infoHash
100 if (obj.name) obj.dn = obj.name
101 if (obj.keywords) obj.kt = obj.keywords
102 if (obj.announce) obj.tr = obj.announce
103 if (obj.urlList) {
104 obj.ws = obj.urlList
105 delete obj.as
106 }
107
108 var result = 'magnet:?'
109 Object.keys(obj)
110 .filter(function (key) {
111 return key.length === 2
112 })
113 .forEach(function (key, i) {
114 var values = Array.isArray(obj[key]) ? obj[key] : [ obj[key] ]
115 values.forEach(function (val, j) {
116 if ((i > 0 || j > 0) && (key !== 'kt' || j === 0)) result += '&'
117
118 if (key === 'dn') val = encodeURIComponent(val).replace(/%20/g, '+')
119 if (key === 'tr' || key === 'xs' || key === 'as' || key === 'ws') {
120 val = encodeURIComponent(val)
121 }
122 if (key === 'kt') val = encodeURIComponent(val)
123
124 if (key === 'kt' && j > 0) result += '+' + val
125 else result += key + '=' + val
126 })
127 })
128
129 return result
130}