sending response in message passing on history search - google-chrome-extension

I have been trying to send search history in the background page and send response back to front page. I am using message parsing .
I am unable to send response back after searching history.
--------background.js-------------
var SECONDS_PER_WEEK = 1000 * 60 * 60 * 24 * 7;
chrome.extension.onMessage.addListener(
function(request, sender, sendResponse) {
if(request === "getHistory"){
var value = getHistory();
console.log("Response" , value);
sendResponse(value);
}
}
);
function getHistory(){
var current_time = new Date().getTime();
var timeToFetch = current_time - SECONDS_PER_WEEK;
return chrome.history.search({
'text' : '',
'startTime' : timeToFetch
},
function(resp){
return resp;
});
}
The problem lies here itself as on logging I get 'undefined' as response.
Can anyone direct me to the solution

Most of the Chrome.* API functions are asynchronous so try doing it something like this:
background.js
var SECONDS_PER_WEEK = 1000 * 60 * 60 * 24 * 7;
chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
if(request === "getHistory"){
getHistory(sendResponse);
return true;
}
});
function getHistory(sendResponse){
var current_time = new Date().getTime();
var timeToFetch = current_time - SECONDS_PER_WEEK;
chrome.history.search({
'text' : '',
'startTime' : timeToFetch
},
function(resp){
console.log("Response: " + resp);
sendResponse(resp);
});
}

Related

Lambda function taking >3 seconds to run + 5-10 secs warmup each time

