Grab content from a web browser in your Mac app

January 2014

One of my favorite features of Tapetrap is its ability to subscribe to a website while surfing the web with your browser. Instead of looking for the RSS feed manually and copy-pasting the link, you can click a button and Tapetrap will find and add the feed for you. In this article I want to go over why I think that’s awesome and how it works. The source code for the URL grabbing is available on GitHub.

Adding feeds from your web browser in Tapetrap

Not a browser plugin

I’ve made and use a few browser extensions. They’re neat little applications that take an insane amount of work to maintain. While some code can be shared you will need to tailer parts of the extension for each browser specifically.

For Tapetrap, I chose to make a system wide browser extension in the form of an icon in the menu bar that “just works” with the active web browser when clicked. This means users don’t have to install anything extra and I don’t have to create a plugin per browser. I can’t say I’m the first one to do something like this, but it works particularly well for Tapetrap.

Add feeds while surfing the web

Of course there are downsides to using this approach too. It’s impossible to manipulate the DOM inside the browser or execute other more context aware methods. It’s fine for getting basic information, though.

How grabbing works

This is where it gets a little technical. I combined a basic menu bar app with my own URL grabber code, called DCOURLGrabber, to get the URL from the web browser that was last active.

I’ll skip the menu bar icon part. There are plenty of good tutorials that explain all you need. Instead I’ll talk about how to interact with the web browser.

AppleScript

Yep, AppleScript. Often used to automate tedious tasks, it’s also a great way to interface with other applications. For DCOURLGrabber I focussed on getting the URL from the selected tab of the key window of the web browser that was last active. This is the AppleScript for getting the URL in Google Chrome:

tell application "Google Chrome"
  get URL of active tab of first window
end tell

Simple enough right? For Safari and Opera the command are very similar. The one for Firefox is a little longer.

tell application "Firefox" to activate
tell application "System Events"
  keystroke "l" using command down
  keystroke "c" using command down
end tell
delay 0.5
the clipboard

Hopefully they’ll switch to a straightforward approach in a future update. In any case, after running these scripts they present the current URL of the browser window.

Objective-C

To run this in Objective-C, create a new Mac project in XCode, paste in the next piece of code in the applicationDidFinishLaunching: method and that’s it.

// The script to run. You could also load this from a file as in DCOURLGrabber
NSString *chromeScript =
@"tell application \"Google Chrome\"\n"
"  get URL of active tab of first window\n"
"end tell";

// Load the script
NSAppleScript *script = [[NSAppleScript alloc] initWithSource:chromeScript];
    
// Grab URL using AppleScript
NSDictionary *scriptExecuteError;
NSAppleEventDescriptor *result = [script executeAndReturnError:&scriptExecuteError];
if(scriptExecuteError) {

  // Failed
  NSLog(@"Error: %@", scriptExecuteError);

} else {

  NSLog(@"Output: %@", result.stringValue);

}

Using DCOURLGrabber it becomes even easier. Check the GitHub page for more documentation.

DCOURLGrabber *grabber = [[DCOURLGrabber alloc] init];
NSURL *url = [grabber grabURLFromBundleID:@"com.google.Chrome" withError:&grabError];
if(grabError) {
    NSLog(@"Failed to retrieve URL: %@", grabError);
} else {
    NSLog(@"Got URL: %@", url.absoluteString);
}

Getting the RSS/Atom URL

Websites that value their feeds will link to it from their website. Not only with the well-known orange button, but also through a meta tag in the source code of the website. This gives apps like Tapetrap a way of retrieving it.

The GameKings website has this setup correctly. Inspecting the source of http://gamekings.tv reveals the following lines near the top of the document.

<link rel="alternate" type="application/rss+xml" title="RSS 2.0" href="https://www.gamekings.tv/feed/" />
<link rel="alternate" type="text/xml" title="RSS .92" href="https://www.gamekings.tv/feed/rss/" />
<link rel="alternate" type="application/atom+xml" title="Atom 1.0" href="https://www.gamekings.tv/feed/atom/" />
<link rel="alternate" type="application/rss+xml" title="Gamekings Video's RSS Feed" href="https://www.gamekings.tv/rss?cat=3">
<link rel="alternate" type="application/rss+xml" title="Gamekings Nieuws RSS Feed" href="https://www.gamekings.tv/rss?cat=18">

All links point to RSS feeds. There are some similarities between the lines. Using these conventions, it’s easy to extract them in code. Even easier with OCGumbo, which is a HTML5 parser. It converts RSS into Objective-C objects.

The following lines of code parse a simple HTML page, check the ‘type’ parameter of the ‘link’ meta tag for either application/rss+xml, application/atom+xml, rss+xml or atom+xml and log the link when it matches.

// Create an array that contains the strings that can appear in the 'type' property
NSArray *linkTypeFeedIndicators = [NSArray arrayWithObjects:@"application/rss+xml", @"application/atom+xml", @"rss+xml", @"atom+xml", nil];

NSString *htmlString =
@"<html><head>"
"<link rel='alternate' type='application/rss+xml' title='RSS 2.0' href='http://www.gamekings.tv/feed/' />"
"</head><body>"
"<h1>Feed discovery experiment</h1>"
"<p>Just testing ;).</p>"
"</body></html>";

// Load the document from a string containing HTML
OCGumboDocument *document = [[OCGumboDocument alloc] initWithHTMLString:htmlString];
OCGumboElement *root = document.rootElement;

// Loop through all 'link' tags
[root.Query(@"link") enumerateObjectsUsingBlock:^(OCGumboElement *element, NSUInteger idx, BOOL *stop) {
    NSString *type = [element.attr(@"type") stringByRemovingNewLinesAndWhitespace];
    
    // Check if the type is equal to that of a RSS/Atom feed
    if([linkTypeFeedIndicators containsObject:type]) {
        NSString *feedURLString = element.attr(@"href");
        NSLog(@"Found a feed: %@", feedURLString);
    }
}];

With the URL of the RSS feed in hand, the road is clear to analyze its content using a RSS parser, or maybe use the URL for something different entirely.

Real world example

You can download Tapetrap to get a feel of how this works. The app is now in public beta and free to use. Also let me know what you think and help improve it!

I love watching web videos

January 2014

Tapetrap header

Online videos are my favorite work break. I might watch an episode of Star Trek TNG during lunch, but I watch far more online videos while I wait for some stuff to compile or just to take my mind off what I’m doing.

Tapetrap makes sure I spend less time searching and more time watching these videos. I use it daily and love how it works. Now I need your help to make it even better.

The videos I like to watch aren’t available from a single website. YouTube has a lot, but definitly not everything. Maybe even less in the future, with the copyright claims a lot of video game oriented channels had to deal with lately. I wanted to get away from browsing to every single website independently, deciding what I wanted to watch and then forgetting which clips I had already seen. Or making an account for every site to overcome that last problem.

Browsing in Tapetrap

Tapetrap will hold all my favorite sites, make it easy to add new ones, play videos and remembers which I’ve already seen. When I have 5-30 minutes to spare, it gives me a quick overview of what’s available and ten seconds later I’m watching a video. I don’t even have to check where I left off if it’s a series. It’s pretty awesome really.

Especially while Tapetrap’s in beta, I’m looking for ways to improve the experience and add features. If you’re like me and follow a bunch of skateboarding channels on YouTube, TED Talks, Funny or Die, Artsy Vimeo Channels, Spoony etc. etc., download Tapetrap and let me know what you think.

AirVLC v.1.1.1, still better with VLC 2.0.x

November 2013

It feels like an update for AirVLC has been overdue. Even though it’s been working very well for me and even made the transition to Mavericks like it was nothing.

The changes in this update are mostly behind the scenes; I updated the frameworks and project to work with Xcode 5. Besides that I’ve made some tweaks to accomodate a few changes Videolan made in VLC 2.1.0.

Unfortunately there is no way to test if those changes work yet, because the AirPlay support in 2.1.x still seems to be completely broken. Even in the nightly builds. So if you intend to use AirVLC to watch your movies on your Mac and play the audio in sync over AirPlay, stick to VLC 2.0.9 or downgrade if you’ve updated already.

Glow for updates

October 2013

Everybody knows software updates. A window pops up to notify you an update is available, you accept and a few seconds later you have new features (and less bugs, hopefully).

Automatic software updates are great and generally make everybody’s lives better. As a developer I can be sure most of my customers are using the latest version. As a user you press a single button to update, instead of going to the website, downloading a new package and overwriting the previous version.

All Danger Cove apps (except the ones on the App Store) use Sparkle for automatic updates. Sparkle checks a feed every day and lets you know when a new version is available. Getting that feed online is what I want to talk about today.

Previously I manually edited these feeds and manually wrote the release notes in a text editor. Knowing that I have to look up version numbers, file sizes and sign the update (for security, nobody should be able to release an update in my name). This can take some time.

That’s why I’ve made Glow and if you’re a software developer, it might come in handy for you as well. That’s why I’ve released it on GitHub. Here’s my description of it.

Glow is a tool that generates Sparkle-compatible Appcast update feeds and html release notes for your Mac projects. It’s not a full blown, database driven, multiple project supporting, update releasing laser canon. Rather it’s a clean, easy to use script that will automate 90% of releasing updates to your users.

The GitHub page contains a very detailed walkthrough on how to get started.

Automating the process of creating these updates leaves more time to work on fixing bugs and adding cool new features. Also, I might have some more time to style those release notes :).

Mavericks

October 2013

Apple released OS X version 10.9 yesterday. It’s a free update and I think most of you will want to upgrade as soon as you get the chance.

All Danger Cove apps have been tested and work with OS X Mavericks. So, don’t let Porthole, AirVLC, coucou or Reign hold you back and have fun checking out all new features Apple’s added!