UNPKG

3.65 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 = new Buffer(m[1], 'hex').toString('hex')
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
71 if (result.dn) result.name = result.dn
72 if (result.kt) result.keywords = result.kt
73
74 if (typeof result.tr === 'string') result.announce = [ result.tr ]
75 else if (Array.isArray(result.tr)) result.announce = result.tr
76 else result.announce = []
77
78 uniq(result.announce)
79
80 result.urlList = []
81 if (typeof result.as === 'string' || Array.isArray(result.as)) {
82 result.urlList = result.urlList.concat(result.as)
83 }
84 if (typeof result.ws === 'string' || Array.isArray(result.ws)) {
85 result.urlList = result.urlList.concat(result.ws)
86 }
87
88 return result
89}
90
91function magnetURIEncode (obj) {
92 obj = extend(obj) // clone obj, so we can mutate it
93
94 // support official magnet key names and convenience names
95 // (example: `infoHash` for `xt`, `name` for `dn`)
96 if (obj.infoHash) obj.xt = 'urn:btih:' + obj.infoHash
97 if (obj.name) obj.dn = obj.name
98 if (obj.keywords) obj.kt = obj.keywords
99 if (obj.announce) obj.tr = obj.announce
100 if (obj.urlList) {
101 obj.ws = obj.urlList
102 delete obj.as
103 }
104
105 var result = 'magnet:?'
106 Object.keys(obj)
107 .filter(function (key) {
108 return key.length === 2
109 })
110 .forEach(function (key, i) {
111 var values = Array.isArray(obj[key]) ? obj[key] : [ obj[key] ]
112 values.forEach(function (val, j) {
113 if ((i > 0 || j > 0) && (key !== 'kt' || j === 0)) result += '&'
114
115 if (key === 'dn') val = encodeURIComponent(val).replace(/%20/g, '+')
116 if (key === 'tr' || key === 'xs' || key === 'as' || key === 'ws') {
117 val = encodeURIComponent(val)
118 }
119 if (key === 'kt') val = encodeURIComponent(val)
120
121 if (key === 'kt' && j > 0) result += '+' + val
122 else result += key + '=' + val
123 })
124 })
125
126 return result
127}