How to combine a video with an audio in javascript? - javascript

I am coding youtube video downloader chrome extension. But youtube has separated mp4 and mp3. How can I combine the audio file and image file I received in blob type and turn it into a video with sound?
async function downloadFile(urlToSend) {
return new Promise(resolve => {
var req = new XMLHttpRequest();
req.open("GET", urlToSend, true);
req.responseType = "blob";
req.onload = function (event) {
// var blob = req.response;
// var fileName = req.getResponseHeader("fileName") //if you have the fileName header available
// var link = document.createElement('a');
// link.href = window.URL.createObjectURL(blob);
resolve(req.response)
};
req.send();
})
};
async function zfc() {
var v = await downloadFile('/videoplayback.mp4')
var a = await downloadFile('/videoplayback.weba')
let newBlob = new Blob([v, a], { type: 'video/mp4' })
var as = document.createElement('a')
as.href = window.URL.createObjectURL(newBlob)
as.download = window.URL.createObjectURL(newBlob)
console.log(as)
console.log(newBlob)
// as.click()
var c = document.createElement('video')
c.src = window.URL.createObjectURL(newBlob)
document.body.appendChild(c)
}
zfc()
I tried merging with new blob but the video still has no sound. Can you please help?
Example video link:
https://rr7---sn-u0g3uxax3-xncs.googlevideo.com/videoplayback?expire=1641956798&ei=XvHdYbG8MI2qx_AP14yRoAQ&ip=95.2.13.77&id=o-APHbyEMFJZdr7FwyLDOkQWqycmDmo9oy8bSvx7qP4z-P&itag=313&aitags=133%2C134%2C135%2C136%2C137%2C160%2C242%2C243%2C244%2C247%2C248%2C271%2C278%2C313&source=youtube&requiressl=yes&mh=YY&mm=31%2C29&mn=sn-u0g3uxax3-xncs%2Csn-hgn7yn76&ms=au%2Crdu&mv=m&mvi=7&pl=21&initcwndbps=88750&vprv=1&mime=video%2Fwebm&ns=O-4SxebNzTxani0g_ScQEtMG&gir=yes&clen=589586219&dur=347.800&lmt=1638064072881015&mt=1641934876&fvip=2&keepalive=yes&fexp=24001373%2C24007246&c=WEB&txp=4532434&n=hBnxjZJEX82hOJ&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cdur%2Clmt&sig=AOq0QJ8wRQIhAIu0SR_UsiQyUpJIkL_erKc_dElHk-1rwJMCI1486YaSAiBkH4jg8WHzRvEDsxnTTheBM_f1KsBFzqLiIUFJAIKh5w%3D%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRAIgapuFt6YijG3nAVfbULkJq2_uAwcjOnZpd0ZNUo1h5NMCIGgJh22ksRMeMOUkhhQUlRapjqa4DhVv-KfcfnYhkW8l
Example sound link:
https://rr7---sn-u0g3uxax3-xncs.googlevideo.com/videoplayback?expire=1641956798&ei=XvHdYbG8MI2qx_AP14yRoAQ&ip=95.2.13.77&id=o-APHbyEMFJZdr7FwyLDOkQWqycmDmo9oy8bSvx7qP4z-P&itag=251&source=youtube&requiressl=yes&mh=YY&mm=31%2C29&mn=sn-u0g3uxax3-xncs%2Csn-hgn7yn76&ms=au%2Crdu&mv=m&mvi=7&pl=21&initcwndbps=88750&vprv=1&mime=audio%2Fwebm&ns=O-4SxebNzTxani0g_ScQEtMG&gir=yes&clen=5822955&dur=347.821&lmt=1638059244799001&mt=1641934876&fvip=2&keepalive=yes&fexp=24001373%2C24007246&c=WEB&txp=4532434&n=hBnxjZJEX82hOJ&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cdur%2Clmt&sig=AOq0QJ8wRQIgaqKAjgRHlNms4IMVKwGJmRb2DOl7slWujc2OeIqIlSkCIQDvVhAPmxgLg0g2WvrgjB0iNNnCyDbyRQQvu5ODx4PLXA%3D%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRAIgapuFt6YijG3nAVfbULkJq2_uAwcjOnZpd0ZNUo1h5NMCIGgJh22ksRMeMOUkhhQUlRapjqa4DhVv-KfcfnYhkW8l

