On stepping back from TweetSharp and thoughts on .Net OSS

Posted on Mon 24 May 2010 in General

See our joint open letter to the TweetSharp community here. What follows are my own personal thoughts regarding our decision.

As one of the two authors of TweetSharp, I've been fortunate enough to contribute to a project that, by any measure, is popular and well used. It's been downloaded thousands of times by developers and used in applications all over the world (and at least once from orbit on the ISS). We consistently see it recommended by other developers on Twitter and Stack Overflow and other forums where developers gather. This makes me feel happy, and proud of what we have accomplished.

Knowing that a large user base depended on our efforts, I personally felt a responsibility to deal with important issues quickly, to respond to new Twitter features and get them in the library as fast as possible, and to help new users get up to speed without too much difficulty.

Either because of the hands-on approach, or other factors, TweetSharp has begun to feel more like our product than anything else. No one but Daniel and me have contributed in a significant way eventually it began to feel more like a vendor/customer relationship than a community of users trying to build something together.

If you examine the commit history for the project, you'll see that most of my checkins happen between Midnight and 3am in my timezone, and on weekends. This is because I have a full-time job and a family, so my most productive time is late in the evening when everyone else is asleep and I can work without distraction. In the long-run, this kind of effort isn't sustainable. With user-streams, annotations, and who knows what other Twitter features coming on board in the next little while and beyond, the workload isn't going to diminish, and frankly, I can't continue working as much as I have on TweetSharp while maintaining my commitments to my job and my family, and I can't give up my family or my income to work on a product and give it away for free.

As such, Dan and I talked a lot and thought a lot about how to continue. Attempting to commercialize it either by selling support contracts or making it closed-source and selling licenses was discussed, as was trying to find an ongoing commercial sponsor that would allow one or both of us to continue on a full-time basis. Ultimately, we decided we'd let the community determine its fate.

I get the feeling, and I'll be delighted to be proven wrong, that your average .Net developer's interest in open source stops shortly after the price tag. Maybe we haven't done a good enough job making what we consider to be implicit (that open-source projects encourage and accept community contributions), explicit. (Maybe I should have dangled ridiculous carrots earlier and more often.) Or maybe .Net devs are so accustomed to being handed code and guidance from Microsoft and "the Internet" in general that they don't even bother to realize that behind a lot of libraries are one or two people giving up their spare time to make something useful.

Whether TweetSharp thrives or whithers on the vine will be up to its user base. Personally I'm happy to continue in a custodial role managing a steady stream of community contributions or, should some benefactor decide there's cause enough to fund the project's continuation that way, as a full-time developer on the project, but I hope I've seen my last 2:30am checkin for a while.


Managing Friends and Followers with Tweetsharp – Part 4 - UI

Posted on Wed 27 January 2010 in General

See Part 1, Part 2, Part 3

It's been a busy time in the world of TweetSharp as we've been pushing toward something we can dub the official 1.0 release for a while now, with a ways yet to go. However, it's high time I returned to this series, so without further ado...

One nice thing about the objects you get back from TweetSharp that represent the Twitter data model is that they implement INotifyPropertyChanged, so you can easily bind to them in WPF or Silverlight.

To make this app useful, I need to show some basic information about the users I'm displaying in the app. For this we will use a WPF DataTemplate. I want to show the user's avatar, both their screen name and their'real' name, their bio information, and a count of how many friends and followers they have. Since those are all bindable properties on the TwitterUser class, th data template is pretty straightforward XAML.

   <DataTemplate x:Key="TwitterUserTemplate" DataType="x:Type
local:TwitterUser">
   <Grid>
   <Grid.RowDefinitions>
   <RowDefinition Height="Auto" MinHeight="20" />
   <RowDefinition Height="\*" MinHeight="35" />
   <RowDefinition Height="Auto" MinHeight="14" />
   </Grid.RowDefinitions>
   <Grid.ColumnDefinitions>
   <ColumnDefinition Width="Auto" />
   <ColumnDefinition Width="\*" />
   <ColumnDefinition Width="Auto" />
   </Grid.ColumnDefinitions>
   <StackPanel Orientation="Horizontal" Grid.Row="0" Grid.Column="0"
