My goal is to stream a video file into chunks rather than loading and playing the entire file at once. It works great on Google Chrome and I am able to see the chunk sizes as they are logged to the console. Now when I try this on firefox but I'm getting nothing logged to the console. The video will play, but I am unable to fast forward past the 10 minute or so mark. Just curious why it works flawlessly in Chrome and not firefox?
router.get("/video", ensureAuthenticated, function(req, res) {
var id = req.query.id;
let path = "";
for (let k in movieLinks) {
if (k === id) {
path = movieLinks[k]
}
}
// const path = "public/movies/noes2.mp4";
const stat = fs.statSync(path);
const fileSize = stat.size;
const range = req.headers.range;
if (range) {
const parts = range.replace(/bytes=/, "").split("-");
const start = parseInt(parts[0],10);
const end = parts[1] ? parseInt(parts[1], 10) : fileSize-1;
const chunkSize = (end - start) + 1;
console.log('RANGE: ' + start + ' - ' + end + ' = ' + chunkSize);
const file = fs.createReadStream(path, {start,end} );
const head = {
'Content-Range' : `bytes ${start}-${end}/${fileSize}`,
'Content-Ranges' : 'bytes',
'Content-Length' : chunkSize,
'Content-Type' : 'video/mp4'
}
res.writeHead(206, head);
file.pipe(res);
} else {
const head = {
'Content-Length' : fileSize,
'Content-Type' : 'video/mp4'
}
res.writeHead(200,head);
fs.createReadStream(path).pipe(res);
}
// res.render('video', {layout: 'videoLayout'});
})
I done this before splitting video files with tool call ffmpeg.
You can use the script below to split your video in 30 second chunks read them from a s3 bucket or wherever they may live. This will make browser download easier.
ffmpeg script example:
ffmpeg -i input -c copy -segment_time 30 -f segment input.mov
I hope this helps
Related
This is my code below that takes get request from client and sends video
const {name} = req.params;
// console.log(req.headers)
let range = req.headers.range
console.log(req.headers);
// if(!range) range = 'bytes=0-'
if(req.headers.referer !== "https://course-client-nine.vercel.app/"){
return res.json({message: "No acces from another domain"})
}
const myBucket = storage.bucket('coursebuckets');
const file = myBucket.file(name);
if(!file){
return res.json({
message:"File not found",
code:401
})
}
const [metadata] = await file.getMetadata();
const videoSize = metadata.size;
// const videoSize = fs.statSync('123.mp4').size
const chunkSize = 1 * 1e+6;
const start = Number(range.replace(/\D/g, ''));
let end = Math.min(start+ chunkSize, videoSize - 1);
let contentLength = end - start + 1;
const headers = {
"Content-Range": `bytes ${start}-${end}/${videoSize}`,
"Accept-Ranges": 'bytes',
"Content-Length" : contentLength,
"Content-Type" : 'video/mp4'
}
res.writeHead(206,headers)
const readStream = file.createReadStream({start, end});
readStream.pipe(res);
readStream.on('error', (error) => {
console.log(error);
})
In chrome it works well but in ios devices it doesn't, no idea and no error, what should i do?
It doesn't look like you're properly parsing and using the requested byte range. So, you respond with a range that is not what Safari asked for. You appear to be crudely whacking off all non-numeric characters and assuming what's left is the start value and then making your own gigantic chunk size, but that's not the way you're supposed to be parsing the range. You are not honoring what the client asked for and Safari is pickier than Chrome in that regard.
If you log the request range from the client and then log what you're sending, you will see that they do not match.
From this article which is about making streaming work with Safari, here's some range parsing code that gives you the actual requested start and end:
if (range) {
const bytesPrefix = "bytes=";
if (range.startsWith(bytesPrefix)) {
const bytesRange = range.substring(bytesPrefix.length);
const parts = bytesRange.split("-");
if (parts.length === 2) {
const rangeStart = parts[0] && parts[0].trim();
if (rangeStart && rangeStart.length > 0) {
options.start = start = parseInt(rangeStart);
}
const rangeEnd = parts[1] && parts[1].trim();
if (rangeEnd && rangeEnd.length > 0) {
options.end = end = parseInt(rangeEnd);
}
}
}
}
I have some huge files which are difficult to read in memory. I need to read each line and then replace double quotes if found and edit the same file. Right now, I am reading the file line by line, storing in an array and overwriting the same file. But, that's giving memory issue for big files. Any pointers ?
Here is my present implementation :
var allData = fs.readFileSync(fileName, { encoding: 'utf8' }).toString().split("\n");
var finalString = "";
for (i in allData) {
allData[i] = allData[i].replace(/"/g, '""');
finalString = finalString.concat(allData[i]);
finalString = finalString.concat("\n");
}
fs.writeFileSync(fileName, finalString);
Is there a way to edit by reading one line at a time and changing that in the file?
I have seen the similar question with scramjet, but that gives an error and is not compatible with all nodejs versions : node.js modify file data stream?
After going through a lot of answers, this worked for me which took care of the required synchronous and asynchronous behaviour, large file and keeping the name same.
function format_file(fileName) {
return new Promise((resolve, reject) => {
if (fs.existsSync(fileName)) {
var fields = fileName.split('/');
var tempFile = "";
var arrayLength = fields.length;
for (var i = 0; i < arrayLength - 1; i++) {
tempFile = tempFile + fields[i] + "/";
}
tempFile = tempFile + "tempFile" + fields[arrayLength - 1];
console.log("tempfile name is : " + tempFile + " actualFileName is :" + fileName);
var processStream = new ProcessStream();
fs.createReadStream(fileName, { bufferSize: 128 * 4096 })
.pipe(processStream)
.pipe(fs.createWriteStream(tempFile)).on('finish', function() { // finished
fs.renameSync(tempFile, fileName);
console.log('done encrypting');
resolve('done');
});
} else {
reject('path not found')
}
});
}
I'm working on a video streaming component and videos seem to be working normally on every platform except for iOS 12. The video works for previous iOS versions (10.3 and 11.0). When I check the network requests, I get the following error. I've tried looking through changes made in iOS 12 related to video streaming but I couldn't find anything specific. Any help would be appreciated! I've also attached the code for the stream endpoint below. Thanks!
exports.DECRYPT = async function(req, res, {
content_id
}) {
let result = await FileFactory.Decrypt({
content_id
});
if (result.content_type.includes('video')) {
let fileSize = result.original_size;
const range = req.headers.range;
const parts = range ? range.replace(/bytes=/, "").split("-") : undefined;
const start = parts ? parseInt(parts[0], 10) : undefined;
const end = parts && parts[1] ? parseInt(parts[1], 10) : fileSize - 1;
res.setHeader('Accept-Ranges', 'bytes');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Content-Type', result.content_type);
res.setHeader('Content-Length', end - start + 1);
res.setHeader('Content-Range', `bytes ${start}-${end}/${fileSize}`);
res.setHeader('Connection', 'Keep-Alive');
res.setHeader('Content-Encoding', 'identity');
if (start === 0 && !(parts[1])) {
res.statusCode = 200;
} else {
res.statusCode = 206;
}
let stream = request({
url: result.url
}).pipe(result.decipher);
let pointer = 0;
stream.on('data', (chunk) => {
pointer += chunk.length;
if (pointer > start) {
res.write(chunk.slice(start - pointer, end + 1));
}
if (pointer > end) {
stream.destroy("Chunk loaded");
res.end();
}
});
stream.on('error', function(e) {
});
stream.on('end', () => {
res.end();
});
} else {
res.setHeader('Content-Type', result.content_type);
res.setHeader('Accept-Ranges', 'bytes');
res.statusCode = 200;
request(result.url).pipe(result.decipher).pipe(res);
}
};
It appears that safari has some issue with HTTP/2. After some research, I ended up removing HTTP/2 configuration from nginx.
I have been using git bash in Windows 10, to try out the telnet command. When communicating between different clients(terminal windows), it is found that only a single character is sent.
let net = require('net');
let chatServer = net.createServer();
let clientList = [];
const broadcast = (message, client) => {
for (let i = 0; i < clientList.length; ++i) {
if (client !== clientList[i]) {
clientList[i].write(client.name + ": " + message + "\n");
}
}
};
chatServer.on('connection', (client) => {
client.name = client.remoteAddress + ':' + client.remotePort;
client.write("Hi, " + client.name + "!\n");
clientList.push(client);
client.on('data', (data) => {
broadcast(data, client);
});
});
chatServer.listen(9000);
Above is my source code with Node in javascript. The response of message "abc" is like below.
Hi, ::ffff:127.0.0.1:51278!
::ffff:127.0.0.1:51285: a
::ffff:127.0.0.1:51285: b
::ffff:127.0.0.1:51285: c
I just wanted to have this communication goes under line by line mode, rather than sending a single character per key stroke. Any suggestions?
I used phogap function for recording audio and uploading audio , file.
Audio File is recording and stored in music folder and I get the path and name of the file by the below function.
function captureSuccess(mediaFiles) {
var i, len;
for (i = 0, len = mediaFiles.length; i < len; i += 1) {
uploadFile(mediaFiles[i]);
}
alert('over');
}
function captureAudio() {
navigator.device.capture.captureAudio(captureSuccess, captureError, { limit: 1, duration: 10 });
}
function uploadFile(mediaFile) {
var ft = new FileTransfer(),
path = mediaFile.fullPath,
name = mediaFile.name;
temp = path; //Assigned path to GLOBAL VARIABLE
temp1 = name;
alert(temp);
}
I call the function to upload the created audio file.
function uplod() {
var options = new FileUploadOptions();
options.fileKey = "file";
options.fileName = name;
options.mimeType = "audio/mpeg";
options.chunkedMode = false;
var ft = new FileTransfer();
ft.upload(temp,
"http:/URL/upload.php",
function (result) {
alert("success" + result.bytesSent + result.responseCode);
console.log('Upload success: ' + result.responseCode);
console.log(result.bytesSent + ' bytes sent');
},
function (error) {
console.log('Error uploading file ' + recordingPath + ': ' + error.code);
},
options);
}
But on running, I get these error, got stucked up, is the error is with URL or PATH ? Unicode ? and path of file is like this
C:/Users/AB/Music/captureAudio (4).mp3
0x80070459 - JavaScript runtime error: No mapping for the Unicode character exists in the target multi-byte code page.
WinRT information: No mapping for the Unicode character exists in the target multi-byte code page.
I got the answer by using Ajax post and by using Formdata() to server there by getting in the form of arrays.