I have a simple node.js function with 2 REST API calls and a socket connection output hosted in an AWS lambda. It takes 5-10 secs warmup time and >3+ secs execution time.
When the code is run locally it executes both requests, socket connection and completes in about ~1300ms. Why is AWS more then double the execution time? I have set-timeout to 120seconds and memory at 128mb (default).
I appreciate the code is not very tidy; I am working on cleaning it but needed something going for the time being.
The project simply gets info from ServiceM8 via API when called by a webhook subscription, then formats the info into ZPL strings and forwards them to a tcp server for printing via thermal printer.
My questions are:
Is it my code bottle necking?
Can it be optimized to run faster?
Do i simply need to employ a warming plugin for my function to allow hot starting?
My function:
'use strict';
//Require libraries
var request = require("request");
var net = require('net');
exports.handler = (event, context, callback) => {
if (event.eventName != 'webhook_subscription') {
callback(null, {});
}
//Global Variables
var strAssetUUID;
var strAssetURL;
var strFormUUID;
var strTestDate;
var strRetestDate;
var appliancePass = true;
var strAccessToken;
var strResponseUUID;
//Printer Access
const tcpUrl = 'example.com';
const tcpPort = 12345;
var client = new net.Socket();
//UUID of Appliance Test Form.
const strTestFormUUID = 'UUID_of_form';
//Begin function
/**
* Inspect the `eventArgs.entry` argument to get details of the change that caused the webhook
* to fire.
*/
strResponseUUID = event.eventArgs.entry[0].uuid;
strAccessToken = event.auth.accessToken;
console.log('Response UUID: ' + strResponseUUID);
console.log('Access Token: ' + strAccessToken);
//URL Options for FormResponse UUID query
const urlFormResponse = {
url: 'https://api.servicem8.com/api_1.0/formresponse.json?%24filter=uuid%20eq%20' + strResponseUUID,
headers: {
// Use the temporary Access Token that was issued for this event
'Authorization': 'Bearer ' + strAccessToken
}
};
//Query form Response UUID to get information required.
request.get(urlFormResponse, function(err, res, body) {
//Check response code from API query
if (res.statusCode != 200) {
// Unable to query form response records
callback(null, {err: "Unable to query form response records, received HTTP " + res.statusCode + "\n\n" + body});
return;
}
//If we do recieve a 200 status code, begin
var arrRecords = JSON.parse(body);
//Store the UUID of the form used for the form response.
strFormUUID = arrRecords[0].form_uuid;
console.log('Form UUID: ' + strFormUUID);
//Store the UUID of the asset the form response relates to.
strAssetUUID = arrRecords[0].asset_uuid;
console.log('Asset UUID: ' + strAssetUUID);
if (strFormUUID == strTestFormUUID){
//Get the edited date and parse it into a JSON date object.
var strEditDate = new Date(arrRecords[0].edit_date);
//Reassemble JSON date to dd-mm-yyyy.
strTestDate = strEditDate.getDate() + '/' + (strEditDate.getMonth() + 1) + '/' + strEditDate.getFullYear();
//Extract the response for retest period.
var strRetestAnswer = JSON.parse(arrRecords[0].field_data);
strRetestAnswer = strRetestAnswer[0].Response;
//Appropriate function based on retest response.
switch(strRetestAnswer) {
case '3 Months':
//Add x months to current test date object
strEditDate.setMonth(strEditDate.getMonth() + 3);
strRetestDate = strEditDate.getDate() + '/' + (strEditDate.getMonth() + 1) + '/' + strEditDate.getFullYear();
break;
case '6 Months':
strEditDate.setMonth(strEditDate.getMonth() + 6);
strRetestDate = strEditDate.getDate() + '/' + (strEditDate.getMonth() + 1) + '/' + strEditDate.getFullYear();
break;
case '12 Months':
strEditDate.setMonth(strEditDate.getMonth() + 12);
strRetestDate = strEditDate.getDate() + '/' + (strEditDate.getMonth() + 1) + '/' + strEditDate.getFullYear();
break;
case '2 Years':
strEditDate.setMonth(strEditDate.getMonth() + 24);
strRetestDate = strEditDate.getDate() + '/' + (strEditDate.getMonth() + 1) + '/' + strEditDate.getFullYear();
break;
case '5 Years':
strEditDate.setMonth(strEditDate.getMonth() + 60);
strRetestDate = strEditDate.getDate() + '/' + (strEditDate.getMonth() + 1) + '/' + strEditDate.getFullYear();
break;
default:
strRetestDate = "FAIL";
appliancePass = false;
}
console.log('Appliance Pass: ' + appliancePass);
console.log('Test Date: ' + strTestDate);
console.log('Retest Period: ' + strRetestAnswer);
console.log('Retest Date: ' + strRetestDate);
//URL Options for Asset UUID query
const urlAssetResponse = {
url: 'https://api.servicem8.com/api_1.0/asset/' + strAssetUUID + '.json',
headers: {
// Use the temporary Access Token that was issued for this event
'Authorization': 'Bearer ' + strAccessToken
}
};
//Query the api for the asset URL of the provided asset UUID.
request.get(urlAssetResponse, function(err, res, body) {
//Check response code from API query
if (res.statusCode != 200) {
// Unable to query asset records
callback(null, {err: "Unable to query asset records, received HTTP " + res.statusCode + "\n\n" + body});
return;
}
//If we do recieve a 200 status code, begin
var strAssetResponse = JSON.parse(body);
//Store the asset URL
strAssetURL = 'https://sm8.io/' + strAssetResponse.asset_code;
console.log('Asset URL: ' + strAssetURL);
//generate tag and send to printer
var strZPLPass = ('^XA....^XZ\n');
var strZPLFail = ('^XA....^XZ\n');
//Now that we have our ZPL generated from our dates and URLs
//Send the correct ZPL to the printer.
client.connect(tcpPort, tcpUrl, function() {
console.log('Connected');
//Send Appropriate ZPL
if (appliancePass) {
client.write(strZPLPass);
}else {
client.write(strZPLFail);
}
console.log('Tag Successfully Printed!');
//As the tcp server receiving the string does not return any communication
//there is no way to know when the data has been succesfully received in full.
//So we simply timeout the connection after 750ms which is generally long enough
//to ensure complete transmission.
setTimeout(function () {
console.log('Timeout, connection closing...');
client.destroy();
}, 750);
});
});
}
});
};
First of all, I would suggest you stop using the request module and switch to native. Everything can be done without a tons of lines these days. request is a module with 48 total dependencies; if you do the math, that's thousands of lines for a simple GET request.
You should always minimize the complexity of your dependencies. I use a Lambda to check the health of my sites, grabbing the whole request and checking the HTML on completely different servers. VPS is located in Frankfurt, AWS in Ireland. My ms/request is ranging between 100~150 ms.
Here's a simple promise request I'm using:
function request(obj, timeout) {
return new Promise(function(res, rej) {
if (typeof obj !== "object") {
rej("Argument must be a valid http request options object")
}
obj.timeout = timeout;
obj.rejectUnauthorized = false;
let request = http.get(obj, (response) => {
if (response.statusCode !== 200) {
rej("Connection error");
}
var body = '';
response.on('data', (chunk) => {
body += chunk;
});
response.on('end', () => {
res(body);
});
response.on('error', (error) => {
rej(error);
});
});
request.setTimeout(timeout);
request.on('error', (error) => {
rej(error);
})
request.on('timeout', () => {
request.abort();
rej("Timeout!")
})
});
}
Example
const reqOpts = {
hostname: 'www.example.com',
port: 443,
path: '/hello',
method: 'GET',
headers: {
handshake: "eXTNxFMxQL4pRrj6JfzQycn3obHL",
remoteIpAddress: event.sourceIp || "lambda"
}
}
try {
httpTestCall = await request(reqOpts, 250);
}
catch (e) {
console.error(e);
}
Now based on that change switch your handler to async using exports.handler = async(event, context, callback) => {} and use console to measure the execution time of every request using console.time() and console.timeEnd() for your request or anything. From there you could see what's stepping down your code using Cloudwatch logs. Here's another example based on your code:
let reqOpts = {
hostname: 'api.servicem8.com',
port: 443,
path: '/api_1.0/formresponse.json?%24filter=uuid%20eq%20' + strResponseUUID,
method: 'GET',
headers: {
// Use the temporary Access Token that was issued for this event
'Authorization': 'Bearer ' + strAccessToken
}
}
console.time("=========MEASURE_servicem8=========")
let error = null;
await request(reqOpts, 5555).catch((e)=>{
error = e;
})
console.timerEnd("=========MEASURE_servicem8=========")
if (error){
callback(null, {err: "Unable to query form response records, received HTTP" + error}); /* or anything similar */
}
References
https://docs.aws.amazon.com/lambda/latest/dg/best-practices.html
https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html
aws lambdas are not fast by nature (as of writing this answer). The startup time is not guaranteed, and it is known to be high.
If you need performance - you will not get it this way.

