Friday, July 11, 2014

Changing Releases and Tags on GitHub to a Different Commit

I recently came across a situation where I accidentally created a release on GitHub, pointed at a specific tag which was tagged to a commit. I fixed the changes, committed, but wanted the release to point to the new commit instead of the old one with the error. Unfortunately, GitHub doesn't have a way to do this online, but it can be done through a combination of command line git and the GitHub website.

First, make sure your repo is up-to-date with "git fetch." This will pull down the tag you made, including the wrong one.

git fetch

Next, delete the old tag by running:

git tag -d [tag-name-here]

So, for example, I did:

git tag -d v1.1.0

Next, commit and push your latest changes including the change you want the release to point to:

git add folder/file
git commit -m "message here"
git push -u origin master

Finally, push your tag changes as well:

git push origin :[tag-name-here]

If your tag has the same name as a branch, run:

git push origin :refs/tags/[tag-name-here]

Now, open GitHub in your browser and navigate to the "releases" page. Your release should now be listed as a "Draft" because its tag has been deleted.

You can now discard this release and create a new one with the same name pointed at the most recent commit.

Wednesday, July 2, 2014

Use google-passport Authentication in Node.js Projects without Google+

NPM passport-google and 400 Error "OpenID auth request contains an unregistered domain"

If you've been using the Node.js module "passport-google" for authentication in your projects you will now notice that new projects are receiving an error stating:

"400 That's an error. OpenID auth request contains an unregistered domain."

This issue is due to the fact that Google has deprecated their OpenID API for new domains beginning in May 2014. Old projects which had previously been used should not have an issue (although you should upgrade at some point), but new projects will not be allowed to authenticate.

There are two fixes for this:

1) Convert your application to using the new, Google+ sign-in. This will require users to have a Google+ profile and approve your application to access it. The passport-google-plus module located on GitHub can do just that: https://github.com/sqrrrl/passport-google-plus

2) Convert your application to use OAuth2.0 signin. Your users will not need to have a Google+ profile and this new method is the closest match to the old. The passport-google-oauth module can help with this: https://github.com/jaredhanson/passport-google-oauth

If you choose the second option, be sure to only provide the userinfo scope (and not the google.plus scope):

passport.authenticate('google', { scope: ['https://www.googleapis.com/auth/userinfo.email'] })

One additional note is that you will now be required to register your application at https://console.developers.google.com and create a client ID and secret (which are used in the passport module).

Friday, June 27, 2014

JavaScript Print Date in YYYY-MM-DD HH:mm:ss Format With Leading Zeros

Printing out a JavaScript date with leading zeros is a bit more complex than it should be.

var now = new Date();
var dateToPrint = now.getFullYear() + '-' + ('0' + (now.getMonth() + 1)).slice(-2) + '-' + ('0' + now.getDate()).slice(-2) + ' ' + ('0' + (now.getHours() + 1)).slice(-2) + ':' + ('0' + now.getMinutes()).slice(-2) + ':' + ('0' + now.getSeconds()).slice(-2);

The .slice(-2) method allows us to add a leading "0" to every date (including those already containing two characters) and then slice out the last two.

For example, if it is 1PM, the time is 13 (getHours() + 1), but then written as 013, then sliced to 13.

Tuesday, June 24, 2014

Sending CollectD Metrics to Graphite

There are no shortage of tutorials on setting up collectd as an agent on a machine. However, I have found little help in the way of describing how to setup a centralized collectd collection server that aggregates statistics from multiple clients and sends them to Graphite. This post will help you do just that. It focuses on Ubuntu, but the instructions are universally applicable.

First, let's setup the central collectd server. This could be on the same machine as your Graphite server, but on large production environments, it is not recommended.

The Ubuntu collectd repositories do not contain the necessary write_graphite plugin, so you must download and install collectd manually. Download the source from the website at: http://collectd.org/download.shtml

Next, run:

tar jxf collectd-version.tar.bz2
cd collectd-version
./configure
make all install

Once collectd is installed, modify the /opt/etc/collectd.conf file to contain the following:

Hostname "hostname"
FQDNLookup true
BaseDir "/opt/collectd/var/lib/collectd"
PIDFile "/opt/collectd/var/run/collectd.pid"
PluginDir "/opt/collectd/lib/collectd"
TypesDB "/opt/collectd/share/collectd/types.db"
Interval 10


LoadPlugin network
<Plugin network>
Listen "*" "12345"
</Plugin>

LoadPlugin interface
<Plugin interface>
Interface "eth0"
</Plugin>

LoadPlugin write_graphite
<Plugin write_graphite>
<Node "graphing">
Host "localhost"
Port "2003"
Protocol "tcp"
LogSendErrors true
Prefix "collectd."
StoreRates true
AlwaysAppendDS false
EscapeCharacter "_"
</Node>
</Plugin>

Make adjustments for your network as needed.

Now, we are going install the collectd agent on the client and then tell it to send the metrics to the collectd server (not Graphite).

The clients do not need the write_graphite plugin and can use the older version of Collectd that ships with the repositories. On each client, run:

sudo apt-get install collectd collectd-utils

Then, cd into /etc/collectd. Backup the collectd.conf file as collectd.conf.bkp or similar and then create a new collectd.conf file. In it, enable the plugins you want and also add the following:

Hostname "hostname"
FQDNLookup true
BaseDir "/var/lib/collectd"
PIDFile "/var/run/collectd.pid"
PluginDir "/usr/lib/collectd"
TypesDB "/usr/share/collectd/types.db"
Interval 10
#Timeout 5
ReadThreads 5

LoadPlugin network
<Plugin network>
Server "collectd.domain.com" "12345"
</Plugin>

LoadPlugin cpu
LoadPlugin load
LoadPlugin disk
LoadPlugin memory
LoadPlugin processes

Include "/etc/collectd/filters.conf"
Include "/etc/collectd/thresholds.conf"

Be sure to configure the network plugin with your collectd server information.

Now, if you log into Graphite, you should see that your clients are sending all of their statistics to the collectd server which is then sending them to Graphite.

Sunday, May 11, 2014

Cast Reddit to Your Chromecast

I just finished up a weekend project called "Castddit.com." The premise is to be able to replace any Reddit URL with "castddit" and have the images from that subreddit sent to your Chromecast in an image-slideshow fashion.

I have released a beta version at: http://castddit.com.

Some of the best subreddits for this platform include the safe-for-work pron networks. You can see some demos here:


It also works well on /r/pics and /r/funny.

If you're interested in how I made the site, check out my Chromecast tutorials, which I wrote while making this site.

Feel free to critique the site and let me know what you think!

Sunday, May 4, 2014

Chromecast Development Tutorial - Your Own First App

Note: This is part of a multi-part series on developing web applications that integrate Google's Chromecast SDK. I am working on making a complete resource for web development with the Chromecast, so consider this a pre-release version and please comment if you see any issues or have recommendations on how to improve the tutorial.
Part 1 - Introduction
Part 2 - Getting Started
Part 3 - Hello World
Part 4 - Your Own First App


4 Your Own First App

Now that you’ve seen an official app from Google, we are going to begin writing our own app from scratch. Start a new project folder, called “MyFirstApp” within the /var/www directory of your server. Within this folder, create a file called “index.html” and then open it for editing within your editor of choice.


Including the Cast Chrome Sender API

In order to communicate with the Chromecast extension within Chrome, you will need to include the Chrome Sender API library. This is done very simply by including the following JavaScript within the <head> of your page like so:


<head>
<script type="text/javascript" src="https://www.gstatic.com/cv/js/sender/v1/cast_sender.js"></script>
</head>


A First Page