Grid.ColumnSpan="3">
   <TextBlock x:Name="ScreenNameBlock" FontSize="14" Height="17"
TextWrapping="Wrap" FontWeight="Bold" Text="{Binding
Path=ScreenName}"/>
   <TextBlock x:Name="UserNameBlock" Margin="5,0,0,0" FontSize="14"
Height="17.342" TextWrapping="Wrap" Text="{Binding Path=Name}"/>
   </StackPanel>
   <Border Height="55" Width="55" Padding="2,2,2,2" Margin="0,0,5,0"
Grid.Row="1" BorderBrush="#FF530000" BorderThickness="2"
CornerRadius="6" Grid.RowSpan="2">
   <Image x:Name="ProfileImage" Source="{Binding Path=ProfileImageUrl}"
Width="50" Height="50" />
   </Border>
   <Border BorderBrush="#FF808084" BorderThickness="1" Grid.Row="1"
Grid.ColumnSpan="2" Grid.Column="1" Margin="0,5,0,0"
VerticalAlignment="Top">
   <TextBlock x:Name="BioText" FontSize="12" Width="110" Height="Auto"
TextWrapping="Wrap" MinHeight="47" MinWidth="250" Text="{Binding
Path=Description}"></TextBlock>
   </Border>
   <TextBlock x:Name="FollowCount" FontSize="9" Grid.Row="2"
Grid.Column="1" Text="{Binding Path=FriendsCount,
StringFormat=Following:{0}}"></TextBlock>
   <TextBlock x:Name="FriendCount" FontSize="9" Grid.Column="2"
Grid.Row="2" Text="{Binding Path=FollowersCount,
StringFormat=Followers:{0}}"></TextBlock>
   </Grid>
   </DataTemplate>

The key points to notice here are the DataType="x:Type local:TwitterUser" attribute on the DataTemplate element, which specifies the type of object this template is used for, and the various {Binding Path=xyz} attribute values that specify what properties of the TwitterUser we're binding to.

This will produce a user-box that looks like this:

image0

Like many developers, my design-fu is kind of weak, the UI itself is rather bland and uninteresting. (Though some might say that's befitting the subject it's displaying in this case - bazinga?)

