Jun 10
6
Well, as much as I like messing around in Linux, it’s just that – messing around. From a software development standpoint, Windows still rules. Visual Studio just can’t be beat. But that is a post for another day (soon). For now, I want to talk about how I used C# and VLC to feed my currently playing music to Twitter.
I like VLC – it plays pretty much everything and does some amazing things. It’s much more powerful than it appears to be on the surface. And I’ve posted in the past about grabbing the current track information from VLC using Perl, but honestly, since it’s a simple XML file, nearly any language can do that. Today, that language is C#.
Up until now, if song that I like comes on one of the stations that I stream, I’ve been grabbing a pen and piece of paper to jot it down for later purchase. Bleck! This is time consuming and distracting. I know that I can capture the information that I want from VLC using code, but how to easily flag songs that I like? Well, while hanging out on Twitter, it hit me – Twitter can be used to do just that!
Here’s the run down – Since I don’t want all of these songs to appear in my regular stream, I created a new Twitter account called gkurts_music. When a song comes on that I like and my application has sent the details to Twitter, I can simply hop over to that page (really, I just keep a tab open to it), and mark it as a favorite. Now I have an easy to view list of my favorite tracks. Once I purchase them, I can simply un-favorite that tweet. Simple!
Now the fun part – getting the track info to Twitter. For this, I used the Twitterizer .Net library. It’s robust and comes with some great examples.
First, we visit dev.twitter.com (as my normal user) and added a new application to my account. This is easy and is pretty self explanatory, so I won’t go into further detail. Now on to the application.
First, we need to Authorize our application to post to our new accounts stream. This is very easily done and the following code is taken almost line-for-line from the Twitterizer example. Why re-invent the wheel?
public partial class frmAuthenticate : Form
{
private string requestToken;
public frmAuthenticate()
{
InitializeComponent();
}
/// <summary>
/// Launch the form and set things up.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void frmAuthenticate_Load(object sender, EventArgs e)
{
OAuthTokenResponse request = OAuthUtility.GetRequestToken(
ConfigurationManager.AppSettings[Constants.AuthConsumerKeyName],
ConfigurationManager.AppSettings[Constants.AuthConsumerSecretName]);
requestToken = request.Token;
//set up our message and link to the Twitter oAuth page.
llFetchAuthCode.Text = Constants.MsgFirstUseMessage;
llFetchAuthCode.LinkArea = new LinkArea(llFetchAuthCode.Text.IndexOf(Constants.MsgFirstUseLinkText), Constants.MsgFirstUseLinkText.Length);
llFetchAuthCode.Tag = OAuthUtility.BuildAuthorizationUri(request.Token).AbsoluteUri;
}
/// <summary>
/// launch the browser to the Twitter oAuth page.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
Process.Start((string)llFetchAuthCode.Tag);
}
/// <summary>
/// Validate our authentication code and set the appropriate properties, if valid.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnDoAuth_Click(object sender, EventArgs e)
{
try
{
//get our access tokens.
OAuthTokenResponse accessTokens = OAuthUtility.GetAccessToken(
ConfigurationManager.AppSettings[Constants.AuthConsumerKeyName],
ConfigurationManager.AppSettings[Constants.AuthConsumerSecretName],
requestToken,
txtAuthCode.Text);
//write our settings to our application exe.config file.
Configuration appConfig = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
appConfig.AppSettings.Settings.Add(Constants.AuthAccessTokenName, accessTokens.Token);
appConfig.AppSettings.Settings.Add(Constants.AuthAccessTokenSecret, accessTokens.TokenSecret);
appConfig.AppSettings.Settings.Add(Constants.AuthUserId, accessTokens.UserId.ToString(CultureInfo.CurrentCulture));
appConfig.AppSettings.Settings.Add(Constants.AuthUserScreenName, accessTokens.ScreenName);
appConfig.Save();
ConfigurationManager.RefreshSection("appSettings"); //MAGIC
MessageBox.Show(string.Format(Constants.MsgThanksAuthentication, accessTokens.ScreenName));
Close();
}
catch (TwitterizerException ex)
{
MessageBox.Show(
string.Format(Constants.MsgFailedAuthentication, ex.ErrorDetails.ErrorMessage));
}
}
private void txtAuthCode_TextChanged(object sender, EventArgs e)
{
btnDoAuth.Enabled = true;
}
}
Now, all this does is launches a browser to Twitter asking permission for the application to post to our stream. Once the user enters the code, the account is verified and credentials are pulled in from Twitter.
Next, in the main form of the application, we build our OAuthTokens object and set its properties accordingly:
private void AuthorizeUser()
{
// try it three times.
for (int i = 0; i < 3; i++)
{
if (IsUserAuthenticated())
{
break;
}
new frmAuthenticate().ShowDialog(this); //show the form
}
//if we still aren't authenticated, drop out.
if (!IsUserAuthenticated())
{
MessageBox.Show(this, Constants.MsgFailedLogin, Constants.MsgMessageBoxTitle, MessageBoxButtons.OK);
Application.Exit();
}
//get our auth details from the app.config file and create our oAuthTokens object.
_oAuthTokens = new OAuthTokens
{
AccessToken = ConfigurationManager.AppSettings[Constants.AuthAccessTokenName],
AccessTokenSecret = ConfigurationManager.AppSettings[Constants.AuthAccessTokenSecret],
ConsumerKey = ConfigurationManager.AppSettings[Constants.AuthConsumerKeyName],
ConsumerSecret = ConfigurationManager.AppSettings[Constants.AuthConsumerSecretName]
};
decimal userId = decimal.Parse(ConfigurationManager.AppSettings[Constants.AuthUserId]);
try
{
//update our user object. Why did they name the method 'Show'? This is misleading.
_user = TwitterUser.Show(_oAuthTokens, userId);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Just call this method in the form_load event. Simple.
Now that the user is authenticated and authorized, we need to get the track info:
private string[] GetCurrentTrack()
{
string artist = "";
string title = "";
string nowPlaying = "";
var returnValue = new string[2];
// make a webrequest to the status.xml service for vlc
var request = (HttpWebRequest)WebRequest.Create(txtServiceUrl.Text);
var response = (HttpWebResponse)request.GetResponse();
Stream responseStream = response.GetResponseStream();
var reader = new XmlTextReader(responseStream);
while (reader.Read())
{
switch (reader.NodeType)
{
// We read each element and see if the name matches what we are looking for.
// If it matches, then we advance the reader so we can get the value of the element.
case XmlNodeType.Element:
if (reader.Name == "title")
{
reader.Read();
title = reader.Value;
}
if (reader.Name == "artist")
{
reader.Read();
artist = reader.Value;
}
if (reader.Name == "now_playing")
{
reader.Read();
nowPlaying = reader.Value;
}
break;
}
}
if (string.IsNullOrEmpty(nowPlaying) && string.IsNullOrEmpty(artist) && string.IsNullOrEmpty(title))
{
//something didn't work... let's just bail out instead of doing something nice and graceful.
return returnValue;
}
// if the now_playing field is populated, we are most likely listening to a stream which populates
// the now_playing field with artist and title info instead of the proper track info fields. Why?
if (!(string.IsNullOrEmpty(nowPlaying)))
{
string[] currentTrack = Regex.Split(nowPlaying, " - ");
returnValue[0] = currentTrack[0];
returnValue[1] = currentTrack[1];
}
else
{
returnValue[0] = artist;
returnValue[1] = title;
}
return returnValue;
}
Once we have the track info, we can send it to our Twitter stream. I decided to do this inside a Timer objects tick event. This way, I can have it poll VLC every 10 seconds (or however often I want to check it).
private void TimerTick(object sender, EventArgs e)
{
_counter--;
UpdateCountdown(_counter);
if (_counter == 0)
{
//get the currently playing track
string[] current = GetCurrentTrack();
string currentArtist = current[0];
string currentTitle = current[1];
//see what the last track played was
string[] last = GetLastPlayedTrackForStartup();
string lastArtist = last[0];
string lastTitle = last[1];
//if the two tracks differ, then get do an update.
if (currentArtist != lastArtist && currentTitle != lastTitle)
{
//except if the track matches an entry in our ignore list - for ads.
if (IsTrackInIgnoreList(current))
{
AppendToUpdateList("Ignoring Ad " + currentArtist + " - " + currentTitle);
SaveLastPlayedTrackToTextFile(currentArtist, currentTitle);
}
else
{
AppendToUpdateList(string.Format("Sending {0} by {1}...", currentTitle, currentArtist));
string status = string.Format("Now listenting to \"{0}\" by \"{1}\"", currentTitle,
currentArtist);
TwitterStatus.Update(_oAuthTokens, status);
SaveLastPlayedTrackToTextFile(currentArtist, currentTitle);
}
}
//reset the counter and update our API calls
_counter = Constants.MagicCountdownTime;
UpdateTwitterAPIText(GetApiInfo());
}
}
This probably looks pretty convoluted, but once you step through it, it really is pretty simple. All in all, I have 3 forms (I created one for setting up and ignore list for ads), a Utils class, a Constants class, and a few other misc. things scattered about.
You can grab the entire project on its page here. It still needs some finishing but I’m working on that and will be updating it regularly. Once I get some of the final bugs worked out, I hope to add support for some other media players in the future. Your comments and suggestions are always welcome!