I am also currently working on a YouTube video downloader extension for Firefox. If you are using ytdl-core, the response object contains a link that has both in res.player_response.streaming_data.formats[0]. However, there is only one such link, which doesn't allow for users to select their preferred resolution, so being able to merge the two would be extremely helpful.
Also if you are using youtube-dl or anything similar to such the response object should be the same or very similar to ytdl-core's

Related

XMLHTTPrequest plain text to blob (video)

I achieved to get a video from php using this code :
var some_video_element = document.querySelector('video')
var req = new XMLHttpRequest();
req.onload = function () {
var blob_uri = URL.createObjectURL(this.response);
some_video_element.src = blob_uri;
some_video_element.addEventListener('oncanplaythrough', (e) => {
URL.revokeObjectURL(blob_uri);
});
};
req.open("get", "vid.php", true);
req.overrideMimeType('blob');
req.send(null);
However, the loading is long so I want to show data as soon as I get it. From Mozilia, it is indicated we can use plain or "" as mime to get the text in progress. However, I can't achieve to convert plain/text to video/mp4 using a blob. Currently this is the code that doesn't work. I try to get the video when some part is available while the rest is still downloading.
var some_video_element = document.querySelector('video')
var req = new XMLHttpRequest();
req.onprogress = function () {
var text = b64toBlob(Base64.encode(this.response), "video/mp4");
var blob_uri = URL.createObjectURL(text);
some_video_element.src = blob_uri;
some_video_element.addEventListener('oncanplaythrough', (e) => {
URL.revokeObjectURL(blob_uri);
});
};
req.onload = function () {
var text = b64toBlob(this.response, "video/mp4");
var blob_uri = URL.createObjectURL(text);
some_video_element.src = blob_uri;
some_video_element.addEventListener('oncanplaythrough', (e) => {
URL.revokeObjectURL(blob_uri);
});
};
req.open("get", "vid.php", true);
req.overrideMimeType('text\/plain');
req.send(null);
Thanks.
NB : This JavaScript is fetching for this php code : https://codesamplez.com/programming/php-html5-video-streaming-tutorial
But echo data has been changed by echo base64_encode(data);
If you use the Fetch API instead of XMLHttpRequest you can consume the response as a ReadableStream which can be fed into a SourceBuffer. This will allow the video to be playable as soon as it starts to load instead of waiting for the full file to download. This does not require any special video container formats, back-end processing or third-party libraries.
const vid = document.getElementById('vid');
const format = 'video/webm; codecs="vp8,vorbis"';
const mediaSource = new MediaSource();
let sourceBuffer = null;
mediaSource.addEventListener('sourceopen', event => {
sourceBuffer = mediaSource.addSourceBuffer(format);
fetch('https://bes.works/dev/samples/test.webm')
.then(response => process(response.body.getReader()))
.catch(err => console.error(err));
}); vid.src = URL.createObjectURL(mediaSource);
function process(stream) {
return new Response(
new ReadableStream({
start(controller) {
async function read() {
let { done, value } = await stream.read();
if (done) { controller.close(); return; }
sourceBuffer.appendBuffer(value);
sourceBuffer.addEventListener(
'updateend', event => read(),
{ once: true }
);
} read();
}
})
);
}
video { width: 300px; }
<video id="vid" controls></video>
As indicated in the comments, you are missing some decent components.
You can implement what you are asking for but you need to make some changes. Following up on the HTML5 streaming API you can create a stream that will make the video using segments you fetch from the server.
Something to keep in mind is the HLS or DASH protocol that already exists can help, looking at the HLS protocol can help as it's simple to use with the idea of segments that can reach out to your server and just decode your base64'd feed.
https://videojs.github.io/videojs-contrib-hls/