To display a collection of these (such as the 'jerks' and 'spammers' lists we've been working with so far), we'll simply create a listbox in the application and tell it to use the previously defined template for its items (again in XAML):

<ListBox Name="JerksList" ItemTemplate="{StaticResource TwitterUserTemplate}" ></ListBox>

Then, in our code, we'll bind the "jerks" collection to the JerksList listbox like so:

var items = new Binding {Source = jerks};
JerksList.SetBinding(ItemsControl.ItemsSourceProperty, items);

Obviously, the process is be repeated for the "Spammers" list as well, and it would not be a terribly difficult stretch to adapt this template to display Tweets (aka. TwitterStatus objects) instead of users.

In the upcoming final installment, we'll wire up actions that allow us to follow, block, unfollow and report (as spammers) the users in our lists. This will give us an elementary tool we can use to begin manage our friends and followers lists.


Managing Friends and Followers with Tweetsharp - Part 3 - MultiThreading Tweetsharp for the User Interface

Posted on Wed 18 November 2009 in General

Next, we want to build some UI to show us the data we've got back from Twitter. For this we're going to use WPF. Before we get started with that, however, a bit more refactoring is needed. Since we want our UI to be nice and responsive, we're going to do all of our talking to twitter on background threads. To that end, we want to make our TweetSharp code thread safe. We have basically have two major requests that we need to make, one to get all of our friends and one to get all of our followers. We can send those off more or less simultaneously, and wait for twitter to send back the response, all the while keeping the UI thread free to repaint the form as needed.

To that end, let's wrap our twitter code up into a class. This also has the nice side-effect of limiting our dependency on TweetSharp from the UI layer. We could sprinkle TweetSharp calls throughout the UI, but that would be a refactoring nightmare should Daniel and I ever fall off a cliff and stop updating it, forcing you to move to one of the other (less fun ;) ) libraries out there.

I'm not feeling particularly creative, so I'm going to just call the class "Twitter":

namespace Diller.SocialGraphMinder
{
    internal class Twitter
    {
        private readonly string _userName;
        private readonly string _password;
        private TwitterUser _User;

        public Twitter( string username, string password )
        {
            _userName = username;
            _password = password;
            IsAuthenticated = Authenticate();
        }

        private bool Authenticate()
        {
            var twitter = FluentTwitter.CreateRequest()
                .AuthenticateAs(_userName, _password)
                .Account().VerifyCredentials()
                .AsJson();

            var response = twitter.Request();
            _User = response.AsUser();
            return _User != null;
        }

        public bool IsAuthenticated{ get; private set; }
   }
}

Nothing particularly interesting there. Upon construction, the class performs a credentials verification with the given credentials and sets the IsAuthenticated bool so we know it's safe to proceed.

Next, we'll add the friend and follower fetching code:

 //add these methods
 public IEnumerable<TwitterUser> GetFriends()
 {
     if (!IsAuthenticated)
     {
         throw new InvalidOperationException("Not authenticated");
     }
     if ( _friends == null ) //only do this if we don't already have the
  friends list
     {
         var twitter = FluentTwitter.CreateRequest()
            .AuthenticateAs(_userName, _password)
            .Configuration.UseGzipCompression() //now using compression for
  performance
            .Users().GetFriends().For(_userName)
            .CreateCursor()
            .AsJson();
        _friends = GetAllCursorValues(twitter, s => s.AsUsers());
     }
     return _friends; //return either the newly fetched list, or the cached copy
 }

 public IEnumerable<TwitterUser> GetFollowers()
 {
     if ( !IsAuthenticated)
     {
         throw new InvalidOperationException("Not authenticated");
     }
     if (_followers == null)
     {
         var twitter = FluentTwitter.CreateRequest()
             .AuthenticateAs(_userName, _password)
             .Configuration.UseGzipCompression()
             .Users().GetFollowers().For(_userName)
             .AsJson();

        _followers = GetAllCursorValues(twitter, s => s.AsUsers());
     }
     return _followers;
 }

//we also need the cursor paging code from part 2
private static IEnumerable<T> GetAllCursorValues<T>(ITwitterLeafNode twitter, Func<string, IEnumerable<T>> conversionMethod)
 {
     long? nextCursor = -1 ;
     var ret = new List<T>();
     do
     {
         twitter.Root.Parameters.Cursor = nextCursor;
         var response = twitter.Request();
         IEnumerable<T> values = conversionMethod(response);
         if (values != null)
         {
            ret.AddRange(values);
         }
         nextCursor = response.AsNextCursor();
     } while (nextCursor.HasValue &amp;amp;amp;&amp;amp;amp;
     nextCursor.Value != 0);
     return ret;
 }

Finally, lets add some methods to get the data we're really after, the aforementioned 'spammers'(users who follow us, but whom we don't follow), and 'jerks' (users whom we follow, but who do not follow us back).

public IEnumerable<TwitterUser> GetFollowersWhoArentFriends()
{
friends = GetFriends();
followers = GetFollowers();
return followers.Except(friends);
}

If we left this code as-is, it would work perfectly well in a single-threaded application. Whatever code path got to the 'GetFriends' and 'GetFollowers' methods first would make all the proper calls and cache the results in the member variables. However if you try to use this class in a multi-threaded fashion you're going to be in for a world of hurt. (Well, really, it most likely just plain ol' won't work).