setTimeout() function is not executed properly

I have to send an SMS 15 minutes before the appointment is there. I have written the code, but it is called at the time of execution itself. Is there any other method or how do I solve this?
"RP.remindePatient" is the function that has to be called 15 minutes before the appointment time.
sendApptConf(s) {
var number = Phone;
var msg = urlencode("Hello " );
var smsData = 'username=' + username + '&hash=' + hash + '&sender=' + sender + '&numbers=' + number + '&message=' + msg;
var options = {
host: 'api.textlocal.in',
path: '/send?' + smsData
};
var callback;
console.log(options);
callback = function (response) {
var str = '';
// Another chunk of data has been recieved, so append it to `str`
response.on('data', function (chunk) {
console.log("new Data received")
str += chunk;
console.log(str);
});
// The whole response has been received, so we just print it out here.
response.on('end', function () {
console.log(str);
});
}
http.request(options, callback).end();
// Send SMS using Textlocal DONE
const convertTime = consultDate + ' ' + consultTime;
var d = new Date(convertTime);
var ms = d.getTime();
var milliLess15 = ms - (15 * 60 * 1000);
console.log(milliLess15);
setTimeout(function () {
console.log("I should be called after some delay")
RP.remindePatient(userPhone, patientName, drName, consultMode, consultDate, consultTime, transId, email, paymentDetails);
}, milliLess15);
I think there is a small issue in your logic. As you would already know, setTimeout(function () {}, milliseconds) will fork the function() after milliseconds you have specified.
Now let's focus on the below snippet of code:
const convertTime = consultDate + ' ' + consultTime;
var d = new Date(convertTime);
var ms = d.getTime();
var milliLess15 = ms - (15 * 60 * 1000);
Let's say your appointment is at 12:00 pm today (in millisec let's say 1492776423) and current execution time is 10:00 am (in millisec 1485576423). By your logic, you are calling the function() after: var milliLess15 = ms - (15 * 60 * 1000) which is 1492776423 - (15 * 60 * 1000) = 1491876423. Note here that, you don't want to call function() after 1491876423 millisec. Rather you want to call it after millisec equivalent of 12:00 - 10:00 - 00:15. So your logic should look something like:
const convertTime = consultDate + ' ' + consultTime;
var d = new Date(convertTime);
var ms = d.getTime();
var currentTime = new Date();
var currentTimeMilli = currentTime.getTime();
var milliLess15 = ms - (15 * 60 * 1000) - currentTimeMilli;
Rest of the code will remain the same. Hope this answer helps you!
Don't call the variable after timeout. Just put in:
setTimeout(function () {
console.log("I should be called after some delay")
RP.remindePatient(userPhone, patientName, drName, consultMode, consultDate, consultTime, transId, email, paymentDetails);
}, 900000);

AWS X-Ray w/ HAPI - No logs in Console