Hls.js record file

Hello and thanks for reading,
I have a Hls stream with an m3u8 playlist.
The Video is playing just fine on an Html page with a Video element and https://github.com/video-dev/hls.js
But if I download the segments to join them they are only white pixels. VLC and FFmpeg can't handle them. VLC shows a white pixel for 10seconds and FFmpeg says that there's no stream in the file.
So now I want to know what this hls.js is doing to make it running. To me a non-js developer it all looks a bit confusing. I was able to understand stuff like which function is called when a new segment is loaded. Unfortunately, I was unable to understand stuff about the data. The one character variables are confusing to me.
For now, I capture the stream of the video element and download it later but I don't like this solution at all.
How to help me
It would be very nice if anyone can tell me how to hook into the
script and tell it to download directly to the disk so I'm independent
of framerate drops.
If anyone can tell how the script is able to convert the data so that
the element can use it and I would be able to implement or do
it with FFmpeg would be really helpful.
I also thought it might be possible to have a listener when the blob
changes to store its contents.
Thanks for everyone helping. I'm trying to find a solution for too many hours now.
I found the solution. After looking at their great event system
https://github.com/video-dev/hls.js/
and this issue which I contributed too and not just copied
https://github.com/video-dev/hls.js/issues/1322
var arrayRecord = [];
function download(data, filename) {
console.log('downloading...');
var blob = new Blob([arrayConcat(data)], {
type: 'application/octet-stream'
});
saveAs(blob, filename);
}
function arrayConcat(inputArray) {
var totalLength = inputArray.reduce(function (prev, cur) {
return prev + cur.length
}, 0);
var result = new Uint8Array(totalLength);
var offset = 0;
inputArray.forEach(function (element) {
result.set(element, offset);
offset += element.length;
});
return result;
}
function saveAs(blob, filename) {
var url = URL.createObjectURL(blob);
var a = document.createElement("a");
document.body.appendChild(a);
a.style = "display: none";
a.href = url;
a.download = filename;
a.click();
window.URL.revokeObjectURL(url);
}
function stopRecord() {
arrayRecord.forEach(function (item) {
download(item.data['video'], "video.mp4");
download(item.data['audio'], "audio.mp4");
item.hls.destroy();
return false;
});
}
function startRecord() {
var video = document.getElementById('video');
var dataStream = {
'video': [],
'audio': []
};
var hls = new Hls();
hls.loadSource("Your playlist");
hls.attachMedia(video);
hls.on(Hls.Events.MANIFEST_PARSED, function () {
video.play();
hls.on(Hls.Events.BUFFER_APPENDING, function (event, data) {
console.log("apending");
dataStream[data.type].push(data.data);
});
});
arrayRecord.push({
hls: hls,
data: dataStream
});
video.onended = function (e) {
stopRecord()
}
}

Download big file from server with range requests