If, for example, you create two threads, one to call the 'GetFriendsWhoDontFollowBack' method and the other to call the 'GetFollowersWhoArentFriends' method, both are going to call GetFriends() and GetFollowers(), likely before the other one has finished. At best, you're duplicating work and chewing up your API limit unnecessarily, at worst, you're going to confuse twitter by resetting the cursor on the second thread somewhere in the middle of paging through it on the first, causing it to throw HTTP 500 errors. (This is what happened when I tried it, and it's something that probably warrants its own blog post as it was unexpected).

So, to remedy this, we need to synchronize access to the TweetSharp calls so we're not making a tangly mess:

   //add the following private members
   private readonly object _friendsLock = new object();
   private readonly object _followersLock = new object();

   //amend the getfriends and getfollowers methods so they look like
this:
   public IEnumerable<TwitterUser> GetFriends()
   {
   if (!IsAuthenticated)
   {
   throw new InvalidOperationException("Not authenticated");
   }
   //see if friends are already fetched
   if (_friends == null)
   {
   //if not already fetched
   //wait for exclusive access to this code
   lock (_friendsLock)
   {
   //need to double-check here
   //another thread might have set the member
   //while we were waiting
   if (_friends == null)
   {
   var twitter = FluentTwitter.CreateRequest()
   .AuthenticateAs(_userName, _password)
   .Configuration.UseGzipCompression()
   .Users().GetFriends().For(_userName)
   .CreateCursor()
   .AsJson();

   _friends = GetAllCursorValues(twitter, s => s.AsUsers());
   }
   }//lock is released here
   }
   return _friends;
   }

   //make the same adjustments to this method:
   public IEnumerable<TwitterUser> GetFollowers()
   {
   if ( !IsAuthenticated)
   {
   throw new InvalidOperationException("Not authenticated");
   }
   if (_followers == null)
   {
   lock (_followersLock)
   {
   if (_followers == null)
   {
   var twitter = FluentTwitter.CreateRequest()
   .AuthenticateAs(_userName, _password)
   .Configuration.UseGzipCompression()
   .Users().GetFollowers().For(_userName)
   .AsJson();

   _followers = GetAllCursorValues(twitter, s => s.AsUsers());
   }
   }
   }
   return _followers;
   }

A few important points:

  • We used separate lock objects around the fetching of _friends and _followers objects as they are independent and can be fetched simultaneously.
  • For best performance we only lock when we're going to do the fetch. Once the objects exist they can be read without locking by any thread.
  • The double-check for null after acquiring the lock is important to prevent duplication of effort.
  • If we decide to periodically update the _friends and _followers lists, we would probably want to convert our locking behavior to a ReaderWriterLockSlim type, which is ideal for resources that are read a lot but infrequently updated.

It's also worth noting that Tweetsharp instances aren't thread safe. You should never try to use the same instance of a FluentTwitter request across multiple threads without doing your own synchronization. Since they are designed to be transient, it's best to just create a new one on each thread that you're using to talk to Twitter and leave it there.

This post has gotten too long already, so let's end it here and take it up again in part 4...at which point, I promise, we will actually put pixels on the screen.


Manage your Twitter Friends and Followers with Tweetsharp – Part 2 - Refactoring

Posted on Tue 10 November 2009 in General

In Part 1 we covered getting all of our friends and followers then using LINQ to find users who appeared in only one or the other collections.

However, the code to fetch followers and friends had a lot of duplication in it because they both use cursors to page through the results, and require a loop and some cursor management code to make sure we get all the results.

We can take care of that duplication by moving all the cursor stuff into a generic method that uses its generic type parameter a lambda expression to take care of the context-specific stuff:

   static IEnumerable GetAllCursorValues(ITwitterLeafNode twitter, Func>
conversionMethod)
   {
      long? nextCursor = -1 ;
      var ret = new List();
      do
      {
          twitter.Root.Parameters.Cursor = nextCursor;
          var response = twitter.Request();
          IEnumerable values = conversionMethod(response);
          if (values != null)
          {
              ret.AddRange(values);
          }
          nextCursor = response.AsNextCursor();
      } while (nextCursor.HasValue && nextCursor.Value != 0);
      return ret;
   }

This method will work with any of the APIs that use cursors. The type parameter T represents the type of object you expect to get back (as an IEnumerable) -in our case we're expecting TwitterUser, and the conversionMethod is the method that deserializes the response into the expected format - in our case we we're using the AsUsers method.

Note that we've "opted-out" of the fluent nature of the Tweetsharp interface and we're setting the Cursor property directly on the Parameters object of the root FluentTwitter instance.

So, the code to get friends and followers is now a lot cleaner, we can omit all of the cursor stuff from it and use our new method to handle that:

   //create the tweetsharp request to get followers
   //omit any cursor stuff as that's handled by the GetAllCursorValues
method
   var getFollowersReq = FluentTwitter.CreateRequest()
   .AuthenticateAs(_userName, _password)
   .Users().GetFollowers()
   .AsJson();

   //call our new method to get all of the values back - will make
multiple calls if necessary
   var followers = GetAllCursorValues(getFollowersReq , response =>
response.AsUsers());

   //do the same to get friends.
   var getFriendsReq = FluentTwitter.CreateRequest()
   .AuthenticateAs(_userName, _password)
   .Users().GetFriends()
   .AsJson();
   var followers = GetAllCursorValues(getFriendsReq , response =>
response.AsUsers());

   //again, use the LINQ extension methods on IEnumerable to get the
differences between the two lists
   var jerks = friends.Except(followers);
   var spammers = followers.Except(friends);

Much nicer.

Now that that's taken care of, we need some user interface - we'll do that in Part 3.


Manage your Twitter Friends and Followers with Tweetsharp - Part 1

Posted on Sat 07 November 2009 in General

I've been looking for a better way to keep track of my Twitter friends and followers for a while now. I'm particularly bad at not following back interesting people who follow me because the emails from Twitter don't contain the user's bio information and I'm often too busy or distracted to go and login to the twitter.com to see if the new follower merits a follow-back (i.e. isn't a fembot or some kind of "social media expert" promising to bring me riches, or a billion followers, or a billion rich followers).