The code sample below shows a very basic first page that includes the Chrome Sender API, a custom JavaScript file called “script.js,” and a button. In the following steps, we will use JQuery to handle a button click, so take note that JQuery is also included within the page’s <head>. (Note: it is common practice to place the JQuery at the end of the page body so that it does not slow down the page load, but for simplicity’s sake, I am including it in the <head>.)


index.html
<!DOCTYPE html>
<html>
<head>
       <title>My First App</title>
       <script type="text/javascript" src="https://www.gstatic.com/cv/js/sender/v1/cast_sender.js"></script>
       <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
</head>
<body>
       <form>
               <button type="button" id="castme">Click To Cast</button>
       </form>
       <script type="text/javascript" src="script.js"></script>
</body>
</html>


Save the index.html file and create another file in the root directory called “script.js.” This is where we will be placing all of the custom code to interact with the Chromecast, as well as code to handle the button click (which will eventually cast an image to the Chromecast). Within the script.js file, add the following:


script.js
$( document ).ready(function(){
       var loadCastInterval = setInterval(function(){
               if (chrome.cast.isAvailable) {
                       console.log('Cast has loaded.');
                       clearInterval(loadCastInterval);
                       initializeCastApi();
               } else {
                       console.log('Unavailable');
               }
       }, 1000);
});


Let’s look at some key parts of this script. To start, it uses JQuery’s document.ready function to execute the code when the page has loaded. Secondly, it sets a one second (1000 milliseconds) interval which attempts to connect to the Cast API. If the API has not yet loaded, the loop will try again after a second. If it has, it calls the function “initializeCastApi()” and stops looping (clearInterval). I have also added some console logging so that we can debug within the browser.


If you save the files, browse to your page, and open the Chrome Developer Tools (within the browser, not the Chromecast Developer Tools), you should see the following:


Screen Shot 2014-04-30 at 10.45.09 PM.png
Figure 4-1 - Initial Initialization


Notice that there is a ReferenceError because we have not coded the iniitalizeCastApi function yet. However, you should see “Cast has loaded.” Congratulations, you’ve initialized your first Chromecast app!


From here, there is a lof of code to type out in order to get even the most basic functionality. This is because the Chromecast API requires all sorts of functions for success and failures. I will do my best to walk you through each step. Begin by creating the initializeCastApi() function directly below your previous code:


script.js
function initializeCastApi() {
       var applicationID = chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID;
       var sessionRequest = new chrome.cast.SessionRequest(applicationID);
       var apiConfig = new chrome.cast.ApiConfig(sessionRequest,
               sessionListener,
               receiverListener);
       chrome.cast.initialize(apiConfig, onInitSuccess, onInitError);
};


Immediately, you can see that there are now a number of functions we will need to create. But first, let’s understand this code.


The main objective of this code is to initialize a session with the Chromecast. To do this, the Chromecast requires an applicationID. This ID is provided via the cast API as the DEFAULT_MEDIA_RECEIVER_APP_ID (the ID of the Chromecast). For now, don’t worry about this ID; it is simply a unique ID associated with the Chromecast. However, later this ID will be customized when custom “receiver applications” (code that customizes the look and feel on the Chromecast) are used.


Once the applicationID is assigned, it is passed into the sessionRequest method which creates a session with the Chromecast. Next, an apiConfig object is created by passing the sessionRequest (just created), a sessionListener function (not yet created), and a receiverListener function (also not yet created) into the ApiConfig function of the Cast API:


var apiConfig = new chrome.cast.ApiConfig(sessionRequest,
               sessionListener,
               receiverListener);


Finally, the last step is to use the apiConfig object that was just created, along with success and error functions (onInitiSuccess and onInitError), to initialize the Chromecast setup.


The next step is to create the two callback functions, sessionListener and receiverListener, which we passed to the ApiConfig method earlier. Also, don’t forget about the onInitSuccess and onInitError functions, we will be creating those shortly as well.


Sessions

