Bittorrent en el navegador con webtorrent

En este post pretendo mostrar un ejemplo simple de como utilizar el protocolo Bittorrent en el navegador utilizando webtorrent. Prefiero omitir la explicación de conceptos como seed o leechers utilizados en Bittorrent, esos detalles se pueden leer el wikipedia.

La base de los siguientes ejemplos son del get started de webtorrent, solo intento mostrar un poco más de detalle en cada paso.

Para utiliza los ejemplos es necesario disponer de nodejs y algunas otras utilidades como browserify y browser-sync.

Seed

El ejemplo será una página la cual al arrastrar un archivo sobre ella comenzara a hacer de seed.

Además de webtorrent, para poder arrastrar archivos sobre la página se necesita drag-drop

npm install webtorrent drag-drop

Para que el seed sirva el contenido creamos el archivo javascript

// browser-seed.js
var dragDrop = require('drag-drop')
var WebTorrent = require('webtorrent')

var client = new WebTorrent()
console.log('webtorrent created')

// When user drops files on the browser, create a new torrent and start seeding it!
dragDrop('body', function (files) {
  client.seed(files, function (torrent) {
    console.log('Client is seeding ' + torrent.infoHash)
    console.log(torrent.magnetURI)
  })
})

En las últimas lineas se envía a consola en hash del torrent y la magnetURI, esta última deberemos usarla en el leecher, es la dirección con la cual el leecher sabe que descargar.

Para utilizar el archivo javascript desde el navegador es necesario instalar browserify

npm install -g browserify

Y ejecutar

browserify browser-seed.js -o js/browser-seed.js

Ahora podemos llamar a js/browser-seed.js desde index.html

<html>
  <head>
    <meta charset="utf-8">
    <title></title>
    <style media="screen">
      body {
        height: 600px;
      }
    </style>
  </head>
  <body>
    <script src="js/browser-seed.js" charset="utf-8"></script>
  </body>
</html>

Como servidor del archivo html voy a usar browser-sync

browser-sync start --server --files='*.html,*.js'

Con la página funcionando solo falta arrastrar un archivo sobre ella, para lo cual creé un archivo llamado fecha.txt

date > fecha.txt

Después de arrastrar el archivo, en la consola del navegador se debería ver algo como

webtorrent created
Client is seeding 0d12d731c700a28fb80bb9e90e5baa3829ec7db6
magnet:?xt=urn:btih:0d12d731c700a28fb80bb9e90e5baa3829ec7db6&dn=fecha.txt&tr=wss%3A%2F%2Ftracker.webtorrent.io

Lo cual indica que el seed está arriba. La última linea es la magnetURI a utilizar en el leecher.

Leecher

Para el leecher, el que se encarga de la descarga, en un directorio diferente al seed creamos el archivo javascript

// client.js
var client = new WebTorrent()

var torrentId = 'magnet:?xt=urn:btih:0d12d731c700a28fb80bb9e90e5baa3829ec7db6&dn=fecha.txt&tr=wss%3A%2F%2Ftracker.webtorrent.io'

client.add(torrentId, function (torrent) {

  console.log(torrent)
  // Torrents can contain many files. Let's use the first.
  var file = torrent.files[0]

  file.appendTo('body')
})

Y el respectivo index.html, en este caso no usé webtorrent con browserify, sino lo cargué directamente desde un CDN.

<html>
  <head>
    <meta charset="utf-8">
    <title></title>
    <script src="https://cdn.jsdelivr.net/webtorrent/latest/webtorrent.min.js"></script>
  </head>
  <body>

    <script src="client.js" charset="utf-8"></script>
  </body>
</html>

Al abrir este archivo html en el navegador, debería comenzar a hacer la descarga desde el seed y cuando este completo presentar el contenido obtenido en el body, mostrando algo similar a la imagen siguiente

leecher

Con lo descrito en el post es posible, desde el navegador, servir y descargar contenido sobre el protocolo Bittorrent.

Referencias


Testing sobre angularjs con karma

Angularjs es genial para desarrollar aplicaciones web rápidamente, pero antes que un proyecto se escape de nuestro entendimiento es necesario tomar medidas, una buena opción es mantenerlo cubierto con unit tests.

He encontrado varios artículos del tema, pero pocos que comienzan desde cero, orientados al que nunca ha usado karma.

En este articulo voy a mostrar como preparar el entorno de desarrollo para testear una factory de angularjs usando karma.

Pre requisitos

  • Tener instalados nodejs, npm y bower.
  • Algún conocimiento de Angularjs.

angular y angular-mocks

La instalación de angular la vamos realizar con bower. Primero, inicializar bower

bower init

E instalar angular

bower install angular#1.4.4 --save-dev

Para poder utilizar los test es necesario instalar angular-mocks

bower install angular-mocks#1.4.4 --save-dev

Es importante que el número de versión de angular-mocks sea el mismo que el de angular. Por eso forcé la versión con #1.4.4.

Código angular

Como ejemplo voy a crear un factory de angular

mkdir js
touch js/services.js

Con el siguiente contenido

// js/services.js
angular.module('factories', [])
  .factory('Users', function(){
    var users = [];
    return {
        add: function(user){
          users.push(user);
        },
        all: function(){
            return users;
        }
    };
  });

Instalación de karma

Para poder utilizar karma, instalarla globalmente

npm install -g karma-cli

Después, inicializar el archivo package.json y agregar karma como dependencia

npm init
npm install karma --save-dev

Configuración de karma

Inicializar karma

karma init

Karma preguntará por algunas preferencias a utilizar, como test framework, navegador, etc. Se puede seleccionar entre las opciones con la tecla tab.

Las que yo escogí:

> jasmine

> no

> PhantomJS

> js/*.js
> test/*.js

>

> yes

Dependiendo de lo anterior será instalado lo necesario, agregado al archivo package.json y creado un archivo llamado karma.conf.js con nuestras preferencias.

Jasmine es el test framework que vamos a utilizar. Prefiero PhantomJS, así todo se ejecuta en consola sin necesidad de lanzar chrome o firefox.

Los expresiones js/*.js y test/*.js representan los archivos que debe cargar karma, estos son agregados en una lista en el archivo karma.conf.js, en dicha lista se debe agregar la ubicación de angularjs y angular-mocks. Similar a

files: [
  'bower_components/angular/angular.js',
  'bower_components/angular-mocks/angular-mocks.js',
  'js/*.js',
  'test/*.js'
],

Creando los test y lanzando karma

Ahora crear el directorio y archivos donde van a estar los tests.

mkdir test
touch test/services.js

El archivo test/services.js debe tener el siguiente contenido

describe('Users', function(){

  beforeEach(module('factories'));

  var users;

  beforeEach(inject(function(_Users_){
    users = _Users_;
  }));

  it('empty users length 0', function(){
    expect(users.all().length).toBe(0);
  });

  it('One user return length 1', function(){
    users.add({name:'user name'});

    expect(users.all().length).toBe(1);
  });
});

Donde se carga el modulo factories, el factory Users y describen los tests.

Finalmente lanzar los tests

karma start

Con lo cual se deberían ejecutar los tests y ver como los dos pasaron.

Lo presentado es un primer ejemplo muy simple, para revisar más detalles sobre karma, dejo algunos enlaces más abajo.

El código fuente descrito está disponible en el repositorio

https://github.com/juanpabloaj/simple-karma-test

Referencias