To that end, I'm working on a small utility app explicitly for managing your social graph, which I will blog about as I build it, then ultimately make available as a download.

So, for part 1, I'm going to start with finding users who follow me, but who I don't follow back. This is pretty easy to do with a few Tweetsharp calls and a bit of LINQ.

First, I'll get all of my friends (people I follow):

   long? nextCursor = -1; //creates a new cursor the first time we call
GetCursor()
   var friends = new List();
   do
   {
       var twitter = FluentTwitter.CreateRequest()
           .AuthenticateAs(_userName, _password)
           .Users().GetFriends()
           .GetCursor(nextCursor.Value)
           .AsJson();

       var response = twitter.Request();
       var users = response.AsUsers();
       if (users != null)
       {
           friends.AddRange(users);
       }
       nextCursor = response.AsNextCursor();
       } while (nextCursor.HasValue &amp;&amp; nextCursor.Value != 0);

The code to get followers is almost identical, except we call 'GetFollowers' instead of 'GetFriends':

    var response = twitter.Request();
    var users = response.AsUsers();
    if (users != null)
    {
        followers.AddRange(users);
    }
    nextCursor = response.AsNextCursor();
    } while (nextCursor.HasValue &amp;&amp; nextCursor.Value != 0);

At this point we have two TwitterUser collections, one containing friends and the other containing our followers. We can use LINQ's extension methods to find the objects that exist in only one or the other collection with a single call:

//get people who follow us, but don't follow back (must be spammers)
var spammers = followers.Except(friends);

And that's it. Coming up in Part 2, we'll refactor the code that gets friends and followers to reduce duplication, then we'll start building out some UI so that we can act on the information we have.


Help Fight Twitter Spam with TweetSharp

Posted on Sat 17 October 2009 in General

You may have noticed a new option that recently appeared on the twitter UI:

image0|

