Appending property to ArrayBuffer sent over DataChannel - javascript

I am currently receiving chunks from a stream of a video that I send over the DataChannel to a peer who will then reconstruct the video on the other end.
I have this part working just fine but I would like to add which chunk # it was that was received so that it doesn't matter if they happen to arrive in a different order than intended.
Initially I thought that adding a parameter chunkId would work but when I do .data.chunkId on the receiver side, it is undefined.
Then I tried to stringify the ArrayBuffer along with the chunkId using JSON.stringify({ "chunkId": chunkId, "data": chunk }) but it causes problems when I parse it on the other end (Unexpected end of JSON input and Unexpected token , in JSON at position #)
DataChannels also accept blobs so I thought I would try that but the sender is using node.js which apparently can't do that. I wasn't quite able to figure out how to get around that.
The last thing I tried was to simply append the chunkId to the beginning/end of the ArrayBuffer itself but when I try to create a new array I get the error source is too large when trying to add the chunk itself.
What is the correct way of achieving this?

You should be able to intermingle sending text and ArrayBuffers, and check for them on reception:
var pc1 = new RTCPeerConnection(), pc2 = new RTCPeerConnection();
pc1.onicecandidate = e => pc2.addIceCandidate(e.candidate);
pc2.onicecandidate = e => pc1.addIceCandidate(e.candidate);
pc1.oniceconnectionstatechange = e => log(pc1.iceConnectionState);
pc1.onnegotiationneeded = e =>
pc1.createOffer().then(d => pc1.setLocalDescription(d))
.then(() => pc2.setRemoteDescription(pc1.localDescription))
.then(() => pc2.createAnswer()).then(d => pc2.setLocalDescription(d))
.then(() => pc1.setRemoteDescription(pc2.localDescription))
.catch(e => log(e));
var dc1 = pc1.createDataChannel("chat", {negotiated: true, id: 0});
var dc2 = pc2.createDataChannel("chat", {negotiated: true, id: 0});
dc2.binaryType = "arraybuffer";
dc2.onmessage = e => {
if (e.data instanceof ArrayBuffer) {
log("Got ArrayBuffer!");
} else if (e.data instanceof Blob) {
log("Got Blob!");
} else {
log("> " + e.data);
}
}
button.onclick = e => dc1.send(new ArrayBuffer(8));
chat.onkeypress = e => {
if (e.keyCode != 13) return;
dc1.send(chat.value);
chat.value = "";
};
var log = msg => div.innerHTML += "<br>" + msg;
Chat: <input id="chat"><button id="button">Send ArrayBuffer</button><br>
<div id="div"></div>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
So why not send the chunk id ahead of each ArrayBuffer?

Related

Async JS validation issues for html textarea

I'm trying to replicate the code in this article:
https://depth-first.com/articles/2020/08/24/smiles-validation-in-the-browser/
What I'm trying to do different is that I'm using a textarea instead of input to take multi-line input. In addition to displaying an error message, I also want to display the entry which doesn't pass the validation.
The original validation script is this:
const path = '/target/wasm32-unknown-unknown/release/smival.wasm';
const read_smiles = instance => {
return smiles => {
const encoder = new TextEncoder();
const encoded = encoder.encode(`${smiles}\0`);
const length = encoded.length;
const pString = instance.exports.alloc(length);
const view = new Uint8Array(
instance.exports.memory.buffer, pString, length
);
view.set(encoded);
return instance.exports.read_smiles(pString);
};
};
const watch = instance => {
const read = read_smiles(instance);
document.querySelector('input').addEventListener('input', e => {
const { target } = e;
if (read(target.value) === 0) {
target.classList.remove('invalid');
} else {
target.classList.add('invalid');
}
});
}
(async () => {
const response = await fetch(path);
const bytes = await response.arrayBuffer();
const wasm = await WebAssembly.instantiate(bytes, { });
watch(wasm.instance);
})();
For working with a textarea, I've changed the watch function to this and added a <p id="indicator"> element to the html to display an error:
const watch = instance => {
const read = read_smiles(instance);
document.querySelector("textarea").addEventListener('input', e => {
const { target } = e;
var lines_array = target.value.split('/n');
var p = document.getElementById("indicator");
p.style.display = "block";
p.innerHTML = "The size of the input is : " + lines_array.length;
if (read(target.value) === 0) {
target.classList.remove('invalid');
} else {
target.classList.add('invalid');
}
});
}
I'm not even able to get a count of entries that fail the validation. I believe this is async js and I'm just a beginner in JavaScript so it's hard to follow what is happening here, especially the part where the function e is referencing itself.
document.querySelector("textarea").addEventListener('input', e => {
const { target } = e;
Can someone please help me in understanding this complicated code and figuring out how to get a count of entries that fail the validation and also printing the string/index of the same for helping the user?
There is a mistake in you code to count entries in the textarea:
var lines_array = target.value.split('\n'); // replace /n with \n
You are asking about the function e is referencing itself:
The destructuring assignment syntax is a JavaScript expression that makes it possible to unpack values from arrays, or properties from objects, into distinct variables. You can find more informations Mdn web docs - Destructuring object

Trying to show off emoji when it's added using Discord.js

Currently I'm trying to add something to my bot to display text and a preview when an emoji is added.
I was pretty close, but it appears the emoji doesn't exist on the server during the emojiCreate event.
my code (which is a mess) looks like this currently:
var latestEmojiName = "";
let announcementChannel = "";
client.on("emojiCreate", emoji => {
announcementChannel = emoji.guild.channels.find(x => x.id === "625678148440162314");
announcementChannel.send("new emoji has been added:");
latestEmojiName = emoji.name;
newEmojiFollowup1();
});
function newEmojiFollowup1() {
setTimeout(newEmojiFollowup2, 2000);
}
function newEmojiFollowup2() {
announcementChannel.send(client.guilds.find(x => x.id === "607642928872947724").emojis.find(x => x.name === latestEmojiName));
}
Ok, I added the following listener to one of my bots and it worked. Also, there is no need to look-up the guild because you do not need a timeout. The emoji object has all you need already.
You need to send: <:${emoji.name}:${emoji.id}>
Also, use let instead of var to resolve scoping issues and there is no need for all the "follow-on functions.
// Verified with discord.js#11.5.1
const channelId = "625678148440162314" // e.g. Channel: #general
client.on("emojiCreate", emoji => {
const channel = emoji.guild.channels.find(x => x.id === channelId)
channel.send("A new emoji has been added:")
channel.send(`<:${emoji.name}:${emoji.id}>`)
})
You can also send a rich-embed message:
// Verified with discord.js#11.5.1
const channelId = "625678148440162314" // e.g. Channel: #general
client.on("emojiCreate", emoji => {
const channel = emoji.guild.channels.find(x => x.id === channelId)
const embed = new RichEmbed()
.setTitle("A new emoji has been added!")
.setColor(0x222222)
.setDescription("Preview:")
embed.addField(`:${emoji.name}:`, `<:${emoji.name}:${emoji.id}>`, true)
channel.send(embed)
})

Using DataView with nodejs Buffer

I'm trying to read/write some binary data using the DataView object. It seems to work correctly when the buffer is initialized from a UInt8Array, however it if pass it a nodejs Buffer object the results seem to be off. Am I using the API incorrectly?
import { expect } from 'chai';
describe('read binary data', () => {
it('test buffer', () => {
let arr = Buffer.from([0x55,0xaa,0x55,0xaa,0x60,0x00,0x00,0x00,0xd4,0x03,0x00,0x00,0x1c,0xd0,0xbb,0xd3,0x00,0x00,0x00,0x00])
let read = new DataView(arr.buffer).getUint32(0, false);
expect(read).to.eq(0x55aa55aa);
})
it('test uint8array', () => {
let arr = new Uint8Array([0x55,0xaa,0x55,0xaa,0x60,0x00,0x00,0x00,0xd4,0x03,0x00,0x00,0x1c,0xd0,0xbb,0xd3,0x00,0x00,0x00,0x00])
let read = new DataView(arr.buffer).getUint32(0, false);
expect(read).to.eq(0x55aa55aa);
})
})
The one with the buffer fails with
AssertionError: expected 1768779887 to equal 1437226410
+ expected - actual
-1768779887
+1437226410
try use this buf.copy
const buf = fs.readFileSync(`...`);
const uint8arr = new Uint8Array(buf.byteLength);
buf.copy(uint8arr, 0, 0, buf.byteLength);
const v = new DataView(uint8arr.buffer);
Nodejs Buffer is just a view over underlying allocated buffer that can be a lot larger. This is how to get ArrayBuffer out of Buffer:
function getArrayBufferFromBuffer( buffer ) {
return buffer.buffer.slice( buffer.byteOffset, buffer.byteOffset + buffer.byteLength ) );
}
This helped (not sure if this is the most elegant):
const buff = Buffer.from(msgBody, 'base64');
let uint8Array = new Uint8Array(buff.length);
for(let counter=0;counter<buff.length;counter++) {
uint8Array[counter] = buff[counter];
//console.debug(`uint8Array[${counter}]=${uint8Array[counter]}`);
}
let dataview = new DataView(uint8Array.buffer, 0, uint8Array.length);

How to convert files to Base64 in React

I have registration for in React where I need to upload files to the server. Those files needs to be Base64 encoded.
The function to encode it is as follows:
getBase64(file) {
let document = "";
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function () {
document = reader.result;
};
reader.onerror = function (error) {
console.log('Error: ', error);
};
return document;
}
And function to handle click on form's next button is as follow:
handleNextButtonClick(event){
event.preventDefault();
let data = {domainId: this.props.user[0].domainId, name: steps.stepThree, values: this.state.files};
let idCard = this.state.files.filter(file => file.file_type === "ID_CARD")[0].values.file;
let statuses = this.state.files.filter(file => file.file_type === "STATUTES")[0].values.file;
let blankLetterHead = this.state.files.filter(file => file.file_type === "LETTER_HEAD")[0].values.file;
let companyPhoto = this.state.files.filter(file => file.file_type === "COMPANY_PICTURE")[0].values.file;
let idCardBase64 = this.getBase64(idCard);
let statusesBase64 = this.getBase64(statuses);
let blankLetterHeadBase64 = this.getBase64(blankLetterHead);
let companyPhotoBase64 = this.getBase64(companyPhoto);
}
If I console log for example the first one this.state.files.filter(file => file.file_type === "ID_CARD")[0].values.file; I get
Everything seems ok, but I'm getting error:
Uncaught TypeError: Failed to execute 'readAsDataURL' on 'FileReader': parameter 1 is not of type 'Blob'.
Any idea how to solve this?
UPDATE
let idCardBase64 = idCard ? this.getBase64(idCard) : "";
let statusesBase64 = statuses ? this.getBase64(statuses) : "";
let blankLetterHeadBase64 = blankLetterHead ? this.getBase64(blankLetterHead) : "";
let companyPhotoBase64 = companyPhoto ? this.getBase64(companyPhoto) : "";
I changed it. And in this case exists only idCard. Now I do not get any errors but idCardBase64 is "" and not Base64 encoded.
file reading is asynchronous. so use callback or promise to solve your problem.
let idCardBase64 = '';
this.getBase64(idCard, (result) => {
idCardBase64 = result;
});
and use callback to return the data which you get.
getBase64(file, cb) {
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function () {
cb(reader.result)
};
reader.onerror = function (error) {
console.log('Error: ', error);
};
}
Also, you can use this React component React File Base64
Check the demo
If you just use https://www.npmjs.com/package/react-file-base64 it makes things a lot simpler.
Use the below as an input field in a form to handle the encoding for you.
My code looks like the below for reference.
<FileBase type="file" multiple={false} onDone={({base64}) => setListingData({ ...listingData, selectedFile: base64})}/>

programmatically use w3c css/html validators with custom html

edit
I've got it working for CSS, only HTML validation to go.
let css = Array.from(document.styleSheets)
.reduce((combinedsheet,sheet) => sheet.rules?
combinedsheet + Array.from(sheet.rules)
.reduce((p,c) => p+c.cssText+'\n', ''):
combinedsheet, '')
try {
document.querySelector('a.validation.css').href =
'https://jigsaw.w3.org/css-validator/validator?text=' +
encodeURIComponent(css) +
'&profile=css3&usermedium=all&warning=1&vextwarning='
document.querySelector('a.validation.html').href =
'https://validator.w3.org/nu/?doc=' +
encodeURIComponent(document.querySelector('html'))
} catch (e) {
// this will fail before page fully loads, and we can be silent about that
}
edit #2
I've got it working. The only problem is that this uses a "popup" instead of opening a window silently like target="blank" would. I'm using an onlick method:
get_html_validation: function () {
let fd = new FormData()
fd.append('fragment', '<!DOCTYPE html>' +
document.querySelector('html').outerHTML)
fd.append('prefill', 0)
fd.append('doctype', 'Inline')
fd.append('prefill_doctype', 'html401')
fd.append('group', 0)
axios.post("https://validator.w3.org/nu/#textarea", fd)
.then(response => {
let win=window.open('about:blank')
console.log(win)
with(win.document)
{
open()
write(response.data)
close()
}
})
.catch(e => console.error(e))
}
original
I'd like to use these validators programmatically:
https://validator.w3.org/nu
http://jigsaw.w3.org/css-validator/validator
— they work just fine if you pass a URL as a parameter, so long as the document doesn't have javascript that manipulates the dom. But... mine does.
How can I hand these sites custom html to check using javascript in the browser? I'm looking to do something like:
onValidateDOM = () => {
let toValidate = '<!DOCTYPE html>' + document.querySelector('html').outerHTML
let returnURLs = []
returnURLs.push(w3cCSS.validate(toValidate), w3cHTML.validate(toValidate)
return returnURLs
}
Set toValidate passed to encodeURIComponent() as value to doc= query
fetch(`https://validator.w3.org/nu/?doc=${encodeURIComponent(toValidate)}`)
.then(response => response.text())
.then(text => console.log(text));

Categories

Resources