PageSourceSearch

https://btorrent.xyz/app.js

js btorrent.xyz collected 2026-09-25 21:50:06 UTC 10,864 bytes, 364 lines download raw bytes

1/* global WebTorrent, angular, moment, prompt */
2import WebTorrent from 'https://cdn.jsdelivr.net/npm/webtorrent@3/dist/webtorrent.min.js'
3
4const VERSION = '2'
5const trackers = ['wss://tracker.btorrent.xyz', 'wss://tracker.openwebtorrent.com', 'wss://open.ftorrent.com']
6const rtcConfig = {
7  'iceServers': [
8    {
9      'urls': ['stun:stun.l.google.com:19305', 'stun:stun1.l.google.com:19305']
10    }
11  ]
12}
13
14const torrentOpts = {
15  announce: trackers
16}
17
18const trackerOpts = {
19  announce: trackers,
20  rtcConfig: rtcConfig
21}
22
23const debug = window.localStorage.getItem('debug') !== null
24
25const dbg = function (string, item, color) {
26  color = color !== null ? color : '#333333'
27  if (debug) {
28    if (item && item.name) {
29      return console.debug(`%cβTorrent:${item.infoHash !== null ? 'torrent ' : 'torrent ' + item._torrent.name + ':file '}${item.name}${item.infoHash !== null ? ' (' + item.infoHash + ')' : ''} %c${string}`, 'color: #33C3F0', `color: ${color}`)
30    } else {
31      return console.debug(`%cβTorrent:client %c${string}`, 'color: #33C3F0', `color: ${color}`)
32    }
33  }
34}
35
36const er = function (err, item) { dbg(err, item, '#FF0000') }
37
38dbg(`Starting v${VERSION}. WebTorrent ${WebTorrent.VERSION}`)
39
40const client = new WebTorrent({
41  tracker: trackerOpts
42})
43
44const app = angular.module('BTorrent',
45  ['ngRoute', 'ui.grid', 'ui.grid.resizeColumns', 'ui.grid.selection', 'ngFileUpload', 'ngNotify'],
46  ['$compileProvider', '$locationProvider', '$routeProvider', function ($compileProvider, $locationProvider, $routeProvider) {
47    $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|magnet|blob|javascript):/)
48    $locationProvider.html5Mode({
49      enabled: true,
50      requireBase: false
51    }).hashPrefix('#')
52    $routeProvider.when('/view', {
53      templateUrl: 'views/view.html',
54      controller: 'ViewCtrl'
55    }).when('/download', {
56      templateUrl: 'views/download.html',
57      controller: 'DownloadCtrl'
58    }).otherwise({
59      templateUrl: 'views/full.html',
60      controller: 'FullCtrl'
61    })
62  }]
63)
64
65app.controller('BTorrentCtrl', ['$scope', '$rootScope', '$http', '$log', '$location', 'ngNotify', function ($scope, $rootScope, $http, $log, $location, ngNotify) {
66  let updateAll
67  $rootScope.version = VERSION
68  $rootScope.webtorrentVersion = WebTorrent.VERSION
69  ngNotify.config({
70    duration: 5000,
71    html: true
72  })
73  if (!WebTorrent.WEBRTC_SUPPORT) {
74    $rootScope.disabled = true
75    ngNotify.set('Please use a WebRTC compatible browser', {
76      type: 'error',
77      sticky: true,
78      button: false
79    })
80  }
81  $rootScope.client = client
82  updateAll = function () {
83    if ($rootScope.client.processing) {
84      return
85    }
86    $rootScope.$apply()
87  }
88  setInterval(updateAll, 500)
89  $rootScope.seedFiles = function (files) {
90    let name
91    if ((files != null) && files.length > 0) {
92      if (files.length === 1) {
93        dbg(`Seeding file ${files[0].name}`)
94      } else {
95        dbg(`Seeding ${files.length} files`)
96        name = prompt('Please name your torrent', 'My Awesome Torrent') || 'My Awesome Torrent'
97        torrentOpts.name = name
98      }
99      $rootScope.client.processing = true
100      $rootScope.client.seed(files, torrentOpts, $rootScope.onSeed)
101      delete torrentOpts.name
102    }
103  }
104  $rootScope.openTorrentFile = function (file) {
105    if (file != null) {
106      dbg(`Adding torrent file ${file.name}`)
107      $rootScope.client.processing = true
108      $rootScope.client.add(file, torrentOpts, $rootScope.onTorrent)
109    }
110  }
111  $rootScope.client.on('error', function (err, torrent) {
112    $rootScope.client.processing = false
113    ngNotify.set(err, 'error')
114    er(err, torrent)
115  })
116  $rootScope.addMagnet = function (magnet, onTorrent) {
117    if ((magnet != null) && magnet.length > 0) {
118      dbg(`Adding magnet/hash ${magnet}`)
119      $rootScope.client.processing = true
120      $rootScope.client.add(magnet, torrentOpts, onTorrent || $rootScope.onTorrent)
121    }
122  }
123  $rootScope.destroyedTorrent = function (err) {
124    if (err) {
125      throw err
126    }
127    dbg('Destroyed torrent', $rootScope.selectedTorrent)
128    $rootScope.selectedTorrent = null
129    $rootScope.client.processing = false
130  }
131  $rootScope.changePriority = function (file) {
132    if (file.priority === '-1') {
133      dbg('Deselected', file)
134      file.deselect()
135    } else {
136      dbg(`Selected with priority ${file.priority}`, file)
137      file.select(file.priority)
138    }
139  }
140  $rootScope.onTorrent = function (torrent, isSeed) {
141    dbg(torrent.magnetURI)
142    torrent.safeTorrentFileURL = URL.createObjectURL(torrent.torrentFileBlob)
143    torrent.fileName = `${torrent.name}.torrent`
144    if (!isSeed) {
145      dbg('Received metadata', torrent)
146      ngNotify.set(`Received ${torrent.name} metadata`)
147      if (!($rootScope.selectedTorrent != null)) {
148        $rootScope.selectedTorrent = torrent
149      }
150      $rootScope.client.processing = false
151    }
152    torrent.files.forEach(async function (file) {
153      if (isSeed) {
154        dbg('Started seeding', torrent)
155        if (!($rootScope.selectedTorrent != null)) {
156          $rootScope.selectedTorrent = torrent
157        }
158        $rootScope.client.processing = false
159      }
160      const blob = await file.blob()
161      file.url = URL.createObjectURL(blob)
162    })
163    torrent.on('done', function () {
164      if (!isSeed) {
165        dbg('Done', torrent)
166      }
167      ngNotify.set(`<b>${torrent.name}</b> has finished downloading`, 'success')
168    })
169    torrent.on('wire', function (wire, addr) { dbg(`Wire ${addr}`, torrent) })
170    torrent.on('error', er)
171  }
172  $rootScope.onSeed = function (torrent) { $rootScope.onTorrent(torrent, true) }
173  dbg('Ready')
174}
175])
176
177app.controller('FullCtrl', ['$scope', '$rootScope', '$http', '$log', '$location', 'ngNotify', function ($scope, $rootScope, $http, $log, $location, ngNotify) {
178  ngNotify.config({
179    duration: 5000,
180    html: true
181  })
182  $scope.addMagnet = function () {
183    $rootScope.addMagnet($scope.torrentInput)
184    $scope.torrentInput = ''
185  }
186  $scope.columns = [
187    {
188      field: 'name',
189      cellTooltip: true,
190      minWidth: '200'
191    }, {
192      field: 'length',
193      name: 'Size',
194      cellFilter: 'pbytes',
195      width: '80'
196    }, {
197      field: 'received',
198      displayName: 'Downloaded',
199      cellFilter: 'pbytes',
200      width: '135'
201    }, {
202      field: 'downloadSpeed',
203      displayName: '↓ Speed',
204      cellFilter: 'pbytes:1',
205      width: '100'
206    }, {
207      field: 'progress',
208      displayName: 'Progress',
209      cellFilter: 'progress',
210      width: '100'
211    }, {
212      field: 'timeRemaining',
213      displayName: 'ETA',
214      cellFilter: 'humanTime',
215      width: '140'
216    }, {
217      field: 'uploaded',
218      displayName: 'Uploaded',
219      cellFilter: 'pbytes',
220      width: '125'
221    }, {
222      field: 'uploadSpeed',
223      displayName: '↑ Speed',
224      cellFilter: 'pbytes:1',
225      width: '100'
226    }, {
227      field: 'numPeers',
228      displayName: 'Peers',
229      width: '80'
230    }, {
231      field: 'ratio',
232      cellFilter: 'number:2',
233      width: '80'
234    }
235  ]
236  $scope.gridOptions = {
237    columnDefs: $scope.columns,
238    data: $rootScope.client.torrents,
239    enableColumnResizing: true,
240    enableColumnMenus: false,
241    enableRowSelection: true,
242    enableRowHeaderSelection: false,
243    multiSelect: false
244  }
245  $scope.gridOptions.onRegisterApi = function (gridApi) {
246    $scope.gridApi = gridApi
247    gridApi.selection.on.rowSelectionChanged($scope, function (row) {
248      if (!row.isSelected && ($rootScope.selectedTorrent != null) && ($rootScope.selectedTorrent.infoHash === row.entity.infoHash)) {
249        $rootScope.selectedTorrent = null
250      } else {
251        $rootScope.selectedTorrent = row.entity
252      }
253    })
254  }
255  if ($location.hash() !== '') {
256    $rootScope.client.processing = true
257    setTimeout(function () {
258      dbg(`Adding ${$location.hash()}`)
259      $rootScope.addMagnet($location.hash())
260    }, 0)
261  }
262}
263])
264
265app.controller('DownloadCtrl', ['$scope', '$rootScope', '$http', '$log', '$location', 'ngNotify', function ($scope, $rootScope, $http, $log, $location, ngNotify) {
266  ngNotify.config({
267    duration: 5000,
268    html: true
269  })
270  $scope.addMagnet = function () {
271    $rootScope.addMagnet($scope.torrentInput)
272    $scope.torrentInput = ''
273  }
274  if ($location.hash() !== '') {
275    $rootScope.client.processing = true
276    setTimeout(function () {
277      dbg(`Adding ${$location.hash()}`)
278      $rootScope.addMagnet($location.hash())
279    }, 0)
280  }
281}
282])
283
284app.controller('ViewCtrl', ['$scope', '$rootScope', '$http', '$log', '$location', 'ngNotify', function ($scope, $rootScope, $http, $log, $location, ngNotify) {
285  let onTorrent
286  ngNotify.config({
287    duration: 2000,
288    html: true
289  })
290  onTorrent = function (torrent) {
291    $rootScope.viewerStyle = {
292      'margin-top': '-20px',
293      'text-align': 'center'
294    }
295    dbg(torrent.magnetURI)
296    torrent.safeTorrentFileURL = URL.createObjectURL(torrent.torrentFileBlob)
297    torrent.fileName = `${torrent.name}.torrent`
298    $rootScope.selectedTorrent = torrent
299    $rootScope.client.processing = false
300    dbg('Received metadata', torrent)
301    ngNotify.set(`Received ${torrent.name} metadata`)
302    torrent.files.forEach(function (file) {
303      file.streamTo(document.querySelector('#viewer'))
304      file.on('done', async function () {
305        dbg('Done ', file)
306        const blob = await file.blob()
307        file.url = URL.createObjectURL(blob)
308      })
309    })
310    torrent.on('done', function () { dbg('Done', torrent) })
311    torrent.on('wire', function (wire, addr) { dbg(`Wire ${addr}`, torrent) })
312    torrent.on('error', er)
313  }
314  $scope.addMagnet = function () {
315    $rootScope.addMagnet($scope.torrentInput, onTorrent)
316    $scope.torrentInput = ''
317  }
318  if ($location.hash() !== '') {
319    $rootScope.client.processing = true
320    setTimeout(function () {
321      dbg(`Adding ${$location.hash()}`)
322      $rootScope.addMagnet($location.hash(), onTorrent)
323    }, 0)
324  }
325}
326])
327
328app.filter('html', [
329  '$sce', function ($sce) {
330    return function (input) {
331      $sce.trustAsHtml(input)
332    }
333  }
334])
335
336app.filter('pbytes', function () {
337  return function (num, speed) {
338    let exponent, unit, units
339    if (isNaN(num)) {
340      return ''
341    }
342    units = ['B', 'kB', 'MB', 'GB', 'TB']
343    if (num < 1) {
344      return (speed ? '' : '0 B')
345    }
346    exponent = Math.min(Math.floor(Math.log(num) / 6.907755278982137), 8)
347    num = (num / Math.pow(1000, exponent)).toFixed(1) * 1
348    unit = units[exponent]
349    return `${num} ${unit}${speed ? '/s' : ''}`
350  }
351})
352
353app.filter('humanTime', function () {
354  return function (millis) {
355    let remaining
356    if (millis < 1000) {
357      return ''
358    }
359    remaining = moment.duration(millis).humanize()
360    return remaining[0].toUpperCase() + remaining.substr(1)
361  }
362})
363
364app.filter('progress', function () { return function (num) { return `${(100 * num).toFixed(1)}%` } })

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.