This actually performs two operations, it blocks the user from following you and notifies the Twitter enforcement folks that you believe the person to be a spammer. You used to be able to do this second step by sending a direct message to the @spam account with the screen name of the spammer account, but not a lot of people knew about it, and most clients didn't automate the process for you.

True to form, the twitter API folks added a new REST API for doing the same thing programatically, so hopefully we'll start seeing clients make use of it.

To that end, we've added support for it to TweetSharp so if you're building (or have already built) a client app with TweetSharp, then you can easily expose this function to your users.

Here's the code for reporting a spammer using their screen name and OAuth:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
//this is an authenticated call, so you need to authenticate with
either OAuth or Basic auth.
var twitter = FluentTwitter.CreateRequest()
.AuthenticateWith(CONSUMER_KEY, CONSUMER_SECRET,
      ACCESS_TOKEN, ACCESS_SECRET)
.ReportSpam()
.ReportSpammer("examplespammer")
.AsJson();

var response = twitter.Request(); //make the request
var spammer = response.AsUser();

You can also pass a user ID to the ReportSpammer method. If everything worked properly, you should get a user object back as the response.

Note that this doesn't guarantee action on twitter's part beyond looking into it. It doesn't cause the user to be auto-banned or anything. Also, as it is on Twitter.com, using this method also blocks the spammer on behalf of the authenticated user, so there's no need to make a second call to create the block.


Open Source: Don't just be a taker.

Posted on Tue 22 September 2009 in General

Over the weekend I had the...um, good fortune (?) to see this supposedly motivational poster:
Don't Await It, Create It

While the sentiment is trite and cheesy, it's still somewhat pertinent to the world of .Net, especially when it comes to Open Source Software.

While I've been a software developer for a long time, until recently my activities were limited to work for hire. I'd used a couple of open source libraries here and there, and usually have a selection of open source applications and utilities installed; but I'd never taken the step of contributing code to an open source project of any kind, and I certainly didn't have the guts to start one from scratch.

That all changed earlier this year when Daniel Crenna (aka Dimebrain) asked me to join the TweetSharp project as a contributor while we worked on a (closed source) Twitter client built using the library. What has followed has been an experience that has been rewarding, eye opening, and at times frustrating.

The rewarding part has been a more visible profile within my community (appearances on popular podcasts for example) and the sincere "thank-you" and and virtual high-fives received from the members of the community who appreciate the work, use it, and recognize that a lot of sweat and effort goes into keeping it up to date and functional for the people who depend on it. These people are truly appreciative, and occasionally will even contribute a patch or at very least take the time to submit a detailed bug report when they have a problem.

The frustrating part comes as a result of those people who I classify as "the takers". The takers don't overlap at all with the grateful people I mentioned above. The takers either don't understand or don't care that most open source projects don't have a corporate sponsor or benevolent millionaire funding the development so most or all of the development happens as a result of developers donating their spare time. The takers aren't anonymous downloaders who use the project quietly and don't say anything one way or the other, oh no. They actively flaunt their taker status by posting, tweeting, or emailing us stuff like:

hey, your library needs DOCUMENTATION, where is it??

or:

I tried using your library but it crashed, HELP!?

, or most commonly:

I need to do this and that with your library, can you write the code for me?

As I understand it, this is much more common in the .Net OSS realm than in other realms where OSS is more the standard than the exception. I realize that as .Net devs we are used to a flood of new (commercial) tech coming out of Redmond and from various vendors selling add-ons and libraries and that there is a certain sense of entitlement to support and documentation when money is involved so the shift to an open-source mentality of cooperation and appreciation may be hard for some people who don't take the time to think about it.

Realistically, there will probably always be takers, I know this. But for the rest of us, who (like I used to), quietly use open source stuff without contributing back, I challenge you to find a project that interests you, take a look at the code and find a way to contribute something. You don't even need prior permission, just find a bug that needs fixing, fix it, create a diff file and attach it to the bug report. Chances are it will be greatly appreciated and you'll find it rewarding, despite the takers.


Yours Truly talking TweetSharp on dnrTV

Posted on Tue 02 June 2009 in General

Daniel Crenna and I recently recorded a screencast/interview for dnrTV on the subject of TweetSharp. It was posted today and is available for viewing here.

I haven't watched it, yet. Nobody likes the sound of their own recorded voice and I'll beat myself up too much if I made any incorrect statements due to nerves or something. :) I'm sure I will watch it eventually, then the wailing and gnashing of teeth will begin.


Auto-Filter your tweets with Tweet# and LINQ

Posted on Mon 01 June 2009 in General

Every so often on twitter a topic becomes so popular as to become annoying. If you follow a lot of people this happens pretty much on a daily basis.

This also gives rise to another twitter phenomenon - people announcing that they're filtering out #trendyTopic because of the noise it generates, thus contributing further to the noise.

This gave me an idea in passing that I immediately posted to twitter. "What if my client could be set to auto-filter out trending topics." I initially thought it was a silly idea, but then as I let it roll around in my noggin a bit I decided it could actually have some promise, if implemented properly.

So...if you're developing a twitter client (admit it, you are, everyone's doing it) and you're using TweetSharp to do it, here's some code to get you started. It's easy!

First, we need the code to get the current trends.

///
/// Gets current trends as a string enumeration
///
///
public static IEnumerable GetTrendingTopics()
{
    var search = FluentTwitter.CreateRequest()
       .Search().Trends().Current()
       .AsJson();
    var response = search.Request();
    var trends = response.AsTrends();
    foreach ( var trend in trends.Trends )
    {
       yield return trend.Name;
    }
}

This code will get us today's trending topics.

Next, we'll get our friends' timeline

public static IEnumerable GetFriendsTimeline()
{
   var twitter = FluentTwitter.CreateRequest()
       .AuthenticateAs( UserName, Password )
       .Statuses().OnFriendsTimeline().AsJson();

   var response = twitter.Request();
   return response.AsStatuses();
}

Now that you have trends, and tweets, it's just a matter of writing a bit of LINQ code to filter out any tweets that have the trending string in the text.

   public static IEnumerable FilterTweets( IEnumerabletweets, IEnumerable
filterTexts )
   {
      var filteredTweets = from t in tweets
      from f in filterTexts
          where t.Text.Contains(f)
          select t;
      return filteredTweets;
   }

For the purposes of this example we'll just dump the tweets out to the console.

public static void WriteFilteredTweets( )
{
   var tweets = GetFriendsTimeline();
   var trends = GetTrendingTopics();
   var filtered = FilterTweets(tweets, trends);
   foreach ( var status in filtered )
   {
       Console.WriteLine("{0} - {1}", status.User.ScreenName, status.Text);
   }
}

This is obviously a fairly trivial example, and represents the nuclear option of tweet filtering. If I were to implement this fully I would probably want to show the user a list of trending topics and have a one-click filter option (i.e. one button or check box per trend) that automatically expired when the trending topic dropped off the list of current trends.

Other ways this could be better

  • Apply it except when you are @replied to in the tweet
  • Rather than simply hiding the tweets, move them to a less prominent area of your client
  • Adapt the code and use it to highlight trending tweets instead of hiding them

Use the iTunes API and C# to do spring cleaning on your music collection

Posted on Sat 16 May 2009 in General • Tagged with csharp, itunes

I was somewhat of an early adopter when it came to digital music. Due to the inordinate amount of time spent sitting at the keyboard, the PC was, for a very long time, my primary music player of choice. I usually have a decent set of speakers or headphones hooked up to my PC and use that to listen both while I work, or when I'm doing other stuff in the vicinity of the PC. However swapping CDs and dragging them to work and back seemed rather pointless once I could easily rip CDs to digital formats and store it on the computer and nothing beats having ones whole library of music at ones fingertips.

For the longest time, Winamp was what I used to play music...then I bought an iPod. I love my iPod because now I can take my entire music collection with me wherever I go, including the car. The iPod, however, did more or less force me to change my PC music software from WinAmp to iTunes (yes, I know there are alternatives, including WinAmp plugins, that would have saved me having to switch, but I was curious enough about iTunes that it didn't bother me too much).

The first problem I had after importing my library was that iTunes had no album art for well over half of what I had imported. However, I had the album art. I had created a 'folder.jpg' file with the album art for every album in my collection so that it would appear in the Windows Explorer thumbnail view for the associated folder. The problem is, iTunes doesn't look at that, it looks at the the file for embedded art (in the ID3 tag).

Now, I could have gone through and manually associated the images via the iTunes GUI, but dammit, I'm a programmer and that's just not how we roll!!

Faced with a repetitive task, I, like any lazy-assed programmer would, looked for an automation solution. I was pleasantly surprised to find out that the iTunes App has an API. It's a COM API, which ain't my favourite, but it'll do. So, without further ado, here's how I associated the 'folder.jpg' album art, with every one of my nearly 10,000 song files, in no time flat.

First, I created a simple Console App and added a reference to the 'iTunesLib' COM component. (If you have iTunes installed, it should appear on the "COM" tab of Visual Studio's 'References' dialog as "iTunes 1.xx Type Library"):

adding a reference|

The next thing we need is a connection to the app and a reference to the main library object. The Library is exposed as a specific type of playlist, we can use the following code for that:

private static IITPlaylist GetLibraryPlaylist()
{
    IITPlaylist libraryPlaylist = null;
    Console.WriteLine( "Connecting to iTunes App... " );
    IiTunes iTunes = new iTunesLib.iTunesAppClass();
    Console.WriteLine( "Getting library:" );
    iTunesLib.IITSource librarySource = null;
    foreach ( iTunesLib.IITSource source in iTunes.Sources )
    {
        if ( source.Kind == ITSourceKind.ITSourceKindLibrary )
        {
            librarySource = source;
            break;
        }
    }
    if ( librarySource != null )
    {
        foreach ( IITPlaylist pl in librarySource.Playlists )
        {
            if ( pl.Kind == ITPlaylistKind.ITPlaylistKindLibrary )
            {
                libraryPlaylist = pl;
                break;
            }
        }
    }
    return libraryPlaylist;
    }

Now we can iterate over the tracks in the library and call a function to assign the artwork (defined below)

IITPlaylist libraryPlaylist = GetLibraryPlaylist();
if ( libraryPlaylist != null )
{
    Console.WriteLine( "Repairing Tracks... " );
    IITTrackCollection tracks = libraryPlaylist.Tracks;
    int i = 1;
    foreach ( IITFileOrCDTrack track in tracks )
    {
        Console.CursorLeft = 0;
        Console.Write( "Checking track {0} of {1}......", i, tracks.Count );
        SetTrackArt( track );
        i++;
    }
}

...and lastly, here's the code to set the album artwork to the 'folder.jpg' file in the same folder as the track, if it exists

private static void SetTrackArt( IITFileOrCDTrack track )
{
    if ( track.Artwork.Count == 0 && !track.Podcast )
    {
        string fileLoc = System.IO.Path.GetDirectoryName( track.Location );
        string artPath = System.IO.Path.Combine( fileLoc, "folder.jpg" );
        if ( System.IO.File.Exists( artPath ) )
        {
            Console.WriteLine( "Adding art to {0}", track.Location );
            try
            {
                track.AddArtworkFromFile( artPath );
            }
            catch
            {
                Console.WriteLine( "FAILED!" );
            }
        }
    }
}

As you can probably imagine, once you're iterating over your entire iTunes Library, there's lots of stuff you can to do make cleaning up your metadata easier. Some of other stuff I've used the iTunes API for include:

  • Normalizing band names so everything matches and is correct. (e.g. "Beatles", "Beetles", "The Beatles" all become "The Beatles")
  • Finding tracks with missing album or artist information and list them in a text file to peruse and deal with later.
  • Populate the "Album Artist" to match the "Artist" field.
  • Find tracks in the library that are missing from the disk and delete them from the library

Hope you found this useful!