Trying out AWS new service X-RAY for my microservice deployment on Kubernetes on EC2. Here are the logs I am seeing:
root#internal-reporting-test-1680758663-3gweh:/usr/src/app# cat AWSXRay.log
2016-12-06 04:36:23.5840 +00:00 [INFO] UDP message sent: {"trace_id":"1-58464048-03fbcdf5c861f95fc5204ec6","id":"105107e6c9df4bfe","start_time":1480998983.553,"name":"localhost","http":{"request":{"method":"GET","user_agent":"curl/7.38.0","client_ip":"127.0.0.1","url":"http://localhost"},"response":{"status":404,"content_length":0}},"service":{"version":"1.0.0"},"error":true,"end_time":1480998983.574}
2016-12-06 04:36:33.2560 +00:00 [INFO] UDP message sent: {"trace_id":"1-58464051-3e3799a566c0933093f51b7a","id":"94d4267609d2ba40","start_time":1480998993.205,"name":"localhost","http":{"request":{"method":"GET","user_agent":"curl/7.38.0","client_ip":"127.0.0.1","url":"http://localhost"},"response":{"status":200,"content_length":0}},"service":{"version":"1.0.0"},"end_time":1480998993.252}
2016-12-06 04:36:35.5810 +00:00 [INFO] UDP message sent: {"trace_id":"1-58464054-5df1e9ec46ef2b26b2d29965","id":"113fd36cddb5de95","start_time":1480998995.552,"name":"localhost","http":{"request":{"method":"GET","user_agent":"curl/7.38.0","client_ip":"127.0.0.1","url":"http://localhost"},"response":{"status":200,"content_length":0}},"service":{"version":"1.0.0"},"end_time":1480998995.579}
2016-12-06 04:36:41.3350 +00:00 [INFO] UDP message sent: {"trace_id":"1-58464059-0dfd2e274ca20499c6bce17f","id":"1dfac818142b2d28","start_time":1480999001.304,"name":"localhost","http":{"request":{"method":"GET","user_agent":"curl/7.38.0","client_ip":"127.0.0.1","url":"http://localhost"},"response":{"status":200,"content_length":0}},"service":{"version":"1.0.0"},"end_time":1480999001.332}
Curl I am using:
# curl localhost/v1/internal/report/users
Supervisor Conf:
[supervisord]
nodaemon=true
[program:xray]
command=/usr/src/app/xray
[program:npm]
command=npm start
Mods to express middleware to use with hapi:
/**
* Express_mw module.
*
* Exposes middleware functions to enable automated data capturing on a web service. To enable on a Node.js/Express application,
* use 'app.use(AWSXRay.express.openSegment())' before defining your routes. After your routes, before any extra error
* handling middleware, use 'app.use(AWSXRay.express.closeSegment())'.
* Use AWSXRay.getSegment() to access the current segment/subsegment.
* Otherwise, for manual mode, this appends the Segment object to the request object as req.segment.
* #module express_mw
*/
var _ = require('underscore');
var CLSUtils = require('../utils').CLSUtils;
var MWUtils = require('./mw_utils');
var Local = require('../segments/attributes/local');
var Segment = require('../segments/segment');
var getCauseType = require('../utils').getCauseTypeFromHttpStatus;
var XRAY_HEADER = 'x-amzn-trace-id';
var NAME = process.env.XRAY_TRACING_NAME;
var DEFAULT_NAME = process.env.XRAY_TRACING_DEFAULT_NAME;
/**
* Use 'app.use(AWSXRay.express.openSegment())' before defining your routes.
* Use AWSXRay.getSegment() to access the current segment/subsegment.
* Otherwise, for manual mode, this appends the Segment object to the request object as req.segment.
* #alias module:express_mw.openSegment
* #returns {function}
*/
module.exports.openSegment = function openSegment() {
if (_.isUndefined(DEFAULT_NAME) && _.isUndefined(MWUtils.defaultName)) {
throw new Error('Default segment name must be set using "process.env.XRAY_TRACING_DEFAULT_NAME"' +
' or "setDefaultName()".');
}
return function open(request, next) {
/*
* Get the true HTTP objects
*/
req = request.raw.req;
res = request.raw.res;
var amznTraceHeader = processHeaders(req);
var segment = new Segment(resolveName(req.headers.host), amznTraceHeader.Root, amznTraceHeader.Parent);
resolveSampling(amznTraceHeader, segment, res);
segment.addAttribute('http', new Local(req));
MWUtils.populate(segment);
res.on('finish', function () {
if (this.statusCode === 429)
segment.addThrottle();
else if (getCauseType(this.statusCode))
segment[getCauseType(this.statusCode)] = true;
segment.http.close(this);
segment.close();
});
if (CLSUtils.isCLSMode()) {
var ns = CLSUtils.getNamespace();
ns.bindEmitter(req);
ns.bindEmitter(res);
ns.run(function () {
CLSUtils.setSegment(segment);
if (next) { next(); }
});
} else {
req.segment = segment;
if (next) { next(); }
}
};
};
/**
* After your routes, before any extra error handling middleware, use 'app.use(AWSXRay.express.closeSegment())'.
* #alias module:express_mw.closeSegment
* #returns {function}
*/
module.exports.closeSegment = function closeSegment() {
return function close(request, next) {
req = request.raw.req;
res = request.raw.res;
var segment = CLSUtils.isCLSMode() ? CLSUtils.getSegment() : segment = req.segment;
if (segment && err) {
segment.counter = 0;
segment.close(err);
} else if (segment) {
segment.close();
}
if (next)
next(err);
};
};
function processHeaders(req) {
var amznTraceHeader = {};
if (req && req.headers && req.headers[XRAY_HEADER]) {
_.each(req.headers[XRAY_HEADER].replace(/ /g,'').split(';'), function (header) {
var pair = header.split('=');
this[pair[0]] = pair[1];
}, amznTraceHeader);
}
return amznTraceHeader;
}
function resolveName(hostName) {
if (NAME)
return NAME;
var regex = new RegExp('(?:[0-9]{1,3}\.){3}[0-9]{1,3}', 'g');
var hostIp = hostName.match(regex) || _.isEmpty(hostName);
return !hostIp ? hostName : (DEFAULT_NAME ? DEFAULT_NAME : MWUtils.defaultName);
}
function resolveSampling(amznTraceHeader, segment, res) {
var isSampled;
if (amznTraceHeader.Sampled === '1')
isSampled = true;
else if (amznTraceHeader.Sampled === '0')
isSampled = false;
isSampled = !_.isUndefined(isSampled) ? isSampled : MWUtils.shouldSample();
if (amznTraceHeader.Sampled === '?')
res.header[XRAY_HEADER] = 'Root=' + amznTraceHeader.Root + '; Sampled=' + (isSampled ? '1' : '0');
if (!isSampled)
segment.notTraced = true;
}
The changes are relatively simple while playing with this service. Just pretty much forcing the raw node HTTP objects to the middleware.
Logs are showing the UDP requests to the xray service are working as expected. Yet in the console I see no results.
Anyone have any ideas?

