/ app / protocols / magnet-protocol.js
magnet-protocol.js
 1  import { Readable } from 'stream'
 2  
 3  const INFO_HASH_MATCH = /^urn:btih:([a-f0-9]{40})$/ig
 4  const PUBLIC_KEY_MATCH = /^urn:btpk:([a-f0-9]{64})$/ig
 5  
 6  export default async function createHandler () {
 7    return function magnetHandler (req, sendResponse) {
 8      try {
 9        const parsed = new URL(req.url)
10  
11        const xt = parsed.searchParams.get('xt')
12        const xs = parsed.searchParams.get('xs')
13  
14        if (xs) {
15          const match = PUBLIC_KEY_MATCH.exec(xs)
16          if (!match) {
17            return sendError('Magnet has no bittorrent infohash')
18          }
19          const publicKey = match[1]
20          const final = `bittorrent://${publicKey}`
21          sendFinal(final)
22        } else if (xt) {
23          const match = INFO_HASH_MATCH.exec(xt)
24          if (!match) {
25            return sendError('Magnet has no bittorrent infohash')
26          }
27          const infohash = match[1]
28          const final = `bittorrent://${infohash}/`
29          sendFinal(final)
30        } else {
31          return sendError('Magnet link has no `xt` or `xs` parameter')
32        }
33      } catch (e) {
34        sendError(e.stack)
35      }
36  
37      function sendFinal (Location) {
38        sendResponse({
39          statusCode: 308,
40          headers: {
41            Location
42          },
43          data: intoStream('')
44        })
45      }
46  
47      function sendError (message) {
48        sendResponse({
49          statusCode: 400,
50          headers: {
51            'content-type': 'text/html'
52          },
53          data: intoStream(message)
54        })
55      }
56    }
57  }
58  
59  function intoStream (data) {
60    return new Readable({
61      read () {
62        this.push(data)
63        this.push(null)
64      }
65    })
66  }