I need to download files through multiple streams in browser. I run the php server which is able to process Range Requests, so it returns the number of bytes requested. But how to download parts of file in multiple streams to then combine them in one is a question. I do have control over requests.
Just combine them together, here is code sample:
var part1, part2
function rangeRequest1() {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open("GET", "http://localhost:3000/zuckjet.pdf", true);
xmlHttp.responseType = "blob";
xmlHttp.setRequestHeader("Range", "bytes=0-18000");
xmlHttp.onload = function() {
part1 = new Blob([oReq.response], {
type: 'application/pdf'
});
// Pseudocode: part2 = another rangeRequest's response
download()
};
function download() {
var link = document.createElement('a');
var blob = new Blob([part1, part2]);
link.download = 'zuckjet.pdf';
link.href = URL.createObjectURL(blob);
link.click();
URL.revokeObjectURL(blob);
}

PDF is blank when downloading using javascript

I have a web service that returns PDF file content in its response. I want to download this as a pdf file when user clicks the link. The javascript code that I have written in UI is as follows:
$http.get('http://MyPdfFileAPIstreamURl').then(function(response){
var blob=new File([response],'myBill.pdf',{type: "text/pdf"});
var link=document.createElement('a');
link.href=window.URL.createObjectURL(blob);
link.download="myBill.pdf";
link.click();
});
'response' contains the PDF byte array from servlet outputstream of 'MyPdfFileAPIstreamURl'. And also the stream is not encrypted.
So when I click the link, a PDF file gets downloaded successfully of size around 200KB. But when I open this file, it opens up with blank pages. The starting content of the downloaded pdf file is in the image.
I can't understand what is wrong here. Help !
This is the downloaded pdf file starting contents:
solved it via XMLHttpRequest and xhr.responseType = 'arraybuffer';
code:
var xhr = new XMLHttpRequest();
xhr.open('GET', './api/exportdoc/report_'+id, true);
xhr.responseType = 'arraybuffer';
xhr.onload = function(e) {
if (this.status == 200) {
var blob=new Blob([this.response], {type:"application/pdf"});
var link=document.createElement('a');
link.href=window.URL.createObjectURL(blob);
link.download="Report_"+new Date()+".pdf";
link.click();
}
};
xhr.send();
i fetched the data from server as string(which is base64 encoded to string) and then on client side i decoded it to base64 and then to array buffer.
Sample code
function solution1(base64Data) {
var arrBuffer = base64ToArrayBuffer(base64Data);
// It is necessary to create a new blob object with mime-type explicitly set
// otherwise only Chrome works like it should
var newBlob = new Blob([arrBuffer], { type: "application/pdf" });
// IE doesn't allow using a blob object directly as link href
// instead it is necessary to use msSaveOrOpenBlob
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(newBlob);
return;
}
// For other browsers:
// Create a link pointing to the ObjectURL containing the blob.
var data = window.URL.createObjectURL(newBlob);
var link = document.createElement('a');
document.body.appendChild(link); //required in FF, optional for Chrome
link.href = data;
link.download = "file.pdf";
link.click();
window.URL.revokeObjectURL(data);
link.remove();
}
function base64ToArrayBuffer(data) {
var binaryString = window.atob(data);
var binaryLen = binaryString.length;
var bytes = new Uint8Array(binaryLen);
for (var i = 0; i < binaryLen; i++) {
var ascii = binaryString.charCodeAt(i);
bytes[i] = ascii;
}
return bytes;
};
I was facing the same problem in my React project.
On the API I was using res.download() of express to attach the PDF file in the response. By doing that, I was receiving a string based file. That was the real reason why the file was opening blank or corrupted.
In my case the solution was to force the responseType to 'blob'. Since I was making the request via axios, I just simply added this attr in the option object:
axios.get('your_api_url_here', { responseType: 'blob' })
After, to make the download happen, you can do something like this in your 'fetchFile' method:
const response = await youtServiceHere.fetchFile(id)
const pdfBlob = new Blob([response.data], { type: "application/pdf" })
const blobUrl = window.URL.createObjectURL(pdfBlob)
const link = document.createElement('a')
link.href = blobUrl
link.setAttribute('download', customNameIfYouWantHere)
link.click();
link.remove();
URL.revokeObjectURL(blobUrl);
solved it thanks to rom5jp but adding the sample code for golang and nextjs
in golang using with gingonic context
c.Header("Content-Description", "File-Transfer")
c.Header("Content-Transfer-Encoding", "binary")
c.Header("Content-Disposition","attachment; filename="+fileName)
c.Header("Content-Type", "application/pdf; charset=utf-8")
c.File(targetPath)
//c.FileAttachment(targetPath,fileName)
os.RemoveAll(targetPath)
in next js
const convertToPDF = (res) => {
const uuid = generateUUID();
var a = document.createElement('a');
var url = window.URL.createObjectURL(new Blob([res],{type: "application/pdf"}));
a.href = url;
a.download = 'report.pdf';
a.click();
window.URL.revokeObjectURL(url);
}
const convertFile = async() => {
axios.post('http://localhost:80/fileconverter/upload', {
"token_id" : cookies.access_token,
"request_type" : 1,
"url" : url
},{
responseType: 'blob'
}).then((res)=>{
convertToPDF(res.data)
}, (err) => {
console.log(err)
})
}
I was able to get this working with fetch using response.blob()
fetch(url)
.then((response) => response.blob())
.then((response) => {
const blob = new Blob([response], {type: 'application/pdf'});
const link = document.createElement('a');
link.download = 'some.pdf';
link.href = URL.createObjectURL(blob);
link.click();
});
Changing the request from POST to GET fixed it for me

Web Audio Api - Download edited MP3

I'm currently editing my mp3 file with multiple effects like so
var mainVerse = document.getElementById('audio1');
var s = source;
source.disconnect(audioCtx.destination);
for (var i in filters1) {
s.connect(filters1[i]);
s = filters1[i];
}
s.connect(audioCtx.destination);
The mp3 plays accordingly on the web with the filters on it. Is it possible to create and download a new mp3 file with these new effects, using web audio api or any writing to mp3 container javascript library ? If not whats the best to solve this on the web ?
UPDATE - Using OfflineAudioContext
Using the sample code from https://developer.mozilla.org/en-US/docs/Web/API/OfflineAudioContext/oncomplete
I've tried using the offline node like so;
var audioCtx = new AudioContext();
var offlineCtx = new OfflineAudioContext(2,44100*40,44100);
osource = offlineCtx.createBufferSource();
function getData() {
request = new XMLHttpRequest();
request.open('GET', 'Song1.mp3', true);
request.responseType = 'arraybuffer';
request.onload = function() {
var audioData = request.response;
audioCtx.decodeAudioData(audioData, function(buffer) {
myBuffer = buffer;
osource.buffer = myBuffer;
osource.connect(offlineCtx.destination);
osource.start();
//source.loop = true;
offlineCtx.startRendering().then(function(renderedBuffer) {
console.log('Rendering completed successfully');
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var song = audioCtx.createBufferSource();
song.buffer = renderedBuffer;
song.connect(audioCtx.destination);
song.start();
rec = new Recorder(song, {
workerPath: 'Recorderjs/recorderWorker.js'
});
rec.exportWAV(function(e){
rec.clear();
Recorder.forceDownload(e, "filename.wav");
});
}).catch(function(err) {
console.log('Rendering failed: ' + err);
// Note: The promise should reject when startRendering is called a second time on an OfflineAudioContext
});
});
}
request.send();
}
// Run getData to start the process off
getData();
Still getting the recorder to download an empty file, I'm using the song source as the source for the recorder. The song plays and everything with his code but recorder doesn't download it
Use https://github.com/mattdiamond/Recorderjs to record a .wav file. Then use https://github.com/akrennmair/libmp3lame-js to encode it to .mp3.
There's a nifty guide here, if you need a hand: http://audior.ec/blog/recording-mp3-using-only-html5-and-javascript-recordmp3-js/
UPDATE
Try moving
rec = new Recorder(song, {
workerPath: 'Recorderjs/recorderWorker.js'
});
so that it is located above the call to start rendering, and connect it to osource instead, like so:
rec = new Recorder(osource, {
workerPath: 'Recorderjs/recorderWorker.js'
});
osource.connect(offlineCtx.destination);
osource.start();
offlineCtx.startRendering().then(function(renderedBuffer) {
.....

Categories

Resources