Node.js node-gcloud synchronous call

I'm using node-gcloud https://github.com/GoogleCloudPlatform/gcloud-node to interact with Google Cloud Storage.
I'm developing a node.js server (my first node.js project) to provide a small set of APIs to clients. Basically when an user uploads a file the API call return the signed url to show that file.
The getSignedUrl function is asynchronous https://googlecloudplatform.github.io/gcloud-node/#/docs/v0.8.1/storage?method=getSignedUrl and I can't find a way to return that result from another function.
I've started playing with Bluebird promises but I can't get to the point of it. Here is my code:
var _signedUrl = function(bucket,url,options) {
new Promise(function (resolve, reject) {
var signed_url
bucket.getSignedUrl(options, function(err, url) {
signed_url = err || url;
console.log("This is defined: " + signed_url)
return signed_url
})
})
}
var _getSignedUrl = function(url) {
new Promise(function(resolve) {
var options = config.gs
, expires = Math.round(Date.now() / 1000) + (60 * 60 * 24 * 14)
, bucket = project.storage.bucket({bucketName: config.gs.bucket, credentials: config.gs })
, signed_url = null
options.action = 'read'
options.expires = expires// 2 weeks.
options.resource= url
signed_url = resolve(_signedUrl(bucket,url,options))
console.log("This is undefined: " + signed_url)
return JSON.stringify( {url: signed_url, expires: expires} );
});
}
I think that I'm missing the basics of how it is supposed to work, so any hint will be appreciated.
Edit:
I have reworked my solution as for the first comment:
getSignedUrl: function() {
var options = config.gs
, expires = Math.round(Date.now() / 1000) + (60 * 60 * 24 * 14)
, bucket = project.storage.bucket({bucketName: config.gs.bucket, credentials: config.gs })
, signed_url = null
options.action = 'read'
options.expires = expires// 2 weeks.
options.resource= this.url
Promise.promisifyAll(bucket);
return bucket.getSignedUrlAsync(options).catch(function(err) {
return url; // ignore errors and use the url instead
}).then(function(signed_url) {
return JSON.stringify( {url: signed_url, expires: expires} );
});
}
It's not clear to me how the double return is supposed to work, but if I keep the
return bucket
what I get is this output:
{ url:
{ _bitField: 0,
_fulfillmentHandler0: undefined,
_rejectionHandler0: undefined,
_promise0: undefined,
_receiver0: undefined,
_settledValue: undefined,
_boundTo: undefined }
}
, and if remove it and keep the
return JSON.stringify( {url: signed_url, expires: expires} );
I get undefined as before. What am I missing?
Some points:
Inside a new Promise(function(res, rej){ … }) resolver callback, you actually need to call resolve() or reject() (asynchronously), not return anything.
resolve doesn't return anything. You seemed to use it like a "wait" operation that returns the result of the promise, but such is impossible. A promise is still asynchronous.
Actually, you should never need to call new Promise at all. Use Promisification instead.
Your code should rather look like
var gcloud = require('gcloud');
Promise.promisifyAll(gcloud); // if that doesn't work, call it once on a
// specific bucket instead
function getSignedUrl(url) {
var options = config.gs,
expires = Math.round(Date.now() / 1000) + (60 * 60 * 24 * 14),
bucket = project.storage.bucket({bucketName: config.gs.bucket, credentials: config.gs });
options.action = 'read';
options.expires = expires; // 2 weeks.
options.resource = url;
return bucket.getSignedUrlAsync(options).catch(function(err) {
return url; // ignore errors and use the url instead
}).then(function(signed_url) {
console.log("This is now defined: " + signed_url);
return JSON.stringify( {url: signed_url, expires: expires} );
});
}

SmoothStreaming issues on Chromecast