A session, very simply put, is a period of time during which a user has connected to, and is interacting with, the Chromecast API. To use YouTube as an example, if you cast a video from the YouTube video page, you have created a session with the Chromecast. Browsing to a different page, then coming back to YouTube, or refreshing the original page does not change that session. The session exists until it is either destroyed by the user (disconnecting via the Cast extension), destroyed programmatically, or destroyed via a connection error. Now, we are going to create the sessionListener function which was passed to the ApiConfig method earlier. First, add a global JavaScript variable called “session” at the top of your code:


script.js
var session = null;

$( document ).ready(function(){
...


Then, add a sessionListener function at the bottom of your script.js file:


script.js
function sessionListener(e) {
       session = e;
       console.log('New session');
       if (session.media.length != 0) {
               console.log('Found ' + session.media.length + ' sessions.');
       }
}


This code establishes a session if one does not exist; but if a session does exist, it will log that fact. We will dive deeper into session management later in this book.

Receivers

The Chromecast API works by using a sender (the application we are writing) and a receiver (the application running on the Chromecast). The sender sends instructions to the receiver which then executes them. The “receiverListener” callback function, which was passed to the ApiConfig method earlier, is responsible for ensuring a valid receiver application is available. In other words, it makes sure that a Chromecast on the network is capable of receiving instructions. Add a receiverListener as follows:


script.js
function receiverListener(e) {
       if( e === 'available' ) {
               console.log("Chromecast was found on the network.");
       }
       else {
               console.log("There are no Chromecasts available.");
       }
}


Success and Error Functions

Remember earlier when we passed “onInitiSuccess” and “onInitError” to the initialize method? Well now we are going to define what should happen when the initialization either succeeds or fails. Add the following functions at the end of the script.js file:


script.js
function onInitSuccess() {
       console.log("Initialization succeeded");
}

function onInitError() {
       console.log("Initialization failed");
}


You can now save the file and test your work. Reloading the page should result in the following appearing within the JavaScript console of the Developer Tools:


Screen Shot 2014-05-01 at 12.27.31 AM.png
Figure 4-2 - Successful Initialization


Involving the User

The next step involves user interaction in order to complete successfully. Because of security concerns, your application cannot simply commandeer a user’s Chromecast and begin casting on its own. Instead, Google requires that you prompt the user (by launching the Chromecast extension dialog) to select a device to which the media should be sent.


Screen Shot 2014-05-01 at 12.38.05 AM 1.png
Figure 4-3 - The Familiar Cast Extension Popup


In order to launch this dialog, we need a function called “launchApp” to be called when when we want to launch the casting. We can do that by attaching a click event handler to the button we created earlier:


script.js
$('#castme').click(function(){
       launchApp();
});


Notice the addition of the “launchApp()” call when the button is clicked. Now, create the “launchApp” function:


script.js
function launchApp() {
       console.log("Launching the Chromecast App...");
       chrome.cast.requestSession(onRequestSessionSuccess, onLaunchError);
}


The second line of the function calls the requestSession method of the cast object. This method takes two arguments: onRequestSessionSuccess, which is called when a session has been successfully created (i.e. the user selected a Chromecast from the dialog), and onLaunchError, which is called when the session fails for some reason (most commonly when the user does not select a Chromecast from the popup).


In order to successfully test these new additions, we first need to create the two functions referenced above. Eventually, we will be adding a lot more functionality to the onRequestSessionSuccess function, but for now we will simply assign the session to the session variable and then log the result. The error function will simply log what happened if the user does not select a device from the popup. Here is the new code:


script.js
function onRequestSessionSuccess(e) {
       console.log("Successfully created session: " + e.sessionId);
       session = e;
}

function onLaunchError() {
       console.log("Error connecting to the Chromecast.");
}


This page can now be tested in the browser. Reload the page and click the button and you should see the popup appear. If you pick a Chromecast, you should see the success message. If you do not, you should see the error.
Screen Shot 2014-05-03 at 11.10.33 AM.png
Figure 4-4 - Clicking the Cast Button


Notice that the text of the popup no longer says “Cast this Tab,” but instead says “Cast <website name> to…”. This is because the official Cast API is being invoked. If you select your Chromecast from the list, you will now see the success message in the console. Clicking out of the window without selecting a device will cause the error message to be displayed.
Screen Shot 2014-05-03 at 11.20.16 AM.png
Figure 4-5 - Successful Casting


Screen Shot 2014-05-03 at 11.21.22 AM.png
Figure 4-6 - Unsuccessful Casting


We now have a session with the Chromecast! This means that we can send media to the device to display on the user’s television (hasn’t the Internet come a long way?).


Casting Media

There are a number of moving parts when it comes to casting media to the device once a session has been established, so I am just going to start out with the shortest amount of code that can cast an image, then walk through the pieces. First, edit the onRequestSessionSuccess function to call a function called “loadMedia” on success:


script.js
function onRequestSessionSuccess(e) {
       console.log("Successfully created session: " + e.sessionId);
       session = e;
       loadMedia();
}


Then, add the following code to the end of the script file:


script.js
function loadMedia() {
       if (!session) {
               console.log("No session.");
               return;
       }

       var mediaInfo = new
chrome.cast.media.MediaInfo('http://i.imgur.com/IFD14.jpg');
       mediaInfo.contentType = 'image/jpg';
 
       var request = new chrome.cast.media.LoadRequest(mediaInfo);
       request.autoplay = true;

       session.loadMedia(request, onLoadSuccess, onLoadError);
}

function onLoadSuccess() {
       console.log('Successfully loaded image.');
}

function onLoadError() {
       console.log('Failed to load image.');
}


The first function, “loadMedia,” is the function called when a session is successfully initialized. The first thing it does is make sure that a valid session is set. If not, there is nothing to cast, so it returns without doing anything. However, if a session does exist, a new “mediaInfo” object is created by calling chrome.cast.media.MediaInfo and passing in a URL of an image (movies, GIFs, and other media can be passed in, but for now let’s stick with images). This image URL is one of a cute kitten hosted on Imgur.


Once created, mediaInfo can be passed into the LoadRequest method of chrome.cast.media. This is assigned to the request object which is finally passed into the session.loadMedia method. Although this sounds complex or as if it is additional, unnecessary work, the methodology will become clear later when more attributes are added to the image and when custom receiver applications are used.


The final two functions are simply success and failure callbacks which log the result of casting the image. Now that everything is in place, let’s try to cast! Save the page, reload, and then cast to your device. If everything goes right, you should see an image of a kitten on your television. (Note: if the image URL no longer works in the distant future, simply replace it with any .jpg image URL.)


Screen Shot 2014-05-03 at 12.03.09 PM.png
Figure 4-7 - Successfully Casting an Image


Screen Shot 2014-05-03 at 12.33.09 PM.jpg
Figure 4-8 - Image Casting to Television


Ending the Session

The final step in the application is to stop the casting session. A user can initiate this process by clicking the “Stop Casting” button within the Chrome extension, but in many cases, you will want to stop a session programmatically, or provide a button within your web application that can stop the session. To do this, first add a button to your index.html file:


index.html
<form>
       <button type="button" id="castme">Click To Cast</button>
       <button type="button" id="stop">Click To Stop</button>
</form>


Now, add a button click event handler in your script:


script.js
$('#stop').click(function(){
       stopApp();
});

function stopApp() {
       session.stop(onStopAppSuccess, onStopAppError);
}

function onStopAppSuccess() {
       console.log('Successfully stopped app.');
}

function onStopAppError() {
       console.log('Error stopping app.');
}
This code follows the same conventions we have seen earlier, so I won’t explain every aspect, but note that the button click event handler calls the stopApp function, which calls the stop method of the session object, passing in success and error functions as callbacks. Try reloading the page, casting the image, then clicking stop. Congratulations, you’ve written your first app!