p2p-media-loader-core
Advanced tools
Comparing version 0.2.1 to 0.3.0
/** | ||
* @license | ||
* Copyright 2018 Novage LLC. | ||
@@ -18,3 +17,5 @@ * | ||
import * as p2pMediaLoaderCore from './index'; | ||
import * as p2pMediaLoaderCore from "./index"; | ||
import * as debug from "debug"; | ||
import * as events from "events"; | ||
@@ -26,1 +27,2 @@ if (!window.p2pml) { | ||
window.p2pml.core = p2pMediaLoaderCore; | ||
window.p2pml._shared = {debug, events}; |
/** | ||
* @license | ||
* Copyright 2018 Novage LLC. | ||
@@ -4,0 +3,0 @@ * |
@@ -17,28 +17,12 @@ "use strict"; | ||
*/ | ||
var __extends = (this && this.__extends) || (function () { | ||
var extendStatics = function (d, b) { | ||
extendStatics = Object.setPrototypeOf || | ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || | ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; | ||
return extendStatics(d, b); | ||
} | ||
return function (d, b) { | ||
extendStatics(d, b); | ||
function __() { this.constructor = d; } | ||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); | ||
}; | ||
})(); | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var Debug = require("debug"); | ||
var stringly_typed_event_emitter_1 = require("./stringly-typed-event-emitter"); | ||
var HttpMediaManager = /** @class */ (function (_super) { | ||
__extends(HttpMediaManager, _super); | ||
function HttpMediaManager() { | ||
var _this = _super.call(this) || this; | ||
_this.xhrRequests = new Map(); | ||
_this.debug = Debug("p2pml:http-media-manager"); | ||
return _this; | ||
const Debug = require("debug"); | ||
const stringly_typed_event_emitter_1 = require("./stringly-typed-event-emitter"); | ||
class HttpMediaManager extends stringly_typed_event_emitter_1.default { | ||
constructor() { | ||
super(); | ||
this.xhrRequests = new Map(); | ||
this.debug = Debug("p2pml:http-media-manager"); | ||
} | ||
HttpMediaManager.prototype.download = function (segment) { | ||
var _this = this; | ||
download(segment) { | ||
if (this.isDownloading(segment)) { | ||
@@ -48,3 +32,3 @@ return; | ||
this.debug("http segment download", segment.url); | ||
var request = new XMLHttpRequest(); | ||
const request = new XMLHttpRequest(); | ||
request.open("GET", segment.url, true); | ||
@@ -55,27 +39,27 @@ request.responseType = "arraybuffer"; | ||
} | ||
var prevBytesLoaded = 0; | ||
request.onprogress = function (event) { | ||
var bytesLoaded = event.loaded - prevBytesLoaded; | ||
_this.emit("bytes-downloaded", bytesLoaded); | ||
let prevBytesLoaded = 0; | ||
request.onprogress = (event) => { | ||
const bytesLoaded = event.loaded - prevBytesLoaded; | ||
this.emit("bytes-downloaded", bytesLoaded); | ||
prevBytesLoaded = event.loaded; | ||
}; | ||
request.onload = function (event) { | ||
_this.xhrRequests.delete(segment.id); | ||
request.onload = (event) => { | ||
this.xhrRequests.delete(segment.id); | ||
if (event.target.status >= 200 && 300 > event.target.status) { | ||
_this.emit("segment-loaded", segment, event.target.response); | ||
this.emit("segment-loaded", segment, event.target.response); | ||
} | ||
else { | ||
_this.emit("segment-error", segment, event); | ||
this.emit("segment-error", segment, event); | ||
} | ||
}; | ||
request.onerror = function (event) { | ||
request.onerror = (event) => { | ||
// TODO: retry with timeout? | ||
_this.xhrRequests.delete(segment.id); | ||
_this.emit("segment-error", segment, event); | ||
this.xhrRequests.delete(segment.id); | ||
this.emit("segment-error", segment, event); | ||
}; | ||
this.xhrRequests.set(segment.id, request); | ||
request.send(); | ||
}; | ||
HttpMediaManager.prototype.abort = function (segment) { | ||
var xhr = this.xhrRequests.get(segment.id); | ||
} | ||
abort(segment) { | ||
const xhr = this.xhrRequests.get(segment.id); | ||
if (xhr) { | ||
@@ -86,15 +70,14 @@ xhr.abort(); | ||
} | ||
}; | ||
HttpMediaManager.prototype.isDownloading = function (segment) { | ||
} | ||
isDownloading(segment) { | ||
return this.xhrRequests.has(segment.id); | ||
}; | ||
HttpMediaManager.prototype.getActiveDownloads = function () { | ||
} | ||
getActiveDownloads() { | ||
return this.xhrRequests; | ||
}; | ||
HttpMediaManager.prototype.destroy = function () { | ||
this.xhrRequests.forEach(function (xhr) { return xhr.abort(); }); | ||
} | ||
destroy() { | ||
this.xhrRequests.forEach(xhr => xhr.abort()); | ||
this.xhrRequests.clear(); | ||
}; | ||
return HttpMediaManager; | ||
}(stringly_typed_event_emitter_1.default)); // end of HttpMediaManager | ||
} | ||
} // end of HttpMediaManager | ||
exports.HttpMediaManager = HttpMediaManager; |
@@ -16,3 +16,2 @@ /** | ||
*/ | ||
/// <reference types="node" /> | ||
import { LoaderInterface, Segment } from "./loader-interface"; | ||
@@ -19,0 +18,0 @@ import { EventEmitter } from "events"; |
@@ -17,26 +17,14 @@ "use strict"; | ||
*/ | ||
var __extends = (this && this.__extends) || (function () { | ||
var extendStatics = function (d, b) { | ||
extendStatics = Object.setPrototypeOf || | ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || | ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; | ||
return extendStatics(d, b); | ||
} | ||
return function (d, b) { | ||
extendStatics(d, b); | ||
function __() { this.constructor = d; } | ||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); | ||
}; | ||
})(); | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var Debug = require("debug"); | ||
var loader_interface_1 = require("./loader-interface"); | ||
var events_1 = require("events"); | ||
var http_media_manager_1 = require("./http-media-manager"); | ||
var p2p_media_manager_1 = require("./p2p-media-manager"); | ||
var media_peer_1 = require("./media-peer"); | ||
var segment_internal_1 = require("./segment-internal"); | ||
var speed_approximator_1 = require("./speed-approximator"); | ||
var getBrowserRtc = require("get-browser-rtc"); | ||
var defaultSettings = { | ||
const Debug = require("debug"); | ||
const loader_interface_1 = require("./loader-interface"); | ||
const events_1 = require("events"); | ||
const http_media_manager_1 = require("./http-media-manager"); | ||
const p2p_media_manager_1 = require("./p2p-media-manager"); | ||
const media_peer_1 = require("./media-peer"); | ||
const segment_internal_1 = require("./segment-internal"); | ||
const speed_approximator_1 = require("./speed-approximator"); | ||
const getBrowserRTC = require("get-browser-rtc"); | ||
const Peer = require("simple-peer"); | ||
const defaultSettings = { | ||
cachedSegmentExpiration: 5 * 60 * 1000, | ||
@@ -52,100 +40,87 @@ cachedSegmentsCount: 30, | ||
p2pSegmentDownloadTimeout: 60000, | ||
trackerAnnounce: ["wss://tracker.btorrent.xyz/", "wss://tracker.openwebtorrent.com/"], | ||
rtcConfig: require("simple-peer").config | ||
trackerAnnounce: ["wss://tracker.btorrent.xyz", "wss://tracker.openwebtorrent.com", "wss://tracker.fastcast.nz"], | ||
rtcConfig: Peer.config | ||
}; | ||
var HybridLoader = /** @class */ (function (_super) { | ||
__extends(HybridLoader, _super); | ||
function HybridLoader(settings) { | ||
if (settings === void 0) { settings = {}; } | ||
var _this = _super.call(this) || this; | ||
_this.debug = Debug("p2pml:hybrid-loader"); | ||
_this.segments = new Map(); | ||
_this.segmentsQueue = []; | ||
_this.httpDownloadProbabilityTimestamp = -999999; | ||
_this.speedApproximator = new speed_approximator_1.SpeedApproximator(); | ||
_this.onPieceBytesDownloaded = function (method, bytes) { | ||
_this.speedApproximator.addBytes(bytes, _this.now()); | ||
_this.emit(loader_interface_1.Events.PieceBytesDownloaded, method, bytes); | ||
class HybridLoader extends events_1.EventEmitter { | ||
constructor(settings = {}) { | ||
super(); | ||
this.debug = Debug("p2pml:hybrid-loader"); | ||
this.segments = new Map(); | ||
this.segmentsQueue = []; | ||
this.httpDownloadProbabilityTimestamp = -999999; | ||
this.speedApproximator = new speed_approximator_1.SpeedApproximator(); | ||
this.onPieceBytesDownloaded = (method, bytes) => { | ||
this.speedApproximator.addBytes(bytes, this.now()); | ||
this.emit(loader_interface_1.Events.PieceBytesDownloaded, method, bytes); | ||
}; | ||
_this.onPieceBytesUploaded = function (method, bytes) { | ||
_this.speedApproximator.addBytes(bytes, _this.now()); | ||
_this.emit(loader_interface_1.Events.PieceBytesUploaded, method, bytes); | ||
this.onPieceBytesUploaded = (method, bytes) => { | ||
this.speedApproximator.addBytes(bytes, this.now()); | ||
this.emit(loader_interface_1.Events.PieceBytesUploaded, method, bytes); | ||
}; | ||
_this.onSegmentLoaded = function (segment, data) { | ||
_this.debug("segment loaded", segment.id, segment.url); | ||
var segmentInternal = new segment_internal_1.SegmentInternal(segment.id, segment.url, segment.range, segment.priority, data, _this.speedApproximator.getSpeed(_this.now())); | ||
_this.segments.set(segment.id, segmentInternal); | ||
_this.emitSegmentLoaded(segmentInternal); | ||
_this.processSegmentsQueue(); | ||
_this.p2pManager.sendSegmentsMapToAll(_this.createSegmentsMap()); | ||
this.onSegmentLoaded = (segment, data) => { | ||
this.debug("segment loaded", segment.id, segment.url); | ||
const segmentInternal = new segment_internal_1.SegmentInternal(segment.id, segment.url, segment.range, segment.priority, data, this.speedApproximator.getSpeed(this.now())); | ||
this.segments.set(segment.id, segmentInternal); | ||
this.emitSegmentLoaded(segmentInternal); | ||
this.processSegmentsQueue(); | ||
this.p2pManager.sendSegmentsMapToAll(this.createSegmentsMap()); | ||
}; | ||
_this.onSegmentError = function (segment, event) { | ||
_this.emit(loader_interface_1.Events.SegmentError, segment, event); | ||
_this.processSegmentsQueue(); | ||
this.onSegmentError = (segment, event) => { | ||
this.emit(loader_interface_1.Events.SegmentError, segment, event); | ||
this.processSegmentsQueue(); | ||
}; | ||
_this.onPeerConnect = function (peer) { | ||
_this.p2pManager.sendSegmentsMap(peer.id, _this.createSegmentsMap()); | ||
_this.emit(loader_interface_1.Events.PeerConnect, peer); | ||
this.onPeerConnect = (peer) => { | ||
this.p2pManager.sendSegmentsMap(peer.id, this.createSegmentsMap()); | ||
this.emit(loader_interface_1.Events.PeerConnect, peer); | ||
}; | ||
_this.onPeerClose = function (peerId) { | ||
_this.emit(loader_interface_1.Events.PeerClose, peerId); | ||
this.onPeerClose = (peerId) => { | ||
this.emit(loader_interface_1.Events.PeerClose, peerId); | ||
}; | ||
_this.settings = Object.assign(defaultSettings, settings); | ||
_this.debug("loader settings", _this.settings); | ||
_this.httpManager = _this.createHttpManager(); | ||
_this.httpManager.on("segment-loaded", _this.onSegmentLoaded); | ||
_this.httpManager.on("segment-error", _this.onSegmentError); | ||
_this.httpManager.on("bytes-downloaded", function (bytes) { return _this.onPieceBytesDownloaded("http", bytes); }); | ||
_this.p2pManager = _this.createP2PManager(); | ||
_this.p2pManager.on("segment-loaded", _this.onSegmentLoaded); | ||
_this.p2pManager.on("segment-error", _this.onSegmentError); | ||
_this.p2pManager.on("peer-data-updated", function () { return _this.processSegmentsQueue(); }); | ||
_this.p2pManager.on("bytes-downloaded", function (bytes) { return _this.onPieceBytesDownloaded("p2p", bytes); }); | ||
_this.p2pManager.on("bytes-uploaded", function (bytes) { return _this.onPieceBytesUploaded("p2p", bytes); }); | ||
_this.p2pManager.on("peer-connected", _this.onPeerConnect); | ||
_this.p2pManager.on("peer-closed", _this.onPeerClose); | ||
return _this; | ||
this.settings = Object.assign(defaultSettings, settings); | ||
this.debug("loader settings", this.settings); | ||
this.httpManager = this.createHttpManager(); | ||
this.httpManager.on("segment-loaded", this.onSegmentLoaded); | ||
this.httpManager.on("segment-error", this.onSegmentError); | ||
this.httpManager.on("bytes-downloaded", (bytes) => this.onPieceBytesDownloaded("http", bytes)); | ||
this.p2pManager = this.createP2PManager(); | ||
this.p2pManager.on("segment-loaded", this.onSegmentLoaded); | ||
this.p2pManager.on("segment-error", this.onSegmentError); | ||
this.p2pManager.on("peer-data-updated", () => this.processSegmentsQueue()); | ||
this.p2pManager.on("bytes-downloaded", (bytes) => this.onPieceBytesDownloaded("p2p", bytes)); | ||
this.p2pManager.on("bytes-uploaded", (bytes) => this.onPieceBytesUploaded("p2p", bytes)); | ||
this.p2pManager.on("peer-connected", this.onPeerConnect); | ||
this.p2pManager.on("peer-closed", this.onPeerClose); | ||
} | ||
HybridLoader.isSupported = function () { | ||
var browserRtc = getBrowserRtc(); | ||
static isSupported() { | ||
const browserRtc = getBrowserRTC(); | ||
return (browserRtc && (browserRtc.RTCPeerConnection.prototype.createDataChannel !== undefined)); | ||
}; | ||
HybridLoader.prototype.createHttpManager = function () { | ||
} | ||
createHttpManager() { | ||
return new http_media_manager_1.HttpMediaManager(); | ||
}; | ||
HybridLoader.prototype.createP2PManager = function () { | ||
} | ||
createP2PManager() { | ||
return new p2p_media_manager_1.P2PMediaManager(this.segments, this.settings); | ||
}; | ||
HybridLoader.prototype.load = function (segments, swarmId) { | ||
} | ||
load(segments, swarmId) { | ||
this.p2pManager.setSwarmId(swarmId); | ||
this.debug("load segments", segments, this.segmentsQueue); | ||
var updateSegmentsMap = false; | ||
var _loop_1 = function (segment) { | ||
if (!segments.find(function (f) { return f.url == segment.url; })) { | ||
this_1.debug("remove segment", segment.url); | ||
if (this_1.httpManager.isDownloading(segment)) { | ||
let updateSegmentsMap = false; | ||
// stop all http requests and p2p downloads for segments that are not in the new load | ||
for (const segment of this.segmentsQueue) { | ||
if (!segments.find(f => f.url == segment.url)) { | ||
this.debug("remove segment", segment.url); | ||
if (this.httpManager.isDownloading(segment)) { | ||
updateSegmentsMap = true; | ||
this_1.httpManager.abort(segment); | ||
this.httpManager.abort(segment); | ||
} | ||
else { | ||
this_1.p2pManager.abort(segment); | ||
this.p2pManager.abort(segment); | ||
} | ||
this_1.emit(loader_interface_1.Events.SegmentAbort, segment); | ||
this.emit(loader_interface_1.Events.SegmentAbort, segment); | ||
} | ||
}; | ||
var this_1 = this; | ||
// stop all http requests and p2p downloads for segments that are not in the new load | ||
for (var _i = 0, _a = this.segmentsQueue; _i < _a.length; _i++) { | ||
var segment = _a[_i]; | ||
_loop_1(segment); | ||
} | ||
var _loop_2 = function (segment) { | ||
if (!this_2.segmentsQueue.find(function (f) { return f.url == segment.url; })) { | ||
this_2.debug("add segment", segment.url); | ||
for (const segment of segments) { | ||
if (!this.segmentsQueue.find(f => f.url == segment.url)) { | ||
this.debug("add segment", segment.url); | ||
} | ||
}; | ||
var this_2 = this; | ||
for (var _b = 0, segments_1 = segments; _b < segments_1.length; _b++) { | ||
var segment = segments_1[_b]; | ||
_loop_2(segment); | ||
} | ||
@@ -161,5 +136,5 @@ // renew segment queue | ||
} | ||
}; | ||
HybridLoader.prototype.getSegment = function (id) { | ||
var segment = this.segments.get(id); | ||
} | ||
getSegment(id) { | ||
const segment = this.segments.get(id); | ||
return segment | ||
@@ -170,7 +145,7 @@ ? segment.data | ||
: undefined; | ||
}; | ||
HybridLoader.prototype.getSettings = function () { | ||
} | ||
getSettings() { | ||
return this.settings; | ||
}; | ||
HybridLoader.prototype.destroy = function () { | ||
} | ||
destroy() { | ||
this.segmentsQueue = []; | ||
@@ -180,10 +155,8 @@ this.httpManager.destroy(); | ||
this.segments.clear(); | ||
}; | ||
HybridLoader.prototype.processSegmentsQueue = function () { | ||
var _this = this; | ||
var startingPriority = this.segmentsQueue.length > 0 ? this.segmentsQueue[0].priority : 0; | ||
} | ||
processSegmentsQueue() { | ||
const startingPriority = this.segmentsQueue.length > 0 ? this.segmentsQueue[0].priority : 0; | ||
this.debug("processSegmentsQueue - starting priority: " + startingPriority); | ||
var pendingCount = 0; | ||
for (var _i = 0, _a = this.segmentsQueue; _i < _a.length; _i++) { | ||
var segment = _a[_i]; | ||
let pendingCount = 0; | ||
for (const segment of this.segmentsQueue) { | ||
if (!this.segments.has(segment.id) && !this.httpManager.isDownloading(segment) && !this.p2pManager.isDownloading(segment)) { | ||
@@ -196,12 +169,11 @@ pendingCount++; | ||
} | ||
var downloadedSegmentsCount = this.segmentsQueue.length - pendingCount; | ||
var updateSegmentsMap = false; | ||
for (var index = 0; index < this.segmentsQueue.length; index++) { | ||
var segment = this.segmentsQueue[index]; | ||
var segmentPriority = index + startingPriority; | ||
let downloadedSegmentsCount = this.segmentsQueue.length - pendingCount; | ||
let updateSegmentsMap = false; | ||
for (let index = 0; index < this.segmentsQueue.length; index++) { | ||
const segment = this.segmentsQueue[index]; | ||
const segmentPriority = index + startingPriority; | ||
if (!this.segments.has(segment.id)) { | ||
if (segmentPriority <= this.settings.requiredSegmentsPriority) { | ||
if (segmentPriority == 0 && !this.httpManager.isDownloading(segment) && this.httpManager.getActiveDownloads().size > 0) { | ||
for (var _b = 0, _c = this.segmentsQueue; _b < _c.length; _b++) { | ||
var s = _c[_b]; | ||
for (const s of this.segmentsQueue) { | ||
this.httpManager.abort(s); | ||
@@ -231,3 +203,3 @@ updateSegmentsMap = true; | ||
} | ||
var now = this.now(); | ||
const now = this.now(); | ||
if (now - this.httpDownloadProbabilityTimestamp < this.settings.httpDownloadProbabilityInterval) { | ||
@@ -239,6 +211,4 @@ return updateSegmentsMap; | ||
} | ||
var pendingQueue = this.segmentsQueue.filter(function (segment) { | ||
return !_this.segments.has(segment.id) && | ||
!_this.p2pManager.isDownloading(segment); | ||
}); | ||
let pendingQueue = this.segmentsQueue.filter(segment => !this.segments.has(segment.id) && | ||
!this.p2pManager.isDownloading(segment)); | ||
downloadedSegmentsCount = this.segmentsQueue.length - pendingQueue.length; | ||
@@ -248,9 +218,8 @@ if (pendingQueue.length == 0 || downloadedSegmentsCount >= this.settings.bufferedSegmentsCount) { | ||
} | ||
var segmentsMap = this.p2pManager.getOvrallSegmentsMap(); | ||
pendingQueue = pendingQueue.filter(function (segment) { return !segmentsMap.get(segment.id); }); | ||
const segmentsMap = this.p2pManager.getOvrallSegmentsMap(); | ||
pendingQueue = pendingQueue.filter(segment => !segmentsMap.get(segment.id)); | ||
if (pendingQueue.length == 0) { | ||
return updateSegmentsMap; | ||
} | ||
for (var _d = 0, pendingQueue_1 = pendingQueue; _d < pendingQueue_1.length; _d++) { | ||
var segment = pendingQueue_1[_d]; | ||
for (const segment of pendingQueue) { | ||
if (Math.random() <= this.settings.httpDownloadProbability) { | ||
@@ -264,22 +233,21 @@ this.debug("HTTP download (random)", segment.priority, segment.url); | ||
return updateSegmentsMap; | ||
}; | ||
HybridLoader.prototype.emitSegmentLoaded = function (segmentInternal) { | ||
} | ||
emitSegmentLoaded(segmentInternal) { | ||
segmentInternal.lastAccessed = this.now(); | ||
var segment = new loader_interface_1.Segment(segmentInternal.id, segmentInternal.url, segmentInternal.range, segmentInternal.priority, segmentInternal.data, segmentInternal.downloadSpeed); | ||
const segment = new loader_interface_1.Segment(segmentInternal.id, segmentInternal.url, segmentInternal.range, segmentInternal.priority, segmentInternal.data, segmentInternal.downloadSpeed); | ||
this.emit(loader_interface_1.Events.SegmentLoaded, segment); | ||
}; | ||
HybridLoader.prototype.createSegmentsMap = function () { | ||
var segmentsMap = []; | ||
this.segments.forEach(function (value, key) { return segmentsMap.push([key, media_peer_1.MediaPeerSegmentStatus.Loaded]); }); | ||
this.httpManager.getActiveDownloads().forEach(function (value, key) { return segmentsMap.push([key, media_peer_1.MediaPeerSegmentStatus.LoadingByHttp]); }); | ||
} | ||
createSegmentsMap() { | ||
const segmentsMap = []; | ||
this.segments.forEach((value, key) => segmentsMap.push([key, media_peer_1.MediaPeerSegmentStatus.Loaded])); | ||
this.httpManager.getActiveDownloads().forEach((value, key) => segmentsMap.push([key, media_peer_1.MediaPeerSegmentStatus.LoadingByHttp])); | ||
return segmentsMap; | ||
}; | ||
HybridLoader.prototype.collectGarbage = function () { | ||
var _this = this; | ||
var segmentsToDelete = []; | ||
var remainingSegments = []; | ||
} | ||
collectGarbage() { | ||
const segmentsToDelete = []; | ||
const remainingSegments = []; | ||
// Delete old segments | ||
var now = this.now(); | ||
this.segments.forEach(function (segment) { | ||
if (now - segment.lastAccessed > _this.settings.cachedSegmentExpiration) { | ||
const now = this.now(); | ||
for (const segment of this.segments.values()) { | ||
if (now - segment.lastAccessed > this.settings.cachedSegmentExpiration) { | ||
segmentsToDelete.push(segment.id); | ||
@@ -290,32 +258,24 @@ } | ||
} | ||
}); | ||
} | ||
// Delete segments over cached count | ||
var countOverhead = remainingSegments.length - this.settings.cachedSegmentsCount; | ||
let countOverhead = remainingSegments.length - this.settings.cachedSegmentsCount; | ||
if (countOverhead > 0) { | ||
remainingSegments.sort(function (a, b) { return a.lastAccessed - b.lastAccessed; }); | ||
var _loop_3 = function (segment) { | ||
if (!this_3.segmentsQueue.find(function (queueSegment) { return queueSegment.id == segment.id; })) { | ||
remainingSegments.sort((a, b) => a.lastAccessed - b.lastAccessed); | ||
for (const segment of remainingSegments) { | ||
if (!this.segmentsQueue.find(queueSegment => queueSegment.id == segment.id)) { | ||
segmentsToDelete.push(segment.id); | ||
countOverhead--; | ||
if (countOverhead == 0) { | ||
return "break"; | ||
break; | ||
} | ||
} | ||
}; | ||
var this_3 = this; | ||
for (var _i = 0, remainingSegments_1 = remainingSegments; _i < remainingSegments_1.length; _i++) { | ||
var segment = remainingSegments_1[_i]; | ||
var state_1 = _loop_3(segment); | ||
if (state_1 === "break") | ||
break; | ||
} | ||
} | ||
segmentsToDelete.forEach(function (id) { return _this.segments.delete(id); }); | ||
segmentsToDelete.forEach(id => this.segments.delete(id)); | ||
return segmentsToDelete.length > 0; | ||
}; | ||
HybridLoader.prototype.now = function () { | ||
} | ||
now() { | ||
return performance.now(); | ||
}; | ||
return HybridLoader; | ||
}(events_1.EventEmitter)); // end of HybridLoader | ||
} | ||
} // end of HybridLoader | ||
exports.default = HybridLoader; |
/** | ||
* @license Apache-2.0 | ||
* Copyright 2018 Novage LLC. | ||
@@ -3,0 +4,0 @@ * |
"use strict"; | ||
/** | ||
* @license Apache-2.0 | ||
* Copyright 2018 Novage LLC. | ||
@@ -4,0 +5,0 @@ * |
@@ -63,3 +63,3 @@ /** | ||
export interface LoaderInterface { | ||
on(eventName: string | symbol, listener: Function): this; | ||
on(eventName: string, listener: Function): this; | ||
load(segments: Segment[], swarmId: string): void; | ||
@@ -66,0 +66,0 @@ getSegment(id: string): Segment | undefined; |
@@ -18,7 +18,4 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var Segment = /** @class */ (function () { | ||
function Segment(id, url, range, priority, data, downloadSpeed) { | ||
if (priority === void 0) { priority = 0; } | ||
if (data === void 0) { data = undefined; } | ||
if (downloadSpeed === void 0) { downloadSpeed = 0; } | ||
class Segment { | ||
constructor(id, url, range, priority = 0, data = undefined, downloadSpeed = 0) { | ||
this.id = id; | ||
@@ -31,4 +28,3 @@ this.url = url; | ||
} | ||
return Segment; | ||
}()); | ||
} | ||
exports.Segment = Segment; | ||
@@ -35,0 +31,0 @@ var Events; |
@@ -34,3 +34,2 @@ /** | ||
private timer; | ||
private isSafari11_0; | ||
constructor(peer: any, settings: { | ||
@@ -40,3 +39,2 @@ p2pSegmentDownloadTimeout: number; | ||
}); | ||
private detectSafari11_0; | ||
private onPeerConnect; | ||
@@ -43,0 +41,0 @@ private onPeerClose; |
@@ -17,18 +17,6 @@ "use strict"; | ||
*/ | ||
var __extends = (this && this.__extends) || (function () { | ||
var extendStatics = function (d, b) { | ||
extendStatics = Object.setPrototypeOf || | ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || | ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; | ||
return extendStatics(d, b); | ||
} | ||
return function (d, b) { | ||
extendStatics(d, b); | ||
function __() { this.constructor = d; } | ||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); | ||
}; | ||
})(); | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var Debug = require("debug"); | ||
var stringly_typed_event_emitter_1 = require("./stringly-typed-event-emitter"); | ||
const Debug = require("debug"); | ||
const stringly_typed_event_emitter_1 = require("./stringly-typed-event-emitter"); | ||
const buffer_1 = require("buffer"); | ||
var MediaPeerCommands; | ||
@@ -47,4 +35,4 @@ (function (MediaPeerCommands) { | ||
})(MediaPeerSegmentStatus = exports.MediaPeerSegmentStatus || (exports.MediaPeerSegmentStatus = {})); | ||
var DownloadingSegment = /** @class */ (function () { | ||
function DownloadingSegment(id, size) { | ||
class DownloadingSegment { | ||
constructor(id, size) { | ||
this.id = id; | ||
@@ -55,49 +43,31 @@ this.size = size; | ||
} | ||
return DownloadingSegment; | ||
}()); | ||
var MediaPeer = /** @class */ (function (_super) { | ||
__extends(MediaPeer, _super); | ||
function MediaPeer(peer, settings) { | ||
var _this = _super.call(this) || this; | ||
_this.peer = peer; | ||
_this.settings = settings; | ||
_this.remoteAddress = ""; | ||
_this.downloadingSegmentId = null; | ||
_this.downloadingSegment = null; | ||
_this.segmentsMap = new Map(); | ||
_this.debug = Debug("p2pml:media-peer"); | ||
_this.timer = null; | ||
_this.isSafari11_0 = false; | ||
_this.peer.on("connect", function () { return _this.onPeerConnect(); }); | ||
_this.peer.on("close", function () { return _this.onPeerClose(); }); | ||
_this.peer.on("error", function (error) { return _this.debug("peer error", _this.id, error, _this); }); | ||
_this.peer.on("data", function (data) { return _this.onPeerData(data); }); | ||
_this.id = peer.id; | ||
_this.detectSafari11_0(); | ||
return _this; | ||
} | ||
class MediaPeer extends stringly_typed_event_emitter_1.default { | ||
constructor(peer, settings) { | ||
super(); | ||
this.peer = peer; | ||
this.settings = settings; | ||
this.remoteAddress = ""; | ||
this.downloadingSegmentId = null; | ||
this.downloadingSegment = null; | ||
this.segmentsMap = new Map(); | ||
this.debug = Debug("p2pml:media-peer"); | ||
this.timer = null; | ||
this.peer.on("connect", () => this.onPeerConnect()); | ||
this.peer.on("close", () => this.onPeerClose()); | ||
this.peer.on("error", (error) => this.debug("peer error", this.id, error, this)); | ||
this.peer.on("data", (data) => this.onPeerData(data)); | ||
this.id = peer.id; | ||
} | ||
MediaPeer.prototype.detectSafari11_0 = function () { | ||
var userAgent = typeof navigator !== "undefined" ? navigator.userAgent || "" : ""; | ||
var isSafari = userAgent.indexOf("Safari") != -1 && userAgent.indexOf("Chrome") == -1; | ||
if (isSafari) { | ||
var match = userAgent.match(/version\/(\d+(\.\d+)?)/i); | ||
var version = (match && match.length > 1 && match[1]) || ""; | ||
if (version === "11.0") { | ||
this.isSafari11_0 = true; | ||
this.debug("enable workaround for Safari 11.0"); | ||
return; | ||
} | ||
} | ||
}; | ||
MediaPeer.prototype.onPeerConnect = function () { | ||
onPeerConnect() { | ||
this.debug("peer connect", this.id, this); | ||
this.remoteAddress = this.peer.remoteAddress; | ||
this.emit("connect", this); | ||
}; | ||
MediaPeer.prototype.onPeerClose = function () { | ||
} | ||
onPeerClose() { | ||
this.debug("peer close", this.id, this); | ||
this.terminateSegmentRequest(); | ||
this.emit("close", this); | ||
}; | ||
MediaPeer.prototype.receiveSegmentPiece = function (data) { | ||
} | ||
receiveSegmentPiece(data) { | ||
if (!this.downloadingSegment) { | ||
@@ -111,8 +81,7 @@ // The segment was not requested or canceled | ||
this.emit("bytes-downloaded", data.byteLength); | ||
var segmentId = this.downloadingSegment.id; | ||
const segmentId = this.downloadingSegment.id; | ||
if (this.downloadingSegment.bytesDownloaded == this.downloadingSegment.size) { | ||
var segmentData = new Uint8Array(this.downloadingSegment.size); | ||
var offset = 0; | ||
for (var _i = 0, _a = this.downloadingSegment.pieces; _i < _a.length; _i++) { | ||
var piece = _a[_i]; | ||
const segmentData = new Uint8Array(this.downloadingSegment.size); | ||
let offset = 0; | ||
for (const piece of this.downloadingSegment.pieces) { | ||
segmentData.set(new Uint8Array(piece), offset); | ||
@@ -130,9 +99,9 @@ offset += piece.byteLength; | ||
} | ||
}; | ||
MediaPeer.prototype.getJsonCommand = function (data) { | ||
var bytes = new Uint8Array(data); | ||
} | ||
getJsonCommand(data) { | ||
const bytes = new Uint8Array(data); | ||
// Serialized JSON string check by first, second and last characters: '{" .... }' | ||
if (bytes[0] == 123 && bytes[1] == 34 && bytes[data.byteLength - 1] == 125) { | ||
try { | ||
return JSON.parse(new TextDecoder("utf-8").decode(data)); | ||
return JSON.parse(new TextDecoder().decode(data)); | ||
} | ||
@@ -143,5 +112,5 @@ catch (_a) { | ||
return null; | ||
}; | ||
MediaPeer.prototype.onPeerData = function (data) { | ||
var command = this.getJsonCommand(data); | ||
} | ||
onPeerData(data) { | ||
const command = this.getJsonCommand(data); | ||
if (command == null) { | ||
@@ -153,3 +122,3 @@ this.receiveSegmentPiece(data); | ||
this.debug("peer segment download is interrupted by a command", this.id, this); | ||
var segmentId = this.downloadingSegment.id; | ||
const segmentId = this.downloadingSegment.id; | ||
this.terminateSegmentRequest(); | ||
@@ -187,22 +156,22 @@ this.emit("segment-error", this, segmentId, "Segment download is interrupted by a command"); | ||
} | ||
}; | ||
MediaPeer.prototype.sendCommand = function (command) { | ||
} | ||
sendCommand(command) { | ||
this.debug("peer send command", this.id, command, this); | ||
this.peer.write(JSON.stringify(command)); | ||
}; | ||
MediaPeer.prototype.destroy = function () { | ||
} | ||
destroy() { | ||
this.debug("peer destroy", this.id, this); | ||
this.terminateSegmentRequest(); | ||
this.peer.destroy(); | ||
}; | ||
MediaPeer.prototype.getDownloadingSegmentId = function () { | ||
} | ||
getDownloadingSegmentId() { | ||
return this.downloadingSegmentId; | ||
}; | ||
MediaPeer.prototype.getSegmentsMap = function () { | ||
} | ||
getSegmentsMap() { | ||
return this.segmentsMap; | ||
}; | ||
MediaPeer.prototype.sendSegmentsMap = function (segments) { | ||
} | ||
sendSegmentsMap(segments) { | ||
this.sendCommand({ "command": MediaPeerCommands.SegmentsMap, "segments": segments }); | ||
}; | ||
MediaPeer.prototype.sendSegmentData = function (segmentId, data) { | ||
} | ||
sendSegmentData(segmentId, data) { | ||
this.sendCommand({ | ||
@@ -213,8 +182,6 @@ "command": MediaPeerCommands.SegmentData, | ||
}); | ||
var bytesLeft = data.byteLength; | ||
let bytesLeft = data.byteLength; | ||
while (bytesLeft > 0) { | ||
var bytesToSend = (bytesLeft >= this.settings.webRtcMaxMessageSize ? this.settings.webRtcMaxMessageSize : bytesLeft); | ||
var buffer = this.isSafari11_0 ? | ||
Buffer.from(data.slice(data.byteLength - bytesLeft, data.byteLength - bytesLeft + bytesToSend)) : // workaround for Safari 11.0 bug: https://bugs.webkit.org/show_bug.cgi?id=173052 | ||
Buffer.from(data, data.byteLength - bytesLeft, bytesToSend); // avoid memory copying | ||
const bytesToSend = (bytesLeft >= this.settings.webRtcMaxMessageSize ? this.settings.webRtcMaxMessageSize : bytesLeft); | ||
const buffer = buffer_1.Buffer.from(data, data.byteLength - bytesLeft, bytesToSend); | ||
this.peer.write(buffer); | ||
@@ -224,7 +191,7 @@ bytesLeft -= bytesToSend; | ||
this.emit("bytes-uploaded", data.byteLength); | ||
}; | ||
MediaPeer.prototype.sendSegmentAbsent = function (segmentId) { | ||
} | ||
sendSegmentAbsent(segmentId) { | ||
this.sendCommand({ "command": MediaPeerCommands.SegmentAbsent, "segment_id": segmentId }); | ||
}; | ||
MediaPeer.prototype.requestSegment = function (segmentId) { | ||
} | ||
requestSegment(segmentId) { | ||
if (this.downloadingSegmentId) { | ||
@@ -236,23 +203,22 @@ throw new Error("A segment is already downloading: " + this.downloadingSegmentId); | ||
this.runResponseTimeoutTimer(); | ||
}; | ||
MediaPeer.prototype.cancelSegmentRequest = function () { | ||
} | ||
cancelSegmentRequest() { | ||
if (this.downloadingSegmentId) { | ||
var segmentId = this.downloadingSegmentId; | ||
const segmentId = this.downloadingSegmentId; | ||
this.terminateSegmentRequest(); | ||
this.sendCommand({ "command": MediaPeerCommands.CancelSegmentRequest, "segment_id": segmentId }); | ||
} | ||
}; | ||
MediaPeer.prototype.runResponseTimeoutTimer = function () { | ||
var _this = this; | ||
this.timer = window.setTimeout(function () { | ||
_this.timer = null; | ||
if (!_this.downloadingSegmentId) { | ||
} | ||
runResponseTimeoutTimer() { | ||
this.timer = setTimeout(() => { | ||
this.timer = null; | ||
if (!this.downloadingSegmentId) { | ||
return; | ||
} | ||
var segmentId = _this.downloadingSegmentId; | ||
_this.cancelSegmentRequest(); | ||
_this.emit("segment-timeout", _this, segmentId); // TODO: send peer not responding event | ||
const segmentId = this.downloadingSegmentId; | ||
this.cancelSegmentRequest(); | ||
this.emit("segment-timeout", this, segmentId); // TODO: send peer not responding event | ||
}, this.settings.p2pSegmentDownloadTimeout); | ||
}; | ||
MediaPeer.prototype.cancelResponseTimeoutTimer = function () { | ||
} | ||
cancelResponseTimeoutTimer() { | ||
if (this.timer) { | ||
@@ -262,10 +228,9 @@ clearTimeout(this.timer); | ||
} | ||
}; | ||
MediaPeer.prototype.terminateSegmentRequest = function () { | ||
} | ||
terminateSegmentRequest() { | ||
this.downloadingSegmentId = null; | ||
this.downloadingSegment = null; | ||
this.cancelResponseTimeoutTimer(); | ||
}; | ||
return MediaPeer; | ||
}(stringly_typed_event_emitter_1.default)); | ||
} | ||
} | ||
exports.MediaPeer = MediaPeer; |
@@ -36,2 +36,3 @@ /** | ||
private debug; | ||
private pendingTrackerClient; | ||
constructor(cachedSegments: Map<string, SegmentInternal>, settings: { | ||
@@ -44,3 +45,3 @@ useP2P: boolean; | ||
}); | ||
setSwarmId(swarmId: string): void; | ||
setSwarmId(swarmId: string): Promise<void>; | ||
private createClient; | ||
@@ -47,0 +48,0 @@ private onTrackerPeer; |
@@ -17,51 +17,46 @@ "use strict"; | ||
*/ | ||
var __extends = (this && this.__extends) || (function () { | ||
var extendStatics = function (d, b) { | ||
extendStatics = Object.setPrototypeOf || | ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || | ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; | ||
return extendStatics(d, b); | ||
} | ||
return function (d, b) { | ||
extendStatics(d, b); | ||
function __() { this.constructor = d; } | ||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); | ||
}; | ||
})(); | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var Debug = require("debug"); | ||
var bittorrent_tracker_1 = require("bittorrent-tracker"); | ||
var crypto_1 = require("crypto"); | ||
var stringly_typed_event_emitter_1 = require("./stringly-typed-event-emitter"); | ||
var media_peer_1 = require("./media-peer"); | ||
var PEER_PROTOCOL_VERSION = 1; | ||
var PeerSegmentRequest = /** @class */ (function () { | ||
function PeerSegmentRequest(peerId, segment) { | ||
const Debug = require("debug"); | ||
const bittorrent_tracker_1 = require("bittorrent-tracker"); | ||
const stringly_typed_event_emitter_1 = require("./stringly-typed-event-emitter"); | ||
const media_peer_1 = require("./media-peer"); | ||
const buffer_1 = require("buffer"); | ||
const sha1 = require("sha.js/sha1"); | ||
const PEER_PROTOCOL_VERSION = 1; | ||
class PeerSegmentRequest { | ||
constructor(peerId, segment) { | ||
this.peerId = peerId; | ||
this.segment = segment; | ||
} | ||
return PeerSegmentRequest; | ||
}()); | ||
var P2PMediaManager = /** @class */ (function (_super) { | ||
__extends(P2PMediaManager, _super); | ||
function P2PMediaManager(cachedSegments, settings) { | ||
var _this = _super.call(this) || this; | ||
_this.cachedSegments = cachedSegments; | ||
_this.settings = settings; | ||
_this.trackerClient = null; | ||
_this.peers = new Map(); | ||
_this.peerCandidates = new Map(); | ||
_this.peerSegmentRequests = new Map(); | ||
_this.swarmId = null; | ||
_this.debug = Debug("p2pml:p2p-media-manager"); | ||
_this.onPieceBytesDownloaded = function (bytes) { | ||
_this.emit("bytes-downloaded", bytes); | ||
} | ||
class P2PMediaManager extends stringly_typed_event_emitter_1.default { | ||
constructor(cachedSegments, settings) { | ||
super(); | ||
this.cachedSegments = cachedSegments; | ||
this.settings = settings; | ||
this.trackerClient = null; | ||
this.peers = new Map(); | ||
this.peerCandidates = new Map(); | ||
this.peerSegmentRequests = new Map(); | ||
this.swarmId = null; | ||
this.debug = Debug("p2pml:p2p-media-manager"); | ||
this.pendingTrackerClient = null; | ||
this.onPieceBytesDownloaded = (bytes) => { | ||
this.emit("bytes-downloaded", bytes); | ||
}; | ||
_this.onPieceBytesUploaded = function (bytes) { | ||
_this.emit("bytes-uploaded", bytes); | ||
this.onPieceBytesUploaded = (bytes) => { | ||
this.emit("bytes-uploaded", bytes); | ||
}; | ||
_this.onPeerConnect = function (peer) { | ||
var connectedPeer = _this.peers.get(peer.id); | ||
this.onPeerConnect = (peer) => { | ||
const connectedPeer = this.peers.get(peer.id); | ||
if (connectedPeer) { | ||
_this.debug("tracker peer already connected (in peer connect)", peer.id, peer); | ||
this.debug("tracker peer already connected (in peer connect)", peer.id, peer); | ||
peer.destroy(); | ||
@@ -71,8 +66,7 @@ return; | ||
// First peer with the ID connected | ||
_this.peers.set(peer.id, peer); | ||
this.peers.set(peer.id, peer); | ||
// Destroy all other peer candidates | ||
var peerCandidatesById = _this.peerCandidates.get(peer.id); | ||
const peerCandidatesById = this.peerCandidates.get(peer.id); | ||
if (peerCandidatesById) { | ||
for (var _i = 0, peerCandidatesById_1 = peerCandidatesById; _i < peerCandidatesById_1.length; _i++) { | ||
var peerCandidate = peerCandidatesById_1[_i]; | ||
for (const peerCandidate of peerCandidatesById) { | ||
if (peerCandidate != peer) { | ||
@@ -82,14 +76,14 @@ peerCandidate.destroy(); | ||
} | ||
_this.peerCandidates.delete(peer.id); | ||
this.peerCandidates.delete(peer.id); | ||
} | ||
_this.emit("peer-connected", { id: peer.id, remoteAddress: peer.remoteAddress }); | ||
this.emit("peer-connected", { id: peer.id, remoteAddress: peer.remoteAddress }); | ||
}; | ||
_this.onPeerClose = function (peer) { | ||
if (_this.peers.get(peer.id) != peer) { | ||
this.onPeerClose = (peer) => { | ||
if (this.peers.get(peer.id) != peer) { | ||
// Try to delete the peer candidate | ||
var peerCandidatesById = _this.peerCandidates.get(peer.id); | ||
const peerCandidatesById = this.peerCandidates.get(peer.id); | ||
if (!peerCandidatesById) { | ||
return; | ||
} | ||
var index = peerCandidatesById.indexOf(peer); | ||
const index = peerCandidatesById.indexOf(peer); | ||
if (index != -1) { | ||
@@ -99,20 +93,20 @@ peerCandidatesById.splice(index, 1); | ||
if (peerCandidatesById.length == 0) { | ||
_this.peerCandidates.delete(peer.id); | ||
this.peerCandidates.delete(peer.id); | ||
} | ||
return; | ||
} | ||
_this.peerSegmentRequests.forEach(function (value, key) { | ||
for (const [key, value] of this.peerSegmentRequests) { | ||
if (value.peerId == peer.id) { | ||
_this.peerSegmentRequests.delete(key); | ||
this.peerSegmentRequests.delete(key); | ||
} | ||
}); | ||
_this.peers.delete(peer.id); | ||
_this.emit("peer-data-updated"); | ||
_this.emit("peer-closed", peer.id); | ||
} | ||
this.peers.delete(peer.id); | ||
this.emit("peer-data-updated"); | ||
this.emit("peer-closed", peer.id); | ||
}; | ||
_this.onPeerDataUpdated = function () { | ||
_this.emit("peer-data-updated"); | ||
this.onPeerDataUpdated = () => { | ||
this.emit("peer-data-updated"); | ||
}; | ||
_this.onSegmentRequest = function (peer, segmentId) { | ||
var segment = _this.cachedSegments.get(segmentId); | ||
this.onSegmentRequest = (peer, segmentId) => { | ||
const segment = this.cachedSegments.get(segmentId); | ||
if (segment) { | ||
@@ -125,51 +119,65 @@ peer.sendSegmentData(segmentId, segment.data); | ||
}; | ||
_this.onSegmentLoaded = function (peer, segmentId, data) { | ||
var peerSegmentRequest = _this.peerSegmentRequests.get(segmentId); | ||
this.onSegmentLoaded = (peer, segmentId, data) => { | ||
const peerSegmentRequest = this.peerSegmentRequests.get(segmentId); | ||
if (peerSegmentRequest) { | ||
_this.peerSegmentRequests.delete(segmentId); | ||
_this.emit("segment-loaded", peerSegmentRequest.segment, data); | ||
this.peerSegmentRequests.delete(segmentId); | ||
this.emit("segment-loaded", peerSegmentRequest.segment, data); | ||
} | ||
}; | ||
_this.onSegmentAbsent = function (peer, segmentId) { | ||
_this.peerSegmentRequests.delete(segmentId); | ||
_this.emit("peer-data-updated"); | ||
this.onSegmentAbsent = (peer, segmentId) => { | ||
this.peerSegmentRequests.delete(segmentId); | ||
this.emit("peer-data-updated"); | ||
}; | ||
_this.onSegmentError = function (peer, segmentId, description) { | ||
var peerSegmentRequest = _this.peerSegmentRequests.get(segmentId); | ||
this.onSegmentError = (peer, segmentId, description) => { | ||
const peerSegmentRequest = this.peerSegmentRequests.get(segmentId); | ||
if (peerSegmentRequest) { | ||
_this.peerSegmentRequests.delete(segmentId); | ||
_this.emit("segment-error", peerSegmentRequest.segment, description); | ||
this.peerSegmentRequests.delete(segmentId); | ||
this.emit("segment-error", peerSegmentRequest.segment, description); | ||
} | ||
}; | ||
_this.onSegmentTimeout = function (peer, segmentId) { | ||
var peerSegmentRequest = _this.peerSegmentRequests.get(segmentId); | ||
this.onSegmentTimeout = (peer, segmentId) => { | ||
const peerSegmentRequest = this.peerSegmentRequests.get(segmentId); | ||
if (peerSegmentRequest) { | ||
_this.peerSegmentRequests.delete(segmentId); | ||
this.peerSegmentRequests.delete(segmentId); | ||
peer.destroy(); | ||
if (_this.peers.delete(peerSegmentRequest.peerId)) { | ||
_this.emit("peer-data-updated"); | ||
if (this.peers.delete(peerSegmentRequest.peerId)) { | ||
this.emit("peer-data-updated"); | ||
} | ||
} | ||
}; | ||
_this.peerId = crypto_1.createHash("sha1").update((Date.now() + Math.random()).toFixed(12)).digest("hex"); | ||
_this.debug("peer ID", _this.peerId); | ||
return _this; | ||
this.peerId = crypto.getRandomValues(new Uint8Array(20)).buffer; | ||
if (this.debug.enabled) { | ||
this.debug("peer ID", buffer_1.Buffer.from(this.peerId).toString("hex")); | ||
} | ||
} | ||
P2PMediaManager.prototype.setSwarmId = function (swarmId) { | ||
if (this.swarmId === swarmId) { | ||
return; | ||
} | ||
this.destroy(); | ||
this.swarmId = swarmId; | ||
this.debug("swarm ID", this.swarmId); | ||
this.createClient(crypto_1.createHash("sha1").update(PEER_PROTOCOL_VERSION + this.swarmId).digest("hex")); | ||
}; | ||
P2PMediaManager.prototype.createClient = function (infoHash) { | ||
var _this = this; | ||
setSwarmId(swarmId) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
if (this.swarmId === swarmId) { | ||
return; | ||
} | ||
this.destroy(); | ||
this.swarmId = swarmId; | ||
this.debug("swarm ID", this.swarmId); | ||
this.pendingTrackerClient = { | ||
isDestroyed: false | ||
}; | ||
const pendingTrackerClient = this.pendingTrackerClient; | ||
// TODO: native browser 'crypto.subtle' implementation doesn't work in Chrome in insecure pages | ||
// TODO: Edge doesn't support SHA-1. Change to SHA-256 once Edge support is required. | ||
// const infoHash = await crypto.subtle.digest("SHA-1", new TextEncoder().encode(PEER_PROTOCOL_VERSION + this.swarmId)); | ||
const infoHash = new sha1().update(PEER_PROTOCOL_VERSION + this.swarmId).digest(); | ||
// destroy may be called while waiting for the hash to be calculated | ||
if (!pendingTrackerClient.isDestroyed) { | ||
this.pendingTrackerClient = null; | ||
this.createClient(infoHash); | ||
} | ||
}); | ||
} | ||
createClient(infoHash) { | ||
if (!this.settings.useP2P) { | ||
return; | ||
} | ||
var clientOptions = { | ||
infoHash: infoHash, | ||
peerId: this.peerId, | ||
const clientOptions = { | ||
infoHash: buffer_1.Buffer.from(infoHash, 0, 20), | ||
peerId: buffer_1.Buffer.from(this.peerId, 0, 20), | ||
announce: this.settings.trackerAnnounce, | ||
@@ -179,9 +187,9 @@ rtcConfig: this.settings.rtcConfig | ||
this.trackerClient = new bittorrent_tracker_1.Client(clientOptions); | ||
this.trackerClient.on("error", function (error) { return _this.debug("tracker error", error); }); | ||
this.trackerClient.on("warning", function (error) { return _this.debug("tracker warning", error); }); | ||
this.trackerClient.on("update", function (data) { return _this.debug("tracker update", data); }); | ||
this.trackerClient.on("error", (error) => this.debug("tracker error", error)); | ||
this.trackerClient.on("warning", (error) => this.debug("tracker warning", error)); | ||
this.trackerClient.on("update", (data) => this.debug("tracker update", data)); | ||
this.trackerClient.on("peer", this.onTrackerPeer.bind(this)); | ||
this.trackerClient.start(); | ||
}; | ||
P2PMediaManager.prototype.onTrackerPeer = function (trackerPeer) { | ||
} | ||
onTrackerPeer(trackerPeer) { | ||
this.debug("tracker peer", trackerPeer.id, trackerPeer); | ||
@@ -193,3 +201,3 @@ if (this.peers.has(trackerPeer.id)) { | ||
} | ||
var peer = new media_peer_1.MediaPeer(trackerPeer, this.settings); | ||
const peer = new media_peer_1.MediaPeer(trackerPeer, this.settings); | ||
peer.on("connect", this.onPeerConnect); | ||
@@ -205,3 +213,3 @@ peer.on("close", this.onPeerClose); | ||
peer.on("bytes-uploaded", this.onPieceBytesUploaded); | ||
var peerCandidatesById = this.peerCandidates.get(peer.id); | ||
let peerCandidatesById = this.peerCandidates.get(peer.id); | ||
if (!peerCandidatesById) { | ||
@@ -212,10 +220,8 @@ peerCandidatesById = []; | ||
peerCandidatesById.push(peer); | ||
}; | ||
P2PMediaManager.prototype.download = function (segment) { | ||
} | ||
download(segment) { | ||
if (this.isDownloading(segment)) { | ||
return false; | ||
} | ||
var entries = this.peers.values(); | ||
for (var entry = entries.next(); !entry.done; entry = entries.next()) { | ||
var peer = entry.value; | ||
for (const peer of this.peers.values()) { | ||
if ((peer.getDownloadingSegmentId() == null) && | ||
@@ -229,7 +235,7 @@ (peer.getSegmentsMap().get(segment.id) === media_peer_1.MediaPeerSegmentStatus.Loaded)) { | ||
return false; | ||
}; | ||
P2PMediaManager.prototype.abort = function (segment) { | ||
var peerSegmentRequest = this.peerSegmentRequests.get(segment.id); | ||
} | ||
abort(segment) { | ||
const peerSegmentRequest = this.peerSegmentRequests.get(segment.id); | ||
if (peerSegmentRequest) { | ||
var peer = this.peers.get(peerSegmentRequest.peerId); | ||
const peer = this.peers.get(peerSegmentRequest.peerId); | ||
if (peer) { | ||
@@ -240,10 +246,10 @@ peer.cancelSegmentRequest(); | ||
} | ||
}; | ||
P2PMediaManager.prototype.isDownloading = function (segment) { | ||
} | ||
isDownloading(segment) { | ||
return this.peerSegmentRequests.has(segment.id); | ||
}; | ||
P2PMediaManager.prototype.getActiveDownloadsCount = function () { | ||
} | ||
getActiveDownloadsCount() { | ||
return this.peerSegmentRequests.size; | ||
}; | ||
P2PMediaManager.prototype.destroy = function () { | ||
} | ||
destroy() { | ||
this.swarmId = null; | ||
@@ -255,36 +261,40 @@ if (this.trackerClient) { | ||
} | ||
this.peers.forEach(function (peer) { return peer.destroy(); }); | ||
if (this.pendingTrackerClient) { | ||
this.pendingTrackerClient.isDestroyed = true; | ||
this.pendingTrackerClient = null; | ||
} | ||
this.peers.forEach(peer => peer.destroy()); | ||
this.peers.clear(); | ||
this.peerSegmentRequests.clear(); | ||
this.peerCandidates.forEach(function (peerCandidateById) { | ||
for (var _i = 0, peerCandidateById_1 = peerCandidateById; _i < peerCandidateById_1.length; _i++) { | ||
var peerCandidate = peerCandidateById_1[_i]; | ||
for (const peerCandidateById of this.peerCandidates.values()) { | ||
for (const peerCandidate of peerCandidateById) { | ||
peerCandidate.destroy(); | ||
} | ||
}); | ||
} | ||
this.peerCandidates.clear(); | ||
}; | ||
P2PMediaManager.prototype.sendSegmentsMapToAll = function (segmentsMap) { | ||
this.peers.forEach(function (peer) { return peer.sendSegmentsMap(segmentsMap); }); | ||
}; | ||
P2PMediaManager.prototype.sendSegmentsMap = function (peerId, segmentsMap) { | ||
var peer = this.peers.get(peerId); | ||
} | ||
sendSegmentsMapToAll(segmentsMap) { | ||
this.peers.forEach(peer => peer.sendSegmentsMap(segmentsMap)); | ||
} | ||
sendSegmentsMap(peerId, segmentsMap) { | ||
const peer = this.peers.get(peerId); | ||
if (peer) { | ||
peer.sendSegmentsMap(segmentsMap); | ||
} | ||
}; | ||
P2PMediaManager.prototype.getOvrallSegmentsMap = function () { | ||
var overallSegmentsMap = new Map(); | ||
this.peers.forEach(function (peer) { return peer.getSegmentsMap().forEach(function (segmentStatus, segmentId) { | ||
if (segmentStatus === media_peer_1.MediaPeerSegmentStatus.Loaded) { | ||
overallSegmentsMap.set(segmentId, media_peer_1.MediaPeerSegmentStatus.Loaded); | ||
} | ||
getOvrallSegmentsMap() { | ||
const overallSegmentsMap = new Map(); | ||
for (const peer of this.peers.values()) { | ||
for (const [segmentId, segmentStatus] of peer.getSegmentsMap()) { | ||
if (segmentStatus === media_peer_1.MediaPeerSegmentStatus.Loaded) { | ||
overallSegmentsMap.set(segmentId, media_peer_1.MediaPeerSegmentStatus.Loaded); | ||
} | ||
else if (!overallSegmentsMap.get(segmentId)) { | ||
overallSegmentsMap.set(segmentId, media_peer_1.MediaPeerSegmentStatus.LoadingByHttp); | ||
} | ||
} | ||
else if (!overallSegmentsMap.get(segmentId)) { | ||
overallSegmentsMap.set(segmentId, media_peer_1.MediaPeerSegmentStatus.LoadingByHttp); | ||
} | ||
}); }); | ||
} | ||
return overallSegmentsMap; | ||
}; | ||
return P2PMediaManager; | ||
}(stringly_typed_event_emitter_1.default)); | ||
} | ||
} | ||
exports.P2PMediaManager = P2PMediaManager; |
@@ -18,7 +18,4 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var SegmentInternal = /** @class */ (function () { | ||
function SegmentInternal(id, url, range, priority, data, downloadSpeed) { | ||
if (priority === void 0) { priority = 0; } | ||
if (data === void 0) { data = undefined; } | ||
if (downloadSpeed === void 0) { downloadSpeed = 0; } | ||
class SegmentInternal { | ||
constructor(id, url, range, priority = 0, data = undefined, downloadSpeed = 0) { | ||
this.id = id; | ||
@@ -32,4 +29,3 @@ this.url = url; | ||
} | ||
return SegmentInternal; | ||
}()); | ||
} | ||
exports.SegmentInternal = SegmentInternal; |
@@ -18,13 +18,12 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var SMOOTH_INTERVAL = 4 * 1000; | ||
var MEASURE_INTERVAL = 60 * 1000; | ||
var NumberWithTime = /** @class */ (function () { | ||
function NumberWithTime(value, timeStamp) { | ||
const SMOOTH_INTERVAL = 4 * 1000; | ||
const MEASURE_INTERVAL = 60 * 1000; | ||
class NumberWithTime { | ||
constructor(value, timeStamp) { | ||
this.value = value; | ||
this.timeStamp = timeStamp; | ||
} | ||
return NumberWithTime; | ||
}()); | ||
var SpeedApproximator = /** @class */ (function () { | ||
function SpeedApproximator() { | ||
} | ||
class SpeedApproximator { | ||
constructor() { | ||
this.lastBytes = []; | ||
@@ -34,3 +33,3 @@ this.currentBytesSum = 0; | ||
} | ||
SpeedApproximator.prototype.addBytes = function (bytes, timeStamp) { | ||
addBytes(bytes, timeStamp) { | ||
this.lastBytes.push(new NumberWithTime(bytes, timeStamp)); | ||
@@ -42,11 +41,10 @@ this.currentBytesSum += bytes; | ||
this.lastSpeed.push(new NumberWithTime(this.currentBytesSum / SMOOTH_INTERVAL, timeStamp)); | ||
}; | ||
} | ||
// in bytes per millisecond | ||
SpeedApproximator.prototype.getSpeed = function (timeStamp) { | ||
getSpeed(timeStamp) { | ||
while (this.lastSpeed.length != 0 && timeStamp - this.lastSpeed[0].timeStamp > MEASURE_INTERVAL) { | ||
this.lastSpeed.shift(); | ||
} | ||
var maxSpeed = 0; | ||
for (var _i = 0, _a = this.lastSpeed; _i < _a.length; _i++) { | ||
var speed = _a[_i]; | ||
let maxSpeed = 0; | ||
for (const speed of this.lastSpeed) { | ||
if (speed.value > maxSpeed) { | ||
@@ -57,11 +55,10 @@ maxSpeed = speed.value; | ||
return maxSpeed; | ||
}; | ||
SpeedApproximator.prototype.getSmoothInterval = function () { | ||
} | ||
getSmoothInterval() { | ||
return SMOOTH_INTERVAL; | ||
}; | ||
SpeedApproximator.prototype.getMeasureInterval = function () { | ||
} | ||
getMeasureInterval() { | ||
return MEASURE_INTERVAL; | ||
}; | ||
return SpeedApproximator; | ||
}()); | ||
} | ||
} | ||
exports.SpeedApproximator = SpeedApproximator; |
@@ -16,3 +16,2 @@ /** | ||
*/ | ||
/// <reference types="node" /> | ||
import { EventEmitter } from "events"; | ||
@@ -19,0 +18,0 @@ export default class<T extends string> extends EventEmitter { |
@@ -17,32 +17,8 @@ "use strict"; | ||
*/ | ||
var __extends = (this && this.__extends) || (function () { | ||
var extendStatics = function (d, b) { | ||
extendStatics = Object.setPrototypeOf || | ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || | ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; | ||
return extendStatics(d, b); | ||
} | ||
return function (d, b) { | ||
extendStatics(d, b); | ||
function __() { this.constructor = d; } | ||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); | ||
}; | ||
})(); | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var events_1 = require("events"); | ||
var default_1 = /** @class */ (function (_super) { | ||
__extends(default_1, _super); | ||
function default_1() { | ||
return _super !== null && _super.apply(this, arguments) || this; | ||
} | ||
default_1.prototype.on = function (event, listener) { return _super.prototype.on.call(this, event, listener); }; | ||
default_1.prototype.emit = function (event) { | ||
var args = []; | ||
for (var _i = 1; _i < arguments.length; _i++) { | ||
args[_i - 1] = arguments[_i]; | ||
} | ||
return _super.prototype.emit.apply(this, [event].concat(args)); | ||
}; | ||
return default_1; | ||
}(events_1.EventEmitter)); | ||
const events_1 = require("events"); | ||
class default_1 extends events_1.EventEmitter { | ||
on(event, listener) { return super.on(event, listener); } | ||
emit(event, ...args) { return super.emit(event, ...args); } | ||
} | ||
exports.default = default_1; |
{ | ||
"name": "p2p-media-loader-core", | ||
"description": "P2P Media Loader core functionality", | ||
"version": "0.2.1", | ||
"version": "0.3.0", | ||
"license": "Apache-2.0", | ||
@@ -27,9 +27,9 @@ "author": "Novage", | ||
"compile": "tsc && copyfiles -f ./lib/*.js ./dist", | ||
"browserify": "mkdirp ./build && browserify -r ./dist/index.js:p2p-media-loader-core ./dist/browser-init.js > ./build/p2p-media-loader-core.js", | ||
"minify": "uglifyjs ./build/p2p-media-loader-core.js -m -c --comments > ./build/p2p-media-loader-core.min.js", | ||
"browserify": "mkdirp ./build && browserify -r ./dist/index.js:p2p-media-loader-core -r debug -r events ./dist/browser-init.js > ./build/p2p-media-loader-core.js", | ||
"minify": "terser ./build/p2p-media-loader-core.js -m -c > ./build/p2p-media-loader-core.min.js", | ||
"build": "npm run compile && npm run browserify && npm run minify", | ||
"webpack:build": "webpack --progress", | ||
"webpack:watch": "webpack --watch --progress", | ||
"lint": "tslint -c ./tslint.json -p ./tsconfig.test.json", | ||
"test": "mocha -r ts-node/register test/*.test.ts" | ||
"lint": "tslint -c ./tslint.json -p ./tsconfig.tslint.json", | ||
"test": "TS_NODE_PROJECT=tsconfig.test.json TS_NODE_CACHE=false mocha -r ts-node/register test/*.test.ts" | ||
}, | ||
@@ -42,11 +42,13 @@ "repository": { | ||
"bittorrent-tracker": "^9.10.1", | ||
"crypto-browserify": "^3.12.0", | ||
"debug": "^3.2.6", | ||
"debug": "^4.1.0", | ||
"events": "^3.0.0", | ||
"get-browser-rtc": "^1.0.2" | ||
"get-browser-rtc": "^1.0.2", | ||
"sha.js": "^2.4.11" | ||
}, | ||
"devDependencies": { | ||
"@types/assert": "^1.4.0", | ||
"@types/debug": "^0.0.31", | ||
"@types/events": "^1.2.0", | ||
"@types/mocha": "^5.2.5", | ||
"@types/node": "^10.12.9", | ||
"@types/node": "^10.12.11", | ||
"browserify": "^16.2.3", | ||
@@ -57,9 +59,9 @@ "browserify-versionify": "^1.0.6", | ||
"mocha": "^5.2.0", | ||
"ts-loader": "^5.3.0", | ||
"terser": "^3.11.0", | ||
"ts-loader": "^5.3.1", | ||
"ts-mockito": "^2.3.1", | ||
"ts-node": "^7.0.1", | ||
"tslint": "^5.11.0", | ||
"typescript": "^3.1.6", | ||
"uglify-js": "^3.4.9", | ||
"webpack": "^4.25.1", | ||
"typescript": "^3.2.1", | ||
"webpack": "^4.26.1", | ||
"webpack-cli": "^3.1.2" | ||
@@ -66,0 +68,0 @@ }, |
@@ -5,3 +5,3 @@ # P2P Media Loader Core | ||
Core functionality for P2P sharing of segmented media streams (i.e. HLS, DASH) using WebRTC. | ||
Core functionality for P2P sharing of segmented media streams (i.e. HLS, MPEG-DASH) using WebRTC. | ||
@@ -8,0 +8,0 @@ Useful links: |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
6
0
568139
18
12143
+ Addedsha.js@^2.4.11
+ Addedstring_decoder@1.3.0(transitive)
- Removedcrypto-browserify@^3.12.0
- Removedasn1.js@4.10.1(transitive)
- Removedbn.js@4.12.0(transitive)
- Removedbrorand@1.1.0(transitive)
- Removedbrowserify-aes@1.2.0(transitive)
- Removedbrowserify-cipher@1.0.1(transitive)
- Removedbrowserify-des@1.0.2(transitive)
- Removedbrowserify-rsa@4.1.1(transitive)
- Removedbrowserify-sign@4.2.3(transitive)
- Removedbuffer-xor@1.0.3(transitive)
- Removedcipher-base@1.0.4(transitive)
- Removedcore-util-is@1.0.3(transitive)
- Removedcreate-ecdh@4.0.4(transitive)
- Removedcreate-hash@1.2.0(transitive)
- Removedcreate-hmac@1.1.7(transitive)
- Removedcrypto-browserify@3.12.1(transitive)
- Removeddebug@3.2.7(transitive)
- Removeddes.js@1.1.0(transitive)
- Removeddiffie-hellman@5.0.3(transitive)
- Removedelliptic@6.6.0(transitive)
- Removedevp_bytestokey@1.0.3(transitive)
- Removedhash-base@3.0.4(transitive)
- Removedhash.js@1.1.7(transitive)
- Removedhmac-drbg@1.0.1(transitive)
- Removedisarray@1.0.0(transitive)
- Removedmd5.js@1.3.5(transitive)
- Removedmiller-rabin@4.0.1(transitive)
- Removedminimalistic-assert@1.0.1(transitive)
- Removedminimalistic-crypto-utils@1.0.1(transitive)
- Removedparse-asn1@5.1.7(transitive)
- Removedpbkdf2@3.1.2(transitive)
- Removedprocess-nextick-args@2.0.1(transitive)
- Removedpublic-encrypt@4.0.3(transitive)
- Removedrandomfill@1.0.4(transitive)
- Removedreadable-stream@2.3.8(transitive)
- Removedripemd160@2.0.2(transitive)
- Removedsafe-buffer@5.1.2(transitive)
- Removedstring_decoder@1.1.1(transitive)
Updateddebug@^4.1.0