I'm trying to load a smoothstreaming media into the chromecast. For that I've used the samples provided by google:
<body>
<video id='vid' />
<script type="text/javascript"
src="//www.gstatic.com/cast/sdk/libs/receiver/2.0.0/cast_receiver.js">
</script>
<script type="text/javascript"
src="//www.gstatic.com/cast/sdk/libs/mediaplayer/0.3.0/media_player.js">
</script>
<script type="text/javascript">
window.onload = function() {
// If you set ?Debug=true in the URL, such as a different App ID in the
// developer console, include debugging information.
cast.receiver.logger.setLevelValue(cast.receiver.LoggerLevel.DEBUG);
var mediaElement = document.getElementById('vid');
// Create the media manager. This will handle all media messages by default.
window.mediaManager = new cast.receiver.MediaManager(mediaElement);
// Remember the default value for the Receiver onLoad, so this sample can Play
// non-adaptive media as well.
window.defaultOnLoad = mediaManager.onLoad;
mediaManager.onLoad = function (event) {
/proto
// MPEG-DASH
protocol = cast.player.api.CreateDashStreamingProtocol(host);
} else if (url.indexOf('.ism/') >= 0) {
// Smooth Streaming
protocol = cast.player.api.CreateSmoothStreamingProtocol(host);
}
// How to override a method in Host. I know that it's safe to just provide this
// method.
host.onError = function(errorCode) {
console.log("Fatal Error - "+errorCode);
window.player.unload();
};
// If you need cookies, then set withCredentials = true also set any header
// information you need. If you don't need them, there can be some unexpected
// effects by setting this value.
// host.updateSegmentRequestInfo = function(requestInfo) {
// requestInfo.withCredentials = true;
// };
console.log("we have protocol "+ext);
if (protocol !== null) {
console.log("Starting Media Player Library");
window.player = new cast.player.api.Player(host);
window.player.load(protocol, initStart);
}
mediaElement.autoplay = autoplay; // Make sure autoplay get's set
if (url.lastIndexOf('.m3u8') >= 0) {
// HTTP Live Streaming
protocol = cast.player.api.CreateHlsStreamingProtocol(host);
} else if (url.lastIndexOf('.mpd') >= 0) {
// MPEG-DASH
protocol = cast.player.api.CreateDashStreamingProtocol(host);
} else if (url.indexOf('.ism/') >= 0) {
// Smooth Streaming
protocol = cast.player.api.CreateSmoothStreamingProtocol(host);
}
// How to override a method in Host. I know that it's safe to just provide this
// method.
host.onError = function(errorCode) {
console.log("Fatal Error - "+errorCode);
window.player.unload();
};
// If you need cookies, then set withCredentials = true also set any header
// information you need. If you don't need them, there can be some unexpected
// effects by setting this value.
// host.updateSegmentRequestInfo = function(requestInfo) {
// requestInfo.withCredentials = true;
// };
console.log("we have protocol "+ext);
And on the sender:
/**
* global variables
*/
var currentMediaSession = null;
var currentVolume = 0.5;
var progressFlag = 1;
var mediaCurrentTime = 0;
var session = null;
var mediaURLs = [
'http://playready.directtaps.net/smoothstreaming/TTLSS720VC1/To_The_Limit_720.ism/',
'http://commondatastorage.googleapis.com/gtv-videos-bucket/ED_1280.mp4',
'http://commondatastorage.googleapis.com/gtv-videos-bucket/tears_of_steel_1080p.mov',
'http://commondatastorage.googleapis.com/gtv-videos-bucket/reel_2012_1280x720.mp4',
'http://commondatastorage.googleapis.com/gtv-videos-bucket/Google%20IO%202011%2045%20Min%20Walk%20Out.mp3'];
var mediaTitles = [
'Big Buck Bunny',
'Elephant Dream',
'Tears of Steel',
'Reel 2012',
'Google I/O 2011 Audio'];
var mediaThumbs = [
'images/bunny.jpg',
'images/ed.jpg',
'images/Tears.jpg',
'images/reel.jpg',
'images/google-io-2011.jpg'];
var currentMediaURL = mediaURLs[0];
/**
* Call initialization
*/
if (!chrome.cast || !chrome.cast.isAvailable) {
setTimeout(initializeCastApi, 1000);
}
/**
* initialization
*/
function initializeCastApi() {
// default app ID to the default media receiver app
// optional: you may change it to your own app ID/receiver
var applicationID = '21176C05';
var sessionRequest = new chrome.cast.SessionRequest(applicationID);
var apiConfig = new chrome.cast.ApiConfig(sessionRequest,
sessionListener,
receiverListener);
chrome.cast.initialize(apiConfig, onInitSuccess, onError);
};
/**
* initialization success callback
*/
function onInitSuccess() {
appendMessage("init success");
}
/**
* initialization error callback
*/
function onError() {
console.log("error");
appendMessage("error");
}
/**
* generic success callback
*/
function onSuccess(message) {
console.log(message);
}
/**
* callback on success for stopping app
*/
function onStopAppSuccess() {
console.log('Session stopped');
appendMessage('Session stopped');
document.getElementById("casticon").src = 'images/cast_icon_idle.png';
}
/**
* session listener during initialization
*/
function sessionListener(e) {
console.log('New session ID: ' + e.sessionId);
appendMessage('New session ID:' + e.sessionId);
session = e;
if (session.media.length != 0) {
appendMessage(
'Found ' + session.media.length + ' existing media sessions.');
onMediaDiscovered('onRequestSessionSuccess_', session.media[0]);
}
session.addMediaListener(
onMediaDiscovered.bind(this, 'addMediaListener'));
session.addUpdateListener(sessionUpdateListener.bind(this));
}
/**
* session update listener
*/
function sessionUpdateListener(isAlive) {
var message = isAlive ? 'Session Updated' : 'Session Removed';
message += ': ' + session.sessionId;
appendMessage(message);
if (!isAlive) {
session = null;
}
};
/**
* receiver listener during initialization
*/
function receiverListener(e) {
if( e === 'available' ) {
console.log("receiver found");
appendMessage("receiver found");
}
else {
console.log("receiver list empty");
appendMessage("receiver list empty");
}
}
/**
* select a media URL
* #param {string} m An index for media URL
*/
function selectMedia(m) {
console.log("media selected" + m);
appendMessage("media selected" + m);
currentMediaURL = mediaURLs[m];
var playpauseresume = document.getElementById("playpauseresume");
document.getElementById('thumb').src = mediaThumbs[m];
}
/**
* launch app and request session
*/
function launchApp() {
console.log("launching app...");
appendMessage("launching app...");
chrome.cast.requestSession(onRequestSessionSuccess, onLaunchError);
}
/**
* callback on success for requestSession call
* #param {Object} e A non-null new session.
*/
function onRequestSessionSuccess(e) {
console.log("session success: " + e.sessionId);
appendMessage("session success: " + e.sessionId);
session = e;
document.getElementById("casticon").src = 'images/cast_icon_active.png';
}
/**
* callback on launch error
*/
function onLaunchError() {
console.log("launch error");
appendMessage("launch error");
}
/**
* stop app/session
*/
function stopApp() {
session.stop(onStopAppSuccess, onError);
}
/**
* load media
* #param {string} i An index for media
*/
function loadMedia(i) {
if (!session) {
console.log("no session");
appendMessage("no session");
return;
}
console.log("loading..." + currentMediaURL);
appendMessage("loading..." + currentMediaURL);
var mediaInfo = new chrome.cast.media.MediaInfo(currentMediaURL);
mediaInfo.contentType = 'application/vnd.ms-sstr+xml';
var request = new chrome.cast.media.LoadRequest(mediaInfo);
request.autoplay = false;
request.currentTime = 0;
var payload = {
"title:" : mediaTitles[i],
"thumb" : mediaThumbs[i]
};
var json = {
"payload" : payload
};
request.customData = json;
session.loadMedia(request,
onMediaDiscovered.bind(this, 'loadMedia'),
onMediaError);
}
/**
* callback on success for loading media
* #param {Object} e A non-null media object
*/
function onMediaDiscovered(how, mediaSession) {
console.log("new media session ID:" + mediaSession.mediaSessionId);
appendMessage("new media session ID:" + mediaSession.mediaSessionId + ' (' + how + ')');
currentMediaSession = mediaSession;
mediaSession.addUpdateListener(onMediaStatusUpdate);
mediaCurrentTime = currentMediaSession.currentTime;
playpauseresume.innerHTML = 'Play';
document.getElementById("casticon").src = 'images/cast_icon_active.png';
}
/**
* callback on media loading error
* #param {Object} e A non-null media object
*/
function onMediaError(e) {
console.log("media error");
appendMessage("media error");
document.getElementById("casticon").src = 'images/cast_icon_warning.png';
}
/**
* callback for media status event
* #param {Object} e A non-null media object
*/
function onMediaStatusUpdate(isAlive) {
if( progressFlag ) {
document.getElementById("progress").value = parseInt(100 * currentMediaSession.currentTime / currentMediaSession.media.duration);
}
document.getElementById("playerstate").innerHTML = currentMediaSession.playerState;
}
/**
* play media
*/
function playMedia() {
if( !currentMediaSession )
return;
var playpauseresume = document.getElementById("playpauseresume");
if( playpauseresume.innerHTML == 'Play' ) {
currentMediaSession.play(null,
mediaCommandSuccessCallback.bind(this,"playing started for " + currentMediaSession.sessionId),
onError);
playpauseresume.innerHTML = 'Pause';
//currentMediaSession.addListener(onMediaStatusUpdate);
appendMessage("play started");
}
else {
if( playpauseresume.innerHTML == 'Pause' ) {
currentMediaSession.pause(null,
mediaCommandSuccessCallback.bind(this,"paused " + currentMediaSession.sessionId),
onError);
playpauseresume.innerHTML = 'Resume';
appendMessage("paused");
}
else {
if( playpauseresume.innerHTML == 'Resume' ) {
currentMediaSession.play(null,
mediaCommandSuccessCallback.bind(this,"resumed " + currentMediaSession.sessionId),
onError);
playpauseresume.innerHTML = 'Pause';
appendMessage("resumed");
}
}
}
}
/**
* stop media
*/
function stopMedia() {
if( !currentMediaSession )
return;
currentMediaSession.stop(null,
mediaCommandSuccessCallback.bind(this,"stopped " + currentMediaSession.sessionId),
onError);
var playpauseresume = document.getElementById("playpauseresume");
playpauseresume.innerHTML = 'Play';
appendMessage("media stopped");
}
/**
* set media volume
* #param {Number} level A number for volume level
* #param {Boolean} mute A true/false for mute/unmute
*/
function setMediaVolume(level, mute) {
if( !currentMediaSession )
return;
var volume = new chrome.cast.Volume();
volume.level = level;
currentVolume = volume.level;
volume.muted = mute;
var request = new chrome.cast.media.VolumeRequest();
request.volume = volume;
currentMediaSession.setVolume(request,
mediaCommandSuccessCallback.bind(this, 'media set-volume done'),
onError);
}
/**
* mute media
* #param {DOM Object} cb A checkbox element
*/
function muteMedia(cb) {
if( cb.checked == true ) {
document.getElementById('muteText').innerHTML = 'Unmute media';
setMediaVolume(currentVolume, true);
appendMessage("media muted");
}
else {
document.getElementById('muteText').innerHTML = 'Mute media';
setMediaVolume(currentVolume, false);
appendMessage("media unmuted");
}
}
/**
* seek media position
* #param {Number} pos A number to indicate percent
*/
function seekMedia(pos) {
console.log('Seeking ' + currentMediaSession.sessionId + ':' +
currentMediaSession.mediaSessionId + ' to ' + pos + "%");
progressFlag = 0;
var request = new chrome.cast.media.SeekRequest();
request.currentTime = pos * currentMediaSession.media.duration / 100;
currentMediaSession.seek(request,
onSeekSuccess.bind(this, 'media seek done'),
onError);
}
/**
* callback on success for media commands
* #param {string} info A message string
* #param {Object} e A non-null media object
*/
function onSeekSuccess(info) {
console.log(info);
appendMessage(info);
setTimeout(function(){progressFlag = 1},1500);
}
/**
* callback on success for media commands
* #param {string} info A message string
* #param {Object} e A non-null media object
*/
function mediaCommandSuccessCallback(info) {
console.log(info);
appendMessage(info);
}
/**
* append message to debug message window
* #param {string} message A message string
*/
function appendMessage(message) {
var dw = document.getElementById("debugmessage");
dw.innerHTML += '\n' + JSON.stringify(message);
};
I am trying media #1 which is .ism - I also tried with the /Manifest - but no luck.
In the debugging console I am getting:
[214.054s] [cast.player.api.Player] load -3
media_player.js:23
[214.060s] [goog.net.XhrIo] Opening Xhr [GET http://playready.directtaps.net/smoothstreaming/TTLSS720VC1/To_The_Limit_720.ism/Manifest -1]
media_player.js:23
[214.065s] [goog.net.XhrIo] Will abort after 10000ms if incomplete, xhr2 false [GET http://playready.directtaps.net/smoothstreaming/TTLSS720VC1/To_The_Limit_720.ism/Manifest -1]
media_player.js:23
[214.070s] [goog.net.XhrIo] Sending request [GET http://playready.directtaps.net/smoothstreaming/TTLSS720VC1/To_The_Limit_720.ism/Manifest -1]
media_player.js:23
[214.088s] [goog.net.XhrIo] Request complete [GET http://playready.directtaps.net/smoothstreaming/TTLSS720VC1/To_The_Limit_720.ism/Manifest 200]
media_player.js:23
[214.120s] [cast.player.api.Player] sourceopen
media_player.js:23
[214.142s] [cast.player.api.Player] play
media_player.js:23
[214.291s] [cast.receiver.MediaManager] Load metadata error
cast_receiver.js:19
[214.296s] [cast.receiver.MediaManager] Resetting media element
cast_receiver.js:19
[214.303s] [cast.receiver.MediaManager] Sending error message to 4:client-34217
cast_receiver.js:19
[214.307s] [cast.receiver.IpcChannel] IPC message sent: {"namespace":"urn:x-cast:com.google.cast.media","senderId":"4:client-34217","data":"{\"requestId\":9217017,\"type\":\"LOAD_FAILED\"}"}
cast_receiver.js:19
[214.171s] [cast.player.api.Player] error
media_player.js:23
Fatal Error - 1 index.html:61
[214.176s] [cast.player.api.Player] unload -3
Any idea???
Thanks!
Where did you find that URL? It's incomplete, the right one is http://playready.directtaps.net/smoothstreaming/TTLSS720VC1/To_The_Limit_720.ism/manifest, but it is unlikely to work. Other files have been posted at: Microsoft's SmoothStreaming samples files, that said, I don't believe that Microsoft is providing a CORS header that works for Chromecast.
We do provide some very simple media that you can host yourself. And we provide a very simple server, if you need that to host and provide a CORS header as well.

Resources