Developer Blog

The 10 Essentials of Making a Great Boxee App

Jeremy Toeman over at Stage Two just posted a great piece on the 10 essentials of making a great Boxee app. He opens up the list with a personal fave bit of advice for first time Boxee devs:

10 – Avoid Input Fields At All Costs

The Boxee Box comes with a clever remote that includes a full QWERTY keyboard on its reverse. That is awesome. Scratch that, it’s super-awesome. Hopefully you only use it when you should – specifically when you are searching for something, logging into something, or some other highly meaningful purpose. Content for the interactive television is not the same as content for a computer or mobile device. Smart couch surfing should feel like analogue couch surfing- not in the content delivered, but in the manner in which it is accessed. If your App asks the user to “type” every 2 minutes, you might as well be thinking of a computer experience, not a TV one.

Check out the full post over at Stage Two.

September 17, 2010 at 4:55 pm

Hak5 Kickstarts Your Boxee Development

We’re huge fans of Hak5 over at Boxee Engineering and we were all dead excited to see Boxee show up in their segments over the past few weeks. In particular, we loved Jason’s intro to developing on Boxee which has been the best video demonstration of how to develop on Boxee we’ve seen so far.

You can watch the intro below or - better yet - in the Revision3 app on Boxee.

Thanks Jason and Hak5! Keep hacking!

September 7, 2010 at 2:21 pm

Building Dynamic Menus with the Python API

I got a great question this week from a developer looking to make a dynamic menu for his Boxee app. Rather than create his menu with a Grouplist that would require a new release of his application if he wanted to add an item to his menu, he was looking to construct his menu on the fly with Python. Not only was it possible on Boxee, but with the ListItems object it was super easy.

First, let’s define the three pieces that can make a dynamic menu possible.

1) List Container

The List Container is the UI control that is going to serve our dynamic menu. Unlike a Grouplist where we would define each of our menu options with an individual button, we define the dimensions, how the the unfocused (itemlayout) and focused (focusedlayout) states should appear, and then feed it a ListItems object to construct our menu dynamically. We’ll use the following as a quick example:

<control type="list" id="100">
	<width>600</width>
	<height>100</height>
	<orientation>horizontal</orientation>
	<itemlayout width="100" height="100">
		<control type="label">
			<width>100</width>
			<height>60</height>
			<font>sans18</font>
			<align>center</align>
			<aligny>center</aligny>
			<label>$INFO[ListItem.Label]</label>
			<textcolor>white</textcolor>
		</control>
	</itemlayout>
	<focusedlayout width="100" height="100">
		<control type="image">
			<width>100</width>
			<height>100</height>
			<texture>background-showfocused.png</texture>
		</control>
		<control type="label">
			<width>100</width>
			<height>60</height>
			<font>sans18</font>
			<align>center</align>
			<aligny>center</aligny>
			<label>$INFO[ListItem.Label]</label>
			<textcolor>white</textcolor>
		</control>
	</focusedlayout>
	<content type="action">
	</content>
</control>

2) ListItems

With the Boxee python API, ListItems are the data object that we use to populate List Containers. They are constructed simply.

itemList = mc.ListItems()

3) ListItem

While ListItems is a data object, we still need individual datum to fill it with. This is where the ListItem object comes in. Each of our individual menu items is defined in a ListItem, appended to a ListItems object, and then fed to a List Container. While the ListItem is a very diverse data object with a number of different uses on the Boxee platform, we can use the simple unknown type to make our menu items.

Now that we’ve defined the parts of the API we need, let’s roll them together for a functioning menu. For the purposes of this post, we’ll assume you have already requested your menu items from some server-side resource and put them into a dictionary. By way of example, we’ll assume the key is the label of the menu item and the value is path we want to use. We’ll also assume in this case that our List Container is assigned ID 100.

First, we need to create our ListItems object.

dynamicMenu = mc.ListItems()

Then we create a ListItem for each entry in our dictionary and append it to the ListItems object.

for key, value in menuDictionary.iteritems():
	item = mc.ListItem(mc.ListItem.MEDIA_UNKNOWN)
	item.SetLabel(str(key))
	item.SetPath(str(value))
	dynamicMenu.append(item)

Finally, we populate our List Container with our ListItems object.

mc.GetActiveWindow().GetList(100).SetItems(dynamicMenu)

And viola! We have a dynamically created menu for our app.

If you’d like to get more tips and tricks and are in the New York area this evening, be sure to drop by this month’s NYC Python meetup where I’ll be introducing Boxee, showing off the Python API and answering your questions.

Also, we’d like to give a big shoutout to our friends at Atlassian who just hooked us up with licenses for their agile development product GreenHopper. If you are a Scrum or Agile shop and looking for a more intelligent way of managing your stories that integrates with tools you are already using like JIRA, the Boxee crew can wholeheartedly endorse GreenHopper for your next sprint.

July 20, 2010 at 11:19 am

Catch Rob at NYC Python Talk About Boxee’s Python API

Next week 20 July Rob is going to be joining the crew at New York Python Meetup Group to give a quick overview of Boxee’s Python API . This deck is all code, no choad focused for Python developers looking to get kickstarted on the Boxee platform.

RSVP is required and space is filling fast - if you’re in New York and want to talk Python with Boxee, be sure to check out NYC Python’s registration page and get your name in with a quickness.

Tags: , ,
July 14, 2010 at 10:27 am

Storing User Settings with LocalConfig

A common question from coders new to the Boxee platform is the storage of configuration settings for their users. From display preferences for the user interface to ID/password combos for subscription systems, many of the apps on Boxee need the ability to store these user preferences.

Many developers ask how to easily write files to Boxee’s temporary directory, taking it upon themselves to develop their own file format to store their settings in. However, if your preferences can be stored as key/value pairs, Boxee has a built in class that makes storing settings far easier called LocalConfig.

LocalConfig is the object all Boxee apps can use to store key/value pairs that will persist from user session to user session on the same Boxee install. While these settings will have to be entered (for now at least) in every Boxee install the user logs into, it is an easy way to keep your users from having to input the same data for every session.

Let’s take a look at the LocalConfig object in action with the example storing a username and password combination. The first thing we want to do is store a username and password with our app. We can do this easily first by creating a login button in our main.xml.

<control type="button" id="100">
	<description>Login</description>
	<width>114</width>
	<height>37</height>
	<texturefocus>button-loginfocused.png</texturefocus>
	<texturenofocus>button-login.png</texturenofocus>	
</control>

Once the user clicks “Login,” we want to present the user to input their username and password with Boxee’s built-in keyboard interface. We can accomplish this by adding two ShowDialogKeyboard requests to an onclick event for the login button.

<control type="button" id="100">
	<description>Login</description>
	<width>114</width>
	<height>37</height>
	<texturefocus>button-loginfocused.png</texturefocus>
	<texturenofocus>button-login.png</texturenofocus>	
	<onclick lang="python"><![CDATA[
emailaddress = mc.ShowDialogKeyboard("Email", "", False)
userpassword = mc.ShowDialogKeyboard("Password", "", True)
]]></onclick>
</control>

Next we’ll check to make sure the user entered their username and password and, if so, store them in our LocalConfig. We add this code to the login event.

<control type="button" id="100">
	<description>Login</description>
	<width>114</width>
	<height>37</height>
	<texturefocus>button-loginfocused.png</texturefocus>
	<texturenofocus>button-login.png</texturenofocus>	
	<onclick lang="python"><![CDATA[
emailaddress = mc.ShowDialogKeyboard("Email", "", False)
userpassword = mc.ShowDialogKeyboard("Password", "", True)
if emailaddress and userpassword:
	config = mc.GetApp().GetLocalConfig()
	config.SetValue("username", emailaddress)
	config.SetValue("password", userpassword)
	mc.ShowDialogNotification("Credentials stored.")
       # Insert login code here
else:
	mc.ShowDialogNotification("Please enter username and password.")
]]></onclick>
</control>

Now that we have stored the username and password, we want to make sure the app knows this and logs the user every time the application is launched without asking the user for his/her credentials again. With the LocalConfig object, we can check if values exist in an onload event for the window and then take appropriate action if it does.

config = mc.GetApp().GetLocalConfig()
emailaddress = config.GetValue("username")
userpassword = config.GetValue("password")
 
if emailaddress:
	mc.ShowDialogNotification("Logging in...")
        # Insert login code here

Lastly, we would be remiss in our duties as courteous developers if we didn’t also give our users the ability to logout. With the paradigm we’ve set up here using LocalConfig, all we have to do is clear the LocalConfig for the app and the user will not no longer login on app start.

<control type="button" id="101">
	<description>Logout</description>
	<posx>200</posx>
	<posy>0</posy>
	<width>114</width>
	<height>37</height>
	<texturefocus>button-logoutfocused.png</texturefocus>
	<texturenofocus>button-logout.png</texturenofocus>
	<onclick lang="python"><![CDATA[
config = mc.GetApp().GetLocalConfig()
config.ResetAll()	
mc.ShowDialogNotification("Logging out...")
# Insert logout code here
]]></onclick>
</control>

Here’s the complete main.xml as an example. Enjoy!

<?xml version="1.0"?>
<window type="window" id="14000">
<onload lang="python"><![CDATA[
config = mc.GetApp().GetLocalConfig()
emailaddress = config.GetValue("username")
userpassword = config.GetValue("password")
 
if emailaddress:
	mc.ShowDialogNotification("Logging in...")
	# Insert login code here
]]></onload>
	<defaultcontrol always="true">111</defaultcontrol>
	<allowoverlay>no</allowoverlay>
	<controls>
		<control type="group">
			<control type="button" id="100">
				<description>Login</description>
				<width>114</width>
				<height>37</height>
				<texturefocus>button-loginfocused.png</texturefocus>
				<texturenofocus>button-login.png</texturenofocus>	
				<onclick lang="python"><![CDATA[
emailaddress = mc.ShowDialogKeyboard("Email", "", False)
userpassword = mc.ShowDialogKeyboard("Password", "", True)
if emailaddress and userpassword:
	config = mc.GetApp().GetLocalConfig()
	config.SetValue("username", emailaddress)
	config.SetValue("password", userpassword)
	mc.ShowDialogNotification("Credentials stored.")
else:
	mc.ShowDialogNotification("Please enter username and password.")
]]></onclick>
			</control>
			<control type="button" id="101">
				<description>Logout</description>
				<posx>200</posx>
				<posy>0</posy>
				<width>114</width>
				<height>37</height>
				<texturefocus>button-logoutfocused.png</texturefocus>
				<texturenofocus>button-logout.png</texturenofocus>
				<onclick lang="python"><![CDATA[
config = mc.GetApp().GetLocalConfig()
config.ResetAll()	
mc.ShowDialogNotification("Logging out...")
]]></onclick>
			</control>
		</control>
	</controls>
</window>
July 2, 2010 at 4:44 pm

Rob Talking at New York Linux Users Group

Penguin enthusiasts in the Big Apple be warned, I will be giving a talk at this month’s meeting of the New York Linux Users Group. The presentation will be a quick intro on Boxee, how open source is woven into the fabric of our product and our culture, and (always my favorite topic) how developers can get involved making apps for Boxee. Held at NYLUG’s hosts IBM at 57th and Madison, the talk will kick off at 6:30pm followed by a friendly drink up nearby.

I’ll have an arm load of T-shirts in tow, plenty time for Q&A and a wide array of terrible jokes for your consuming pleasure.

You must RSVP here first for the talk.

More info on the talk and location are available at NYLUG.

When: Wednesday, 16 June 2010, 6:30pm
Where: IBM, 590 Madison Avenue, 12th Fl, New York, NY


View Larger Map

June 9, 2010 at 10:15 am

Why Your App Belongs In The Boxee Library

Here at Boxee we pride ourselves in being the open alternative to other television partners and this philosophy extends to our development community. Developing apps on Boxee is and always will be free for anyone who wants to bring their content to the platform. Distributing these apps occurs through two mechanisms - the Boxee App Library and third-party repositories. Let’s take a look at both of these two options and why one is likely preferable to the other for your Boxee app.

What is the Boxee App Library?

Also called the “App Box” and the “main repo” on the forums, the Boxee App Library is the default app repository pre-configured on every Boxee install. Every app in in the Library is vetted and approved by Boxee’s Quality Assurance team, ensuring every user can be confident that the experience he/she will find in the App Library will be great.

What are third-party repositories?

In addition to submitting to our App Library, developers have the option of adding their own repositories using our open specification. Just as any one can build an app on Boxee, anyone can build a library for Boxee apps. In keeping with our open source culture, this gives our community an option available on few other development platforms (and fewer still for the TV): the freedom to control the distribution of their work.

Why should my app be submitted to the App Library?

With the ability to distribute your software yourself, some developers wonder what the benefits are to submitting their app to the App Library. There are three big wins that the App Library brings to every app:

1) Visibility

A very strong majority of Boxee users never install a third-party repository. This creates a gate between Boxee’s 1.1 million user audience and your app. By submitting to the App Library, you ensure that every single user of Boxee can access your app without having to follow any instructions or encounter any obstructions. All the most popular apps on Boxee are distributed through the App Library.

2) Quality Assurance

Like many other app ecosystems, each app that is published in the App Library gets screened by Boxee’s crack Quality Assurance group. Coding always means bugs and even the best among us let some get through the cracks. There is no one in the Boxee community as adept at finding bugs as our QA group and their assistance will always ensure a better end product.

3) Maintenance Tools

Every developer that submits their app to the App Library also gets access to our tools to maintain apps, most notably JIRA. As the central bug tracker for all things Boxee, this means that users can submit quality bugs for your app, giving you instant feedback on the app’s function and quality. In addition, you can integrate your IDE with Boxee’s JIRA install quickly and easily with tools like Mylyn, giving you even easier control over the maintenance of your app.

In short, the Boxee App Library gives your app the distribution, the tools and the tender-lovin’-care it needs to thrive in our wide development ecosystem. In addition, it is super simple!

How To Submit:

Submitting to our App Library is easy and transparent. All you have to do is follow the Application Submission process found on our developer wiki.

Your app with then get into our queue and you will start receiving communication from our QA group within 1 business day.

We’ve invested significant effort in our submission process as of late. While we feel we still have a way to go before we are satisfied, we strongly believe that it is more open and transparent than many other submission processes developers have to endure when making the software they love.

As always, I welcome your feedback - find me on FreeNode in #boxee or on Twitter @boxee_api.

June 4, 2010 at 12:31 pm

Creating Rounded Rectangles with Diffuse

A common design element in Boxee apps is that staple of Web 2.0 called the rounded rectangle. Useful for a number of different interface elements, I frequently get asked how to round the corners of dynamic image controls like thumbnails.

Fortunately, this is easily accomplished with one of the most useful GUI techniques available to Boxee developers: the diffuse attribute. Available on image controls, diffuse allows developers to apply a mask to their image controls that renders with the image in the app. The concept may seem confusing initially, but is very simple in practice.

Let’s take for example on of my first Boxee apps, Associated Press. On the left side of the interface the thumbnail image for the currently selected listitem is displayed. Pictured here, the edges seem a little sharp and harsh to the eye - a perfect candidate to smooth out with a rounded rectangle.

To create the rounded rectangle, first we need to create a mask image. In the image editor of our choice (the GIMP for me), we create an image the same size as the image control we are looking to mask, 480×360. Then we create a white (FFFFFF) rounded rectangle inside the image and save as a transparent png.

Next we need to apply this mask to the image control to get the rounded look we are after. Consider the XML for the image control without the mask.

<control type="image">
	<posx>24</posx>
	<posy>177</posy>
	<width>480</width>
	<height>360</height>
	<texture>$INFO[Container(111).ListItem.Thumb]</texture>
</control>

Adding the mask is super-simple - just add the diffuse attribute to the texture element.

<control type="image">
	<posx>24</posx>
	<posy>177</posy>
	<width>480</width>
	<height>360</height>
	<texture diffuse="mask.png">$INFO[Container(111).ListItem.Thumb]</texture>
</control>

And viola - the image is diffused with our mask, rendering the part of the image we want (the white rectangle) and cropping the part we don’t (the transparency) producing a much smoother look.

The best part about using the diffuse attribute is that it scales with your image controls on multiple resolutions, making your image control render accurately on any aspect ratio. For more info on how to use this element, check out the developer docs for the image control.

May 24, 2010 at 3:18 pm

Grant Goodale Wins Music Hack Day Prize, JuxBux Team Pwns

This past weekend Idan and I represented Boxee at Music Hack Day in San Francisco, a rad hackathon event focused on music technologists of every stripe. We met some very enthusiastic Boxee users, met some top notch engineering talent, and saw some singular hacks over the course of the weekend.

Winning our top prize for bringing the electronic music community BeatPort to Boxee with a soon-to-be-released Boxee app was Grant Goodale (@ggodale)! Called BeatBox, the app allows users to flip through the top 100 most popular and recently released tracks as well as browse by genre through BeatPort’s huge catalog of electro tunes. Grant put together the killer hack in 18 total hours of development time with no previous Python experience. Grant had this to say about the experience:

I’m impressed at how easy it is to build great apps for Boxee.

In addition to meeting Grant, we got to get some quality hack time in with a pair of developers to work on our own hack called JuxBux. Joined by Noah Zoschke (@nzoschke) from Heroku and Antoine Margurie (@antoinem) from Fairtilizer, we produced a social music jukebox game for Boxee.

The scenario stems from a frustration common to many New York startups: space. With the limited office space in Manhattan, most startups can only reasonably have one set of speakers going at one time, leading to a natural conflict of musical tastes. JuxBux turns that conflict into a social game by creating a jukebox controlled by the crowdsourced selection of those present in the office. Users can submit tracks to the queue, vote on the current playing track, and expend the points they earn on instant plays and vetoes. We’re planning on continuing development on this hack and doing a public release soon.

Many thanks to Noah and Antoine for lending their extensive talent to the project - it was great working with you!

Got a developer event you think Boxee should represent at? Talk to Idan at idan [at] boxee [dot] tv!

Your Friend JIRA - A Quick Guide To Bug Squashing With Boxee

Like most developers, my day begins and ends with opening my bug tracking software. It is the secretary for the coding set; the clutch organizational tool all hackers use to stay organized and keep on top of their apps.

Boxee joins many other open source projects by relying on JIRA as our bug tracking software of choice. A best-of-breed dbase for keeping track of your app’s issues and features, our JIRA implementation is available to you the Boxee developer free of charge as you release your apps on the platform.

As you get started using your JIRA account on Boxee, you may wonder how the developer community defines many of the fields in your bugs. What does “resolved” really mean? How is that different from “Closed?”

Well worry no longer with this helpful guide to using our JIRA dbase to make your app better.

Status

  • Open: The issue is open and ready for the assignee to start work on it.
    When an issue is opened in Application project. it is automatically assigned to the Component owner.
  • In Progress: This issue is being actively worked on at the moment by the assignee.
  • Reopened: This issue was once resolved, but the resolution was deemed incorrect. From here issues are either marked assigned or resolved.
  • Resolved: A resolution has been taken, and it is awaiting verification by reporter. From here issues are either reopened, or closed.
  • Closed: The issue is considered finished, the resolution is correct. Issues which are closed can be reopened.

Resolution

  • Fixed (Default): A fix for this issue is checked into the source control and tested.
  • Won’t Fix: The problem described is an issue which will never be fixed. Or This behavior is by design.
  • Duplicate: The problem is a duplicate of an existing issue.
  • Incomplete: The problem is not completely described.
  • Cannot Reproduce: All attempts at reproducing this issue failed, or not enough information was available to reproduce the issue. Reading the code produces no clues as to why this behavior would occur. If more information appears later, please reopen the issue.
May 13, 2010 at 8:05 pm