streamium
Version:
Decentralized trustless video streaming using bitcoin payment channels.
279 lines (239 loc) • 9.05 kB
JavaScript
'use strict';
angular.module('streamium.client.controller', ['ngRoute'])
.config(['$routeProvider',
function($routeProvider) {
var join = {
templateUrl: '/app/client/join.html',
controller: 'JoinStreamCtrl'
};
$routeProvider.when('/s/:streamId', join);
$routeProvider.when('/t/s/:streamId', join);
var watch = {
templateUrl: '/app/client/stream.html',
controller: 'WatchStreamCtrl'
};
$routeProvider.when('/s/:streamId/watch', watch);
$routeProvider.when('/t/s/:streamId/watch', watch);
var cashout = {
templateUrl: '/app/client/cashout.html',
controller: 'WithdrawStreamCtrl'
};
$routeProvider.when('/s/:streamId/cashout', cashout);
$routeProvider.when('/t/s/:streamId/cashout', cashout);
}
])
.controller('JoinStreamCtrl', function($scope, $rootScope, $routeParams, StreamiumClient, Insight, $location, bitcore, Stats) {
$scope.client = StreamiumClient;
$scope.minutes = [5, 10, 15, 30, 45, 60, 90, 120, 180, 240];
$scope.stream = {};
$scope.stream.minutes = $scope.minutes[4];
$scope.stream.founds = 0;
$scope.stream.name = $routeParams.streamId;
$scope.config = config;
var started = new Date();
Stats.client.joinedRoom($scope.stream.name);
if (!DetectRTC.isWebRTCSupported) {
$scope.nowebrtc = true;
return;
}
var twoFees = 0.0002;
$scope.$watch('stream.minutes', function() {
$scope.amount = ($scope.stream.minutes * $scope.client.rate + twoFees).toFixed(8);
$scope.payUrl = 'bitcoin:' + $scope.fundingAddress + '?amount=' + $scope.amount;
});
if (config.DEBUG) {
$scope.client.change = config.defaults.clientChange;
}
$.ajax({
url: config.RELAYSTORE_QUERY,
dataType: 'json'
}).done(function(result) {
StreamiumClient.setRelayParams({
address: result.address,
amount: result.charge
})
});
StreamiumClient.connect($routeParams.streamId, function(err, fundingAddress, isStatic) {
if (err) {
if (err.type === 'peer-unavailable') {
$scope.error = 'Looks like ' + $routeParams.streamId + ' is offline at the moment.';
} else {
$scope.error = 'Unexpected error when trying to join ' + $routeParams.streamId;
}
$scope.$apply();
return;
}
$rootScope.castType = isStatic ? 'static' : 'stream';
$scope.fundingAddress = fundingAddress;
$scope.amount = ($scope.stream.minutes * $scope.client.rate + twoFees).toFixed(8);
$scope.payUrl = 'bitcoin:' + fundingAddress + '?amount=' + $scope.amount;
$scope.$apply();
var updateBalance = function(err, utxos) {
var funds = 0;
var utxo;
for (utxo in utxos) {
funds += utxos[utxo].satoshis;
}
$.ajax({
url: config.BLOCKCYPHERTX + utxos[0].txId,
dataType: 'json'
}).done(function(transaction) {
$scope.client.change = transaction.inputs[0].addresses[0];
$scope.changeFromTransaction = $scope.client.change;
$scope.$apply();
});
StreamiumClient.processFunding(utxos);
$scope.funds = funds;
$scope.fundedMillis = StreamiumClient.getDuration(funds);
$scope.fundedSeconds = $scope.fundedMillis / 1000;
$scope.fundedMinutes = $scope.fundedSeconds / 60;
$scope.funded = true;
//$scope.$apply();
};
Insight.pollBalance(fundingAddress, updateBalance);
});
$scope.submit = function() {
Stats.client.funded({
receivedMoney: StreamiumClient.consumer.commitmentTx.inputAmount,
delayToJoin: (new Date().getTime() - started.getTime()) / 1000,
rate: StreamiumClient.rate
})
StreamiumClient.consumer.refundAddress = $scope.client.change;
window.scrollTo(0, 0);
$location.path(config.appPrefix + '/s/' + $routeParams.streamId + '/watch');
};
})
.controller('WatchStreamCtrl', function($location, $rootScope, $routeParams, $scope, video, StreamiumClient, $interval, bitcore, Stats, Streamer) {
$scope.message = '';
$scope.messages = [];
$scope.PROVIDER_COLOR = config.PROVIDER_COLOR;
$scope.name = $routeParams.streamId;
$scope.started = new Date().getTime();
$scope.loadingStatic = true;
window.addEventListener('beforeunload', dontCloseClient);
if (!StreamiumClient.isReady()) {
$location.path(config.appPrefix + '/s/' + $scope.name);
return;
}
StreamiumClient.askForRefund();
var startViewer = function() {
if ($rootScope.castType !== 'static') {
video.setPeer(StreamiumClient.peer);
video.view($scope.name, function(err, stream) {
// called on provider calling us
if (err) {
console.log(err);
StreamiumClient.end();
return;
}
// show provider video on screen
var videoSrc = URL.createObjectURL(stream);
$scope.videoSrc = videoSrc;
$scope.$digest();
// start sending payments at regular intervals
$interval(calculateSeconds, 1000);
StreamiumClient.setupPaymentUpdates();
Stats.client.startedWatching({
receivedMoney: StreamiumClient.consumer.commitmentTx.inputAmount,
rate: StreamiumClient.rate
});
});
} else {
var streamer = new Streamer();
streamer.video = document.getElementById('video');
streamer.receive();
$scope.$digest();
// start sending payments at regular intervals
$interval(calculateSeconds, 1000);
StreamiumClient.setupPaymentUpdates();
Stats.client.startedWatching({
receivedMoney: StreamiumClient.consumer.commitmentTx.inputAmount,
rate: StreamiumClient.rate
});
$('#video').addEventListener('ended', function() {
StreamiumClient.errored = false;
StreamiumClient.end();
}, false);
StreamiumClient.onStream = function (payload) {
$scope.loadingStatic = false;
try {
if (payload.data.end) {
streamer.end();
}
else {
var data = new window.Uint8Array(payload.data);
streamer.append(data);
}
} catch (e) {
console.log(e, e.stack);
StreamiumClient.errored = false;
StreamiumClient.end();
}
};
}
};
StreamiumClient.on('refundReceived', function() {
bitcore.util.preconditions.checkState(StreamiumClient.peer, 'StreamiumClient.peer should be set');
startViewer();
StreamiumClient.sendFirstPayment();
});
StreamiumClient.on('chatroom:message', function(data) {
$scope.messages.push({
color: data.color,
text: data.message
});
});
var calculateSeconds = function() {
$scope.secondsLeft = Math.floor(($scope.expirationDate - new Date().getTime()) / 1000);
};
StreamiumClient.on('paymentUpdate', function() {
$scope.expirationDate = StreamiumClient.getExpirationDate();
Stats.client.watchingUpdate((new Date().getTime() - $scope.started) / 1000);
calculateSeconds();
});
StreamiumClient.on('end', function() {
console.log('Moving to cashout stream', $routeParams);
var seconds = (new Date().getTime() - $scope.started) / 1000;
Stats.client.endStream({
seconds: seconds,
totalSpent: StreamiumClient.consumer.paymentTx.paid,
});
$location.path(config.appPrefix + '/s/' + $routeParams.streamId + '/cashout');
});
$scope.end = function() {
StreamiumClient.errored = false;
StreamiumClient.end();
};
$scope.chat = function () {
StreamiumClient.sendMessage($scope.message);
$scope.message = '';
};
})
.controller('WithdrawStreamCtrl', function($scope, $routeParams, $location, StreamiumClient, Duration, bitcore) {
$scope.client = StreamiumClient;
window.removeEventListener('beforeunload', dontCloseClient);
if (!StreamiumClient.consumer) {
$location.path(config.appPrefix + '/');
return;
}
$scope.refundTx = StreamiumClient.consumer.refundTx.uncheckedSerialize();
$scope.displayRefund = StreamiumClient.errored;
$scope.spent = StreamiumClient.consumer.paymentTx.paid;
$scope.duration = Duration.for(StreamiumClient.consumer.paymentTx.paid);
$scope.change = StreamiumClient.consumer.paymentTx.amount - StreamiumClient.consumer.paymentTx.paid;
$scope.transaction = StreamiumClient.consumer.paymentTx.id;
$scope.commitmentTx = StreamiumClient.consumer.commitmentTx.uncheckedSerialize();
$scope.fundingKey = StreamiumClient.consumer.fundingKey.toString();
$scope.privkey = StreamiumClient.consumer.commitmentKey.toString();
$scope.serverPubkey = StreamiumClient.consumer.providerPublicKey.toString();
$scope.contractAddress = StreamiumClient.consumer.commitmentTx.address.toString();
$scope.addressUrl = 'https://' + (bitcore.Networks.defaultNetwork.name === 'testnet' ? 'test-' : '') + 'insight.bitpay.com/address/' + $scope.contractAddress;
});
var dontCloseClient = function (e) {
var e = e || window.event;
var question = 'Leaving now will close the stream.';
if (e) {
e.returnValue = question;
}
return question;
};