-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathYoutubeStreamerServer.js
More file actions
102 lines (88 loc) · 3.37 KB
/
YoutubeStreamerServer.js
File metadata and controls
102 lines (88 loc) · 3.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
var url = require('url');
var request = require('request');
var NetworkStreamer = require('./NetworkStreamer.js').NetworkStreamer;
var YoutubeVideoExtractor = require('./YoutubeVideoExtractor.js').YoutubeVideoExtractor;
/**
YoutubeStreamerServer takes socket.io server as parameter and creates /stream websocket endpoint.
It listens for client requests and when streaming request is received, it creates YoutubeVideoExtractor promise.
When the extractor fulfils, the streamer creates a NetworkStreamer instance and starts a network request, which is piped to the NetworkStreamer.
NetworkStreamer then sends all the output to the client using socket.io.
*/
function YoutubeStreamerServer(io)
{
var self = this;
this.io = io;
//to keep track of connected sockets
var connections = {};
//remove socket from the connections structure if it exists,
//removing running requests if any.
function dropConnection(socket) {
if (socket.id in connections) {
var req = connections[socket.id].request;
if (req != null) {
req.abort();
req = null;
}
delete connections[socket.id];
}
}
this.io.of('/stream').on('connection', function(socket){
//add socket to the connections structure
connections[socket.id] = {request : null};
//remove socket from connections structure when its disconnected
socket.on('disconnect', function(){
dropConnection(socket);
});
//listen for streamYoutubeVideoRequest messages
socket.on('streamYoutubeVideoRequest', function(data, callback)
{
//extract video id from the url passed by the client
var videoId = url.parse(data.url, true).query.v;
//if we have found the videoId
if (videoId != undefined) {
//create YoutubeVideoExtractor promise
new YoutubeVideoExtractor(videoId)
.then(function(extractor){
//youtube formats were succesfully extracted
var formats = extractor.getFormats();
//check if audio only (itag=140) format is included
if (!formats.hasFormat(140)){
throw new Error("Video does not include AAC audio only format!");
}
//start the network streamer
//on completion let the client know how many total bytes were downloaded in this request,
//then remove client from the connections structure
var streamer = new NetworkStreamer(socket, {highWaterMark:65536});
streamer.on('finish', function(){
socket.emit('streamYoutubeVideoComplete', {sizeOnServer: (this.getTotalSize())});
dropConnection(socket);
});
//extract the audio only, aac format
var aac = formats.getFormat(140);
//compute the url for the format
var url = aac.getUrl();
//send the information to the client
callback({
status : "success",
fileName : encodeURIComponent(extractor.getInfo().getKey("title")),
fileSize : aac.getParams().getKey("clen"),
fileExt : ".m4a",
fileUrl : url
});
//download the video and pipe it to the network streamer
connections[socket.id].request = request(url);
connections[socket.id].request.pipe(streamer);
})
.catch(function(e){
console.error(e);
socket.emit('streamError', {reason: e.message});
dropConnection(socket);
});
} else {
socket.emit('streamError', {reason: "Provided url does not include video id."});
dropConnection(socket);
}
});
});
}
exports.YoutubeStreamerServer = YoutubeStreamerServer;