This question already has answers here:
MediaRecorder.stop() doesn't clear the recording icon in the tab
(2 answers)
Closed 4 months ago.
This has been racking my brain for a week.
I have getDisplayMedia working just fine, however I want to turn off the "sharing permission" after recording has been stopped.
I've seen this work here:
https://www.webrtc-experiment.com/RecordRTC/
Set the form to record the Full Screen
Start recording
Stop recording
You will see the "permission dialog" disappear.
How can I programmatically do this?
Current Code:
recBtn.onclick = () => {
stopBtn.disabled = false;
recBtn.disabled = true;
limRange.disabled = true;
navigator.mediaDevices
.getDisplayMedia ({
audio: {
sampleRate: 44100,
echoCancellation: false,
noiseSuppression: false
},
video: { mediaSource: "screen" },
})
.then ( ( stream ) =>
{
mediaRecorder = new MediaRecorder( stream );
//music.play();
mediaRecorder.addEventListener("dataavailable", (e) =>
{
// manage timer
if ( seconds >= 60 ) {
seconds = 0;
minutes += 1;
}
seconds += 1;
if ( minutes > 9 && seconds > 9 )
{
p.innerHTML = minutes + ":" + seconds;
}
else if ( minutes > 9 )
{
p.innerHTML = minutes + ":0" + seconds;
}
else if ( seconds > 9 )
{
p.innerHTML = "0" + minutes + ":" + seconds;
}
else {
p.innerHTML = "0" + minutes + ":0" + seconds;
}
// push data
chunks.push(e.data);
});
mediaRecorder.onstop = ( e ) =>
{
//console.log( chunks );
const blob = new Blob( chunks , { type: "audio/mp3" });
//console.log( minutes, seconds );
var offlineAudioContext = new OfflineAudioContext (
2,
44100 * ((minutes * 60) + seconds),
44100
);
var soundSource = offlineAudioContext.createBufferSource();
var audioContext = new window.AudioContext();
var reader = new FileReader();
var buff;
reader.readAsArrayBuffer(blob); // video file
reader.onload = function ()
{
var videoFileAsBuffer = reader.result; // arraybuffer
audioContext
.decodeAudioData(videoFileAsBuffer)
.then( function ( decodedAudioData )
{
myBuffer = decodedAudioData;
soundSource.buffer = myBuffer;
soundSource.connect(offlineAudioContext.destination);
soundSource.start();
offlineAudioContext
.startRendering()
.then( function ( renderedBuffer )
{
console.log(renderedBuffer); // outputs audiobuffer
buff = renderedBuffer;
let wav = audioBufferToWav(buff);
console.log(wav);
let bblob = new window.Blob([new DataView(wav)], {
type: "audio/mp3",
});
const audioURL = URL.createObjectURL(bblob);
audioRecorded.src = audioURL;
/*const fileName =
"recording-" +
recs +
"-" +
minutes +
"_" +
seconds +
".mp3";*/
var fileName = "Block " + block_letter.toUpperCase() + " - " + block_filename + '.mp3';
let elm = document.createElement("a");
elm.id = 'download-mp3'
elm.setAttribute("href", audioRecorded.src);
elm.setAttribute("download", fileName);
//elm.click();
//elm.remove();
$('.download-link').html( elm );
$('#download-mp3').html( fileName ).addClass('text-4 text-primary font-weight-semi-bold');
$('.recording-link').show();
$('.recording-info').hide();
minutes = 0;
seconds = 0;
})
.catch(function (err) {
console.log("Rendering failed: " + err);
});
})
.catch(function (err) {
console.log("audioContext failed: " + err);
});
};
console.log("recorder stopped");
chunks = [];
p.innerHTML = "00:00";
if (recs == 0)
{
document.body.appendChild(audioRecorded);
}
recs += 1;
$('#btn-record, .btn-block-stop, .modal-btn-right').show();
$('#btn-record-stop, .recording-inprogress').hide();
$('.btn-block-stop').click();
};
mediaRecorder.start(1000);
$('.ajax-modal-btn-right').click();
$('#btn-record, .btn-block-stop, .modal-btn-right').hide();
$('#btn-record-stop, .recording-inprogress').show();
//console.log(mediaRecorder.state);
//console.log("recorder started");
})
.catch ( (error) =>
{
console.log ('error');
});
};
stopBtn.onclick = () =>
{
recBtn.disabled = false;
limRange.disabled = false;
stopBtn.disabled = true;
mediaRecorder.stop( 5000 );
console.log("recording stopped");
$('#btn-record, .btn-block-stop, .modal-btn-right').show();
$('#btn-record-stop, .recording-inprogress').hide();
$('.btn-block-stop').click();
};
The media permissions settings are not accessible from Javascript code. They just aren't, so it's no wonder you can't find them. Because cybercreeps could trick our users into revealing sensitive information.
It's a real pain in the neck, but it works the same for everybody from Google Meet on down.
Related
I'm having issues in this process . First is that I have a button in disabled state(true) and I need to change that value to false when the video is uploaded . I have this scenary and I think I got a windows object inside the changing method . Any idea, help please . I'm getting undefined value for the variable.
data: () => ({
buttonState: true} }),
changeBehavior() {
let self
(function () {
const input = document.getElementById('uploader')
self = this
console.log(this)
const changing = ({ target: { files } }) => {
if (input.files.length > 0) {
// self.buttonState = false
const video = document.getElementById('output-video')
video.src = URL.createObjectURL(files[0])
}
}
input.addEventListener('change', changing)
})()
const au = document.getElementById('output-video')
au.onloadedmetadata = () => {
const hidden = document.getElementById('hiddenSlider')
hidden.removeAttribute('hidden')
const muteHidden = document.getElementById('muteHidden')
muteHidden.removeAttribute('hidden')
self = this
self.range = [0, au.duration]
this.max = au.duration
const secNum = parseInt(au.duration, 10)
let hours = Math.floor(secNum / 3600)
let minutes = Math.floor((secNum - (hours * 3600)) / 60)
let seconds = secNum - (hours * 3600) - (minutes * 60)
if (hours < 10) {
hours = '0' + hours
}
if (minutes < 10) {
minutes = '0' + minutes
}
if (seconds < 10) {
seconds = '0' + seconds
}
document.getElementById('renderizado').innerHTML =
hours + ':' + minutes + ':' + seconds
}
},
<v-btn
id="run"
class="accent-3 blue ml-15"
dark
#click="$refs.inputUpload.click()"
>
<input
v-show="false"
id="uploader"
ref="inputUpload"
accept=".mkv,video/*"
type="file"
#click="changeBehavior"
>
Select to Upload Video
</v-btn>
<v-btn
id="doTrimming"
block
class="accent-3 blue mt-5"
dark
:disabled="buttonState"
#click="cutIt"
>
Trim Video Now
</v-btn>
Where you define self you need to assign this to it then.
changeBehavior() {
const self = this;
const callback = function() {
// now you can access the vue instance when in another functions scope
self.buttonState = true;
}
}
I try to run poc which created in angular 8 using webrtc-adpater. Problem is that it is not working in firefox and safari, but working in chrome.
What I observe connection is never connecting or connected in firefox.
I import webrtc in code so anything else we need to handle for firefox and safari.
Basically from Wowza I trying to consume streaming.
Below code sample which working in chrome but not in Firefox and safari:
const ICE_SERVERS: RTCIceServer[] = [
{ urls: ['stun:stun.example.com', 'stun:stun-1.example.com'] },
{ urls: 'stun:stun.l.google.com:19302' }
];
setupSignalingServer(ele) {
const self = this;
window.RTCPeerConnection = window.RTCPeerConnection ||
window.mozRTCPeerConnection ||
window.webkitRTCPeerConnection;
let signalingConnection = new WebSocket('wss://local.geofabricdev.com/webrtc-session.json');
signalingConnection.binaryType = 'arraybuffer';
signalingConnection.onopen = function (res) {
console.log('connection open');
signalingConnection.onerror = self.errorHandler;
// window.RTCPeerConnection = self.getRTCPeerConnection();
const peerConnection = new RTCPeerConnection(PEER_CONNECTION_CONFIG);
peerConnection.addEventListener('connectionstatechange', event => {
console.log(event);
if (peerConnection.connectionState === 'connected') {
console.log('connected');
}
});
peerConnection.addEventListener('icecandidate', event => {
console.log('peerconnection state5:' + peerConnection.connectionState);
if (event.candidate) {
// signalingChannel.send({'new-ice-candidate': event.candidate});
}
});
peerConnection.addEventListener('track', async event => {
console.log('gotRemoteTrack: kind:' + event.track.kind + ' stream:' + event.streams[0]);
const video = self.createVideo();
// const remoteVideo1 = document.querySelector('#remoteVideo') as HTMLVideoElement;
try {
video.srcObject = event.streams[0];
// remoteVideo1.srcObject = event.streams[0];
} catch (error) {
video.src = window.URL.createObjectURL(event.streams[0]);
}
});
signalingConnection.addEventListener('message', async message => {
console.dir(message);
// self.getSignalMessageCallback(message);
console.log('wsConnection.onmessage: ' + message.data);
const signal = JSON.parse(message.data);
if(signal.status !== 200) {
const video = self.createVideo();
video.poster = '../assets/streaming-error.png';
}
const streamInfoResponse = signal['streamInfo'];
let g = [];
// self.socketStream.some((f) => {
g = self.streams.filter((str) => signal['streamInfo'].streamName === str.streamName);
// if(g.length > 0) {
// return true;
// }
// });
// });
if (streamInfoResponse !== undefined) {
g[0]['sessionId'] = streamInfoResponse.sessionId;
}
console.log('Received signal');
const msgCommand = signal['command'];
console.log(msgCommand);
if (signal.sdp) {
peerConnection.setRemoteDescription(new RTCSessionDescription(signal.sdp));
if (signal.sdp) {
const description = await peerConnection.createAnswer();
await peerConnection.setLocalDescription(description);
signalingConnection.send('{"direction":"play", "command":"sendResponse", "streamInfo":' +
JSON.stringify(g[0]) + ', "sdp":' + JSON.stringify(description) + ',"userData":' + JSON.stringify(self.userData) + '}');
}
// })
// .catch(self.errorHandler);
} else if (signal.iceCandidates) {
console.log('ice: ' + JSON.stringify(signal.iceCandidates));
peerConnection.addIceCandidate(new RTCIceCandidate(signal.iceCandidates[0])).catch(self.errorHandler);
}
if ('sendResponse'.localeCompare(msgCommand) === 0) {
if (signalingConnection != null) {
signalingConnection.close();
}
signalingConnection = null;
}
// };
});
signalingConnection.send('{"direction":"play", "command":"getOffer", "streamInfo":' +
JSON.stringify(ele) + ', "userData":' + JSON.stringify(self.userData) + '}');
};
signalingConnection.onclose = function (r) {
console.log('close');
};
}
private createVideo() {
const video = document.createElement('video');
video.classList.add('cameraPanel');
video.autoplay = true;
video.preload = 'true';
video.muted = true;
video.width = 300;
const remoteVideo = document.querySelector('#videoContainer') as HTMLElement;
remoteVideo.appendChild(video);
return video;
}
I have problems capturing and loading the webpage:
https://myaccount.nytimes.com/auth/login
The username and password fields as well as the submit button are missing. What could be the reason? Thanks
As you can see the code listed below waits for each and all the resources to be loaded, one by one.
I did not write the code, however it is excellent and tested by many. The original can be found here:
https://gist.github.com/cjoudrey/1341747
(ALL the credits to the guy who wrote it.)
var resourceWait = 3000,
maxRenderWait = 30000,
url = 'https://myaccount.nytimes.com/auth/login'; //'https://twitter.com/#!/nodejs';
var page = require('webpage').create(),
count = 0,
forcedRenderTimeout,
renderTimeout;
page.viewportSize = { width: 1280, height : 1024 };
function doRender() {
page.render('twitter.png');
phantom.exit();
}
page.onResourceRequested = function (req) {
count += 1;
console.log('> ' + req.id + ' - ' + req.url);
clearTimeout(renderTimeout);
};
page.onResourceReceived = function (res) {
if (!res.stage || res.stage === 'end') {
count -= 1;
console.log(res.id + ' ' + res.status + ' - ' + res.url);
if (count === 0) {
renderTimeout = setTimeout(doRender, resourceWait);
}
}
};
page.open(url, function (status) {
if (status !== "success") {
console.log('Unable to load url');
phantom.exit();
} else {
forcedRenderTimeout = setTimeout(function () {
console.log(count);
doRender();
}, maxRenderWait);
}
});
SPEC : Building a customer support application.
First time, when a user send a message able to save to DB and show in the view without any issues. And from my end, if i send I am able to push the data to the UI as well DB and the user is able to get the message.
But the issue is when the user send back response again, its not showing up refreshing the UI.
Below is my knockout code,
var csendpoints = {
"Active": "/cs/oc/",
"ActiveClientData": "/cs/gnm/",
"Archive": "/cw/archive/",
"Dequeue": function (id) { return "/cw/dequeue/" + id; },
"GetQueue": "/cs/q/",
"GetArchive": "/cs/h/",
"GetActive": "/cs/active/",
"RecentlyQueued": "/cs/rq/",
"Send": "/cs/send",
"SitRep": "/cw/sitrep",
};
var customerSupportViewModel;
function PagedViewModels(model) {
var self = this;
self.Items = ko.observableArray(model.Items || []);
self.MetaData = ko.observable(model.MetaData || "");
}
function PagedListMetaDataViewModel(model) {
var self = this;
self.FirstItemOnPage = ko.observable(model.FirstItemOnPage || "");
self.HasNextPage = ko.observable(model.HasNextPage || "");
self.HasPreviousPage = ko.observable(model.HasPreviousPage || "");
self.IsFirstPage = ko.observable(model.IsFirstPage || "");
self.IsLastPage = ko.observable(model.IsLastPage || "");
self.LastItemOnPage = ko.observable(model.LastItemOnPage || "");
self.PageCount = ko.observable(model.PageCount || "");
self.PageNumber = ko.observable(model.PageNumber || "");
self.PageSize = ko.observable(model.PageSize || "");
self.TotalItemCount = ko.observable(model.TotalItemCount || "");
}
function ConversationMessageViewModels(model) {
var self = this;
self.ClientId = ko.observable(model.ClientId || "");
self.ClientName = ko.observable(model.ClientName || "");
self.ClientSex = ko.observable(model.ClientSex || "");
self.ClientLanguage = ko.observable(model.ClientLanguage || "");
self.ClientProvince = ko.observable(model.ClientProvince || "");
self.ClientCountry = ko.observable(model.ClientCountry || "");
self.ClientCity = ko.observable(model.ClientCity || "");
self.ClientProfileImage = ko.observable(model.ClientProfileImage || "");
self.ClientContent = ko.observable(model.ClientContent || "");
self.Content = ko.observable(model.Content || "");
self.ConversationMessageId = ko.observable(model.ConversationMessageId || "");
self.dummy = ko.observable();
self.Sender = ko.observable(model.Sender || "");
self.SentOn = ko.observable(model.SentOn || "");
self.SentOnUniversalSortable = ko.observable(model.SentOnUniversalSortable || "");
self.SentTimeFromNow = ko.computed(function () {
self.dummy();
return moment.utc(self.SentOnUniversalSortable()).fromNow();
});
function updateTimeFromNow() {
setTimeout(function () {
self.dummy.notifySubscribers();
updateTimeFromNow();
},
60 * 1000);
}
updateTimeFromNow();
}
function ConversationViewModels(model) {
var self = this;
self.ClientId = ko.observable(model.ClientId || "");
self.ClientName = ko.observable(model.ClientName || "");
self.ClientProfileImage = ko.observable(model.ClientProfileImage || "");
self.ClientSex = ko.observable(model.ClientSex || "");
self.ClientLanguage = ko.observable(model.ClientLanguage || "");
self.ClientProvince = ko.observable(model.ClientProvince || "");
self.ClientCountry = ko.observable(model.ClientCountry || "");
self.ClientCity = ko.observable(model.ClientCity || "");
self.ClientNameInitial = ko.observable(model.ClientName.substring(0, 1).toUpperCase());
self.ClientContent = ko.observable(model.ClientContent || "");
self.ConversationId = ko.observable(model.ConversationId || "");
self.CreatedOn = ko.observable(model.CreatedOn || "");
self.MessagePreview = ko.observable(model.MessagePreview || "");
self.Messages = ko.observableArray([]);
self.ProfileImageUrl = ko.observable(model.ProfileImageUrl || "");
self.Sender = ko.observable(model.Sender || "");
self.SupportName = ko.observable(customerSupportViewModel.CustomerSupportName());
self.TextBoxContent = ko.observable("");
self.TextBoxEnabled = ko.observable(true);
self.MakeActive = function () {
customerSupportViewModel.WaitingList.remove(self);
customerSupportViewModel.ArchivedConversations.remove(self);
customerSupportViewModel.ActiveConversation(self);
customerSupportViewModel.CurrentConversations.push(self);
$.ajax(csendpoints.Active + self.ConversationId(),
{
success: console.log("Debug: Now Active")
});
};
self.MakeRemoveMultiActive = function () {
customerSupportViewModel.WaitingList.remove(self);
customerSupportViewModel.ArchivedConversations.remove(self);
customerSupportViewModel.ActiveConversation(self);
//customerSupportViewModel.CurrentConversations.push(self);
//customerSupportViewModel.CurrentConversations.remove(self);
$.ajax(csendpoints.Active + self.ConversationId(),
{
success: console.log("Debug: Now Active")
});
$("#chat-wrap").mCustomScrollbar("destroy");
$("#chat-wrap").height($(window).height() - 314);
$("#chat-wrap")
.mCustomScrollbar({
axis: "y",
scrollEasing: "linear",
scrollButtons: { enable: true },
theme: "dark-thick",
scrollbarPosition: "inside",
mouseWheelPixels: 400
});
initMap()
};
self.Archive = function () {
customerSupportViewModel.ArchivedConversations.push(self);
customerSupportViewModel.ActiveConversation('');
customerSupportViewModel.CurrentConversations.remove(self);
customerSupportViewModel.WaitingList.remove(self);
customerSupportViewModel.RefreshWaiting();
customerSupportViewModel.RefreshActive();
customerSupportViewModel.RefreshArchive();
$.ajax(csendpoints.Archive + self.ConversationId(),
{
success: console.log("Debug: Now Archived")
});
};
self.SendMessage = function (data) {
self.TextBoxEnabled(false);
var msj = {
"SentOn": moment.utc().format(),
"SentOnUniversalSortable": moment().format(),
"Sender": 1,
"SenderName": customerSupportViewModel.CustomerSupportName(),
"Content": self.TextBoxContent(),
"ClientId": self.ClientId(),
"ClientName": self.ClientName()
};
self.Messages.push(new ConversationMessageViewModels(msj));
$.ajax({
type: "POST",
url: csendpoints.Send,
data: $(data).serialize(),
success: function () { },
complete: function () {
self.TextBoxContent("");
self.TextBoxEnabled(true);
}
});
};
_.each(model.Messages, function (item) {
self.Messages.push(new ConversationMessageViewModels(item));
});
var sortedMessages = _.sortBy(self.Messages(),
function (iteratedItem) { return iteratedItem.SentOnUniversalSortable(); });
self.Messages(sortedMessages);
}
function CustomerSupportViewModel(model) {
var self = this;
customerSupportViewModel = self;
function getWaitingList(page) {
$.ajax(csendpoints.GetQueue + page,
{
success: function (data) {
self.WaitingList.removeAll();
_.each(data.Items, function (item) { self.WaitingList.push(new ConversationViewModels(item)); });
self.WaitingListPaging(data.MetaData);
}
});
}
function getActiveList(page) {
$.ajax(csendpoints.GetActive + page,
{
success: function (data) {
self.CurrentConversations.removeAll();
_.each(data.Items, function (item) { self.CurrentConversations.push(new ConversationViewModels(item)); });
self.CurrentConversationsPaging(data.MetaData);
}
});
}
function getArchiveList(page) {
$.ajax(csendpoints.GetArchive + page,
{
success: function (data) {
self.ArchivedConversations.removeAll();
_.each(data.Items, function (item) {
self.ArchivedConversations.push(new ConversationViewModels(item));
});
self.ArchivedConversations(data.MetaData);
}
});
}
// Conversations in Queue(i.e. waiting), Current(i.e. active) and Archive (i.e. history)
self.WaitingList = ko.observableArray([]);
self.ArchivedConversations = ko.observableArray([]);
self.CurrentConversations = ko.observableArray([]);
// Paging models
self.WaitingListPaging = ko.observable(model.ConversationsInQueue.MetaData);
self.ArchiveListPaging = ko.observable(model.ArchivedConversations.MetaData);
self.CurrentConversationsPaging = ko.observable(model.CurrentConversations.MetaData);
// The conversation currently active.
self.ActiveConversation = ko.observable();
//setInterval(self.ActiveConversation, 500);
// The name of the WeChatAccount serving the client
self.CustomerSupportName = ko.observable(model.CustomerSupportName);
self.RefreshWaiting = function () {
var page = self.WaitingListPaging().PageNumber;
getWaitingList(page);
};
self.RefreshActive = function () {
var page = self.CurrentConversationsPaging().PageNumber;
getActiveList(page);
//getWaitingList(page);
};
self.RefreshArchive = function () {
var page = self.ArchiveListPaging().PageNumber;
//getArchiveList(page);
getWaitingList(page);
};
//self.RefreshChatActive = function () {
// getActiveList()
//};
// ###### PAGING for Waiting / Queue ######
// Forward paging for the queue / waiting list
self.NextPageWaitingList = function () {
if (self.WaitingListPaging().IsLastPage) return;
var page = self.WaitingListPaging().PageNumber + 1;
getWaitingList(page);
};
self.PreviousPageWaitingList = function (data, e) {
//console.log($(data))
//console.log((e.target) ? e.target : e.srcElement)
if (self.WaitingListPaging().IsFirstPage) return;
var page = self.WaitingListPaging().PageNumber - 1;
getWaitingList(page);
};
// ###### PAGING for Active / Current conversations ######
self.NextPageActiveList = function () {
if (self.CurrentConversations().IsLastPage) return;
var page = self.CurrentConversations().PageNumber + 1;
getActiveList(page);
};
self.PreviousPageActiveList = function () {
if (self.CurrentConversations().IsFirstPage) return;
var page = self.CurrentConversations().PageNumber - 1;
getActiveList(page);
};
// ###### PAGING for History / Archived conversations ######
self.NextPageArchiveList = function () {
if (self.ArchivedConversations().IsLastPage) return;
var page = self.ArchivedConversations().PageNumber + 1;
getArchiveList(page);
};
self.PreviousPageArchiveList = function (data, e) {
if (self.ArchivedConversations().IsFirstPage) return;
var page = self.ArchivedConversations().PageNumber - 1;
getArchiveList(page);
};
_.each(model.ConversationsInQueue.Items,
function (item) { self.WaitingList.push(new ConversationViewModels(item)); });
_.each(model.CurrentConversations.Items,
function (item) { self.CurrentConversations.push(new ConversationViewModels(item)); });
_.each(model.ArchivedConversations.Items,
function (item) { self.ArchivedConversations.push(new ConversationViewModels(item)); });
function getNewQueue(page) {
setTimeout(function () {
$.ajax(csendpoints.GetQueue + page,
{
success: function (data, odata) { getNewQueue(); }
});
},
15 * 1000);
}
}
var customerSupportViewModel = new CustomerSupportViewModel(jsonViewModel);
window.setInterval(customerSupportViewModel.RefreshWaiting, 1000);
window.setInterval(customerSupportViewModel.RefreshActive, 200);
window.setInterval(customerSupportViewModel.Messages, 200);
ko.applyBindings(customerSupportViewModel);
var conversationMessageViewModels = new ConversationMessageViewModels(jsonViewModel);
Here MakeRemoveMultiActive is used as a click function, and in the view, it load the data what user sent as well the response sent from my end. Note, this happens only on click event.
I want to update the UI after the click event so the view is open with the data and autoupdates.
Request experts to shed some light.
I'm trying to get the exchanges in ripple and I found this data API and its working. But I want to use the ripple websocket tool for some reasons. Is there any websocket equivalent for this data API?
I think there is equivalent if you use "tx_history" command in the socket but Sorry to tell you that the json result are not equal to your specific data result.
ripple data apiv2 is being played by ajax. see the result json formatter in ripple for exchange:
} else if (resp.rows.length) {
resp.rows[0] = {
base_currency: resp.rows[0].base_currency,
base_issuer: resp.rows[0].base_issuer,
base_amount: resp.rows[0].base_amount,
counter_amount: resp.rows[0].counter_amount,
counter_currency: resp.rows[0].counter_currency,
counter_issuer: resp.rows[0].counter_issuer,
rate: resp.rows[0].rate,
executed_time: resp.rows[0].executed_time,
ledger_index: resp.rows[0].ledger_index,
buyer: resp.rows[0].buyer,
seller: resp.rows[0].seller,
taker: resp.rows[0].taker,
provider: resp.rows[0].provider,
autobridged_currency: resp.rows[0].autobridged_currency,
autobridged_issuer: resp.rows[0].autobridged_issuer,
offer_sequence: resp.rows[0].offer_sequence,
tx_type: resp.rows[0].tx_type,
tx_index: resp.rows[0].tx_index,
node_index: resp.rows[0].node_index,
tx_hash: resp.rows[0].tx_hash
};
}
res.csv(resp.rows, filename);
} else {
res.json({
result: 'success',
count: resp.rows.length,
marker: resp.marker,
exchanges: resp.rows
});
} }
and it can be only access by get url :
route: '/v2/exchanges/{:base}/{:counter}'
that is bind in there server.js:
app.get('/v2/exchanges/:base/:counter', routes.getExchanges);
and last hint this is their database query using hbase:
HbaseClient.getExchanges = function (options, callback) {
var base = options.base.currency + '|' + (options.base.issuer || '');
var counter = options.counter.currency + '|' + (options.counter.issuer
||''); var table;
var keyBase;
var startRow;
var endRow;
var descending;
var columns;
if (counter.toLowerCase() > base.toLowerCase()) {
keyBase = base + '|' + counter;
} else {
keyBase = counter + '|' + base;
options.invert = true; }
if (!options.interval) {
table = 'exchanges';
descending = options.descending ? true : false;
options.unreduced = true;
//only need certain columns
if (options.reduce) {
columns = [
'd:base_amount',
'd:counter_amount',
'd:rate',
'f:executed_time',
'f:buyer',
'f:seller',
'f:taker'
];
}
} else if (exchangeIntervals.indexOf(options.interval) !== -1) {
keyBase = options.interval + '|' + keyBase;
descending = options.descending ? true : false;
table = 'agg_exchanges';
} else {
callback('invalid interval: ' + options.interval);
return; }
startRow = keyBase + '|' + options.start.hbaseFormatStartRow();
endRow = keyBase + '|' + options.end.hbaseFormatStopRow();
if (options.autobridged) {
options.filterstring = "DependentColumnFilter('f', 'autobridged_currency')";
if (columns) {
columns.push('f:autobridged_currency');
} }
this.getScanWithMarker(this, {
table: table,
startRow: startRow,
stopRow: endRow,
marker: options.marker,
limit: options.limit,
descending: descending,
columns: columns,
filterString: options.filterstring }, function (err, resp) {
if (!resp) {
resp = {rows: []};
}
if (!resp.rows) {
resp.rows = [];
}
if (options.reduce && options.unreduced) {
if (descending) {
resp.rows.reverse();
}
resp.reduced = reduce(resp.rows);
} else if (table === 'exchanges') {
resp.rows = formatExchanges(resp.rows);
} else {
resp.rows = formatAggregates(resp.rows);
}
callback(err, resp); });
/** * formatExchanges */
function formatExchanges (rows) {
rows.forEach(function(row) {
var key = row.rowkey.split('|');
delete row.base_issuer;
delete row.base_currency;
delete row.counter_issuer;
delete row.counter_currency;
row.base_amount = parseFloat(row.base_amount);
row.counter_amount = parseFloat(row.counter_amount);
row.rate = parseFloat(row.rate);
row.offer_sequence = Number(row.offer_sequence || 0);
row.ledger_index = Number(row.ledger_index);
row.tx_index = Number(key[6]);
row.node_index = Number(key[7]);
row.time = utils.unformatTime(key[4]).unix();
});
if (options.invert) {
rows = invertPair(rows);
}
return rows; }
/** * formatAggregates */
function formatAggregates (rows) {
rows.forEach(function(row) {
var key = row.rowkey.split('|');
row.base_volume = parseFloat(row.base_volume),
row.counter_volume = parseFloat(row.counter_volume),
row.buy_volume = parseFloat(row.buy_volume),
row.count = Number(row.count);
row.open = parseFloat(row.open);
row.high = parseFloat(row.high);
row.low = parseFloat(row.low);
row.close = parseFloat(row.close);
row.vwap = parseFloat(row.vwap);
row.close_time = Number(row.close_time);
row.open_time = Number(row.open_time);
});
if (options.invert) {
rows = invertPair(rows);
}
return rows; }
/** * if the base/counter key was inverted, we need to swap * some of the values in the results */
function invertPair (rows) {
var swap;
var i;
if (options.unreduced) {
for (i=0; i<rows.length; i++) {
rows[i].rate = 1/rows[i].rate;
//swap base and counter vol
swap = rows[i].base_amount;
rows[i].base_amount = rows[i].counter_amount;
rows[i].counter_amount = swap;
//swap buyer and seller
swap = rows[i].buyer;
rows[i].buyer = rows[i].seller;
rows[i].seller = swap;
}
} else {
for (i=0; i<rows.length; i++) {
//swap base and counter vol
swap = rows[i].base_volume;
rows[i].base_volume = rows[i].counter_volume;
rows[i].counter_volume = swap;
//swap high and low
swap = 1/rows[i].high;
rows[i].high = 1/rows[i].low;
rows[i].low = swap;
//invert open, close, vwap
rows[i].open = 1/rows[i].open;
rows[i].close = 1/rows[i].close;
rows[i].vwap = 1/rows[i].vwap;
//invert buy_volume
rows[i].buy_volume /= rows[i].vwap;
}
}
return rows; }
/** * reduce * reduce all rows */
function reduce (rows) {
var buyVolume = 0;
var reduced = {
open: 0,
high: 0,
low: Infinity,
close: 0,
base_volume: 0,
counter_volume: 0,
buy_volume: 0,
count: 0,
open_time: 0,
close_time: 0
};
rows = formatExchanges(rows);
// filter out small XRP amounts
rows = rows.filter(function(row) {
if (options.base.currency === 'XRP' && row.base_amount < 0.0005) {
return false;
} else if (options.counter.currency === 'XRP' && row.counter_amount < 0.0005) {
return false;
} else {
return true;
}
});
if (rows.length) {
reduced.open_time = moment.unix(rows[0].time).utc().format();
reduced.close_time = moment.unix(rows[rows.length-1].time).utc().format();
reduced.open = rows[0].rate;
reduced.close = rows[rows.length -1].rate;
reduced.count = rows.length;
} else {
reduced.low = 0;
return reduced;
}
rows.forEach(function(row) {
reduced.base_volume += row.base_amount;
reduced.counter_volume += row.counter_amount;
if (row.rate < reduced.low) reduced.low = row.rate;
if (row.rate > reduced.high) reduced.high = row.rate;
if (row.buyer === row.taker) {
reduced.buy_volume += row.base_amount;
}
});
reduced.vwap = reduced.counter_volume / reduced.base_volume;
return reduced; } };
Maybe you should make a custom websocket that make your RPC call upgrade to 1.1 http protocol (ws).
In nodejs you can simply
// for http
var http = require('http');
// for websocket
var ws = require("nodejs-websocket")
var options = {
host: 'URL-RPC-HERE',
port: '80',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': post_data.length
}
};
var req = http.request(options, function(res) {
// after getting the response wich is the <res>
// we can upgrade it to ws
upToWebsocket(res);
});
//Upgrade to websocket
var upToWebsocket = function(json) {
var server = ws.createServer(function (conn) {
conn.on("json", function (str) {
conn.sendText(str.toUpperCase()+"!!!")
})
conn.on("close", function (code, reason) {
console.log("Connection closed")
})
}).listen(8001)
}
And also if you have Rippled running on a server this does not help because there's no RPC or WS that supports exchange API.