Posted by Chuck Vose
Thu, 15 Apr 2010 16:30:00 GMT
Cross-posted on the metaltoad blog
Okay, as mundane as it sounds there are a lot of articles around the internet that are fundamentally misunderstanding the simplicity of this request: how do I reconnect my bluetooth device after it’s been disconnected from being in my laptop bag or whatever.
There is no reconnect option in Windows 7 and the reconnect option in OS X is fairly buried. As long as your device is in the bluetooth devices menu the following should work for you:
Mash buttons.
I know how stupid that sounds, but it’s been working for me for a couple days now and I’m staggered that I didn’t think of it earlier. I always wiggled the mouse or hit the alt key on the keyboard like I do to wake my mac from sleep, but it really needs a good ole mashing.
Posted in Life | Tags 7, bluetooth, keyboard, magic, mighty, mouse, os, vista, windows, x | 4 comments
Posted by Chuck Vose
Fri, 02 Apr 2010 22:04:00 GMT
During my last mini flash project I created a project designed to help me with async calls in AS3. I hope that it helps you too. You can find the project on my github account.
Intro
Required Sequence is a tiny AS3 library that mandates that certain bits of code run before other bits of code. I’ve heard that Senocular has an excellent version of this but I could never quite grok his library. There’s also the apparently magical BulkLoader which you can find here. Before I found those two libraries I had already written my own and it works quite well for me.
Background
Required Sequence is essentially an implementation of the Publish/Subscribe design pattern in AS3. It relies on the principle that when an important configuration process finishes it will dispatch an Event on the main class. Other processes that are interested in this merely tell their Required Sequence instance to listen for that Event and execute some code when it happens.
For example, if you need to load an XML config file before doing anything else you could easily write something like this:
rs.requireFlag(MyClass.CONFIG_LOADED,
function () {
// Do the rest of my program
}
);
Instructions
Build your RS listener as follows. This would be good to do for each class or each section of code but it must not die during execution until all bits of code are run.
Here is a complete running example of a program that requires the loadConfig function to be run before doing anything else.
package
{
import com.chuckvose.utils.RequiredSequence;
public class MyClass
{
public static const CONFIG_LOADED = 'configLoaded';
public var rs:RequiredSequence;
public function MyClass()
{
rs = new RequiredSequence();
// Ensure that we see the CONFIG_LOADED Event before doing anything else
rs.requireFlag(MyClass.CONFIG_LOADED,
function () {
// Do the rest of my program
}
);
}
public function loadConfig()
{
// Do some stuff
dispatchEvent(new Event(MyClass.CONFIG_LOADED));
}
}
}
Okay, that’s pretty cool, but does it do anything else? I’m glad you asked actually. The library does three other things right now that are really, really damned important; in fact, I would hazard that most people will only use these later functions but since they all descend from that somewhat simple use above it seemed like a good starting point.
Stubbornness
If you’ve ever dealt with URLLoaders you’ll know that sometimes they can be flakey. Wouldn’t it be cool if you could just write a bit of code to try a function n times once every m seconds until you get some signal?
rs.requireFlagWithRetry(MyClass.CONFIG_LOADED,
// Code that would be necessary to generate the interesting Event. You do
// not need to invoke this manually, it will run this code immediately.
function () {
loadConfig();
},
// Code to run after we see the interesting Event.
function () {
// Do the rest of my program
}
);
Or if you want to more tightly control how many times and the spacing between attempts you could do this:
var retries = 5; // Max number of tries
var period = 2000; // Milliseconds between tries
rs.requireFlagWithRetry(MyClass.CONFIG_LOADED,
// Code that would be necessary to generate the interesting Event. You do
// not need to invoke this manually, it will run this code immediately.
function () {
loadConfig();
},
// Code to run after we see the interesting Event.
function () {
// Do the rest of my program
},
retries,
period
);
Repetition
Naturally, being the subscriber/publisher pattern there will come a time when you want to listen repeatedly to something. For example, after I render something onto the canvas I usually want to refresh my scroll panes and hide any spinners that I’ve drawn, the following easily accommodates this desire.
rs.requireFlagRepeatedly(MyClass.PANE_LOADED, function () {
refreshCanvas();
hideSpinner();
});
Groups
Okay hot shit, what about if I just want to listen for a bunch of signals and I don’t really care about the order that they come in?
rs.requireFlags([MyClass.CONFIG_LOADED, MyClass.LOCATION_LOADED],
function () {
drawWeatherDisplay();
}
);
Manually
Okay, so maybe your desires don’t fall into one of the nice cases above, here’s what needs to happen in order for events to be registered. The heart is in the following 4 functions:
- isComplete(flagName): checks to see if a flag has completed. Run this before you start listening just in case the Event has already fired for a previous listener.
- addFlag(flagName): adds a flag to the list of flags to listen for. When we see an Event fire with this string in it we’ll check with all subscribers to see if that flag was interesting to them.
- completeFlag/uncompleteFlag(flagName): after you run your code you probably want to completeFlag so that RS knows to not bother you anymore and delete your listener. Similarly, a standard part of the require* functions is to run completeFlag so you may want to uncompleteFlag if you want to mark an Event as not having run yet.
- stopWatching/stopWatchingGroup: counterpart to addFlag these both tell RS to just stop listening for that flag or flags.
You can also look at the source, I hope that it’s easy enough to read.
Contact
If you have ideas for improvement you can always email me at vosechu+rs@gmail.com. Either that or just send me a pull request and I’ll probably rip it in lickety-split.
Thanks
Thank you to everyone that has ever sent me a message of encouragement. I really appreciate it; these are the fuel that Open Source developers thrive on.
Secondly, thank you to Metal Toad Media for letting me play with fun projects and paying me to develop outlandish little libraries like this.
Posted in Actionscript, Git | Tags as3, cs4, flash, github, requirements | 3 comments
Posted by Chuck Vose
Tue, 02 Mar 2010 22:50:00 GMT
Cross posted on the Metal Toad Blog
There’s a lot of good to be said about the Brightcove iPhone SDK, it’s easy, it seems to work really well, but there’s also a ton of gotchas and the docs are a little conflicting and/or out of date. The OnePlanet app is also out of date. This post is my attempt to rectify some of these things.
There are three things needed to get your app going:
You must be using your Read with URL Acccess token to fetch videos. Old accounts don’t have this so you’ll have to contact brightcove support to get them. Access this page from Home -> Account Settings -> API Management:

Next thing you need is to make sure that your UDS is enabled. You can find this page at Home -> Account Settings -> Publishing Settings:

Despite the documentation saying you need your videos to say Progressive Download it doesn’t actually matter. All you need to do is adjust your call to be like this from this excellent doc:
bc = [[BCMediaAPI alloc] initWithReadToken:@"your read with URL Access api"];
// This makes UDS happen. Without it there is no happiness in the world.
[bc setUdsSupportOn:YES];
videoList = [ [ NSMutableArray alloc ] init ];
NSError *err;
BCPlaylist *bcp = [ bc findPlaylistById:1234512345L error:&err ];
if (bcp != nil) {
for (int i = 0; i < [ bcp.videos count ]; i++) {
[ videoList addObject: [ bcp.videos objectAtIndex: i ] ];
}
}
Other notes:
- When using the OnePlanet app beware that the Headers/Libs have been set on the target as well as on the project so you need to amend their instructions to remove the Header Search Path and Library Search Path from the target.
- Also with the OnePlanet app the BCMoviePlayer.bundle needs to be reimported from your SDK or it’s not going to be happy.
- The README states that you can call the BCMediaAPI calls without the additional frameworks MediaPlayer, OpenGLES, and QuartzCore but you actually need all three or no project you build is ever going to compile.
- It’s already been stated in the docs but Geo restricted videos just don’t work yet. I’m not sure about the status of Localization yet.
- Use the error catching logic from the best practices page, it helps a lot and it’s actually pretty mandatory if you don’t want blank players and app crashes.
Posted in Programming, Brightcove, Objective-C, iPhone Development | Tags BCPlayer, brightcove, iPhone | 4 comments
Posted by Chuck Vose
Tue, 16 Feb 2010 22:53:00 GMT
Cross posted on the Metal Toad Blog
Imagination time: Imagine for a moment that you have an app, it has one button and one little window. When you click the button it changes the text within the window to something else. Now, when you get into flash development it seems like the easiest and clearest way to do this is to wipe out the contents in the window, but you would be wrong for thinking so. While it is the most obvious I intend to prove to you that to do so is both slower and prevents you from turning on the juice later with caching.
On to story time: We’re building an app for the OpenPeak tabletop device, it’s totally rad and you’ll love it I promise but that’s really not the point of the post. When we started the app we had what I considered a pretty awesome solution, we’d clear the state, build it onto a global, then write the global out to a window on the app. It actually worked great for a long time until we decided to start doing some caching where it became wildly apparent that I had actually written all three parts of that app dead wrong. It’s pretty exciting to do something wrong enough that you can write about it later. :)
Building on Globals (Horrible)
Okay, lets start with the most awful part of the lesson for me, that using globals is horrible and should never be done. Oh sure, they make certain listeners easier but it really does blow up fast when you start trying to cache things or thread. Here’s two scenarios that will inevitably bite you if you start using globals:
- When you start threading you’ll end up painting two scenes on to the global stage object at once.
- Even if you try to be synchronous you have to descend into an event listener hell since AS3 often decides to not dispatch Event.COMPLETE which means you’ll end up with a deadlocked app while a URLLoader tries in vane to do nothing.
- At some point you’ll accidentally use the same URLLoader twice or some other critical variable and the results will be completely random seeming as one gets overwritten on occasion.
So, how can you deal with this? As annoying as it is, define your variables at the beginning of your functions. I know it seems like a waste, but unlike that one global TextFormat object your URLLoaders really are totally different beasts and need to be treated as such. Same goes for almost all objects and display containers. I know it seems nice and elegant to use the same object all the time but what you may not be seeing is that all those globals need to have IPC methods defined on them or they’re completely useless in the inherently async world that is AS3.
Clearing the State (Wrong)
This one is simple really, I should have thought of it to begin with it but I wasn’t in OO space yet. Rather than thinking about pages or DisplayObjects as something physical that needs to be removed from the stage it’s much, much more efficient to just build a new Sprite() and replace the old stage that you were using. Best part is that when you just replace the display container you’re using you can easily stash this away in a cache object somewhere. Simple caching is only moments away but if you wipe the slate clean not only did you spend time killing all those children objects, you lost the ability to cache it away somewhere.
Metaphorical you of course, my apologies.
So what we did was to use an Object (we should have used a Dictionary but I didn’t know any better yet) with attributes that described the path we were writing to. We never wrote to a global stage object (we actually deleted those so we couldn’t). This way async calls could continue to write to the correct Sprite even in the background, the only place we needed to put a lock around was the drawing part as we drew out the content but rather than drawing a global DisplayObject we took the data from the cache and drew that which meant it was much, much faster, handled multiple threads just fine, and was extremely cacheable.
Conclusion
I hope this gives you some ideas; my hope was merely to get people thinking about two things: global objects require IPC, and that wiping the slate clean is wasteful. Let the garbage collector take care of clearing memory and make sure that you have only a few global objects.
Next time I’m going to talk about how I got a requirements engine running which would allow me to ensure that certain steps were always completed before anything else without creating messy spaghetti code. I think it’s pretty fantastic and I hope that you do too.
Posted in Programming, Actionscript | Tags 3, actionscript, cs4, evil, flash, globals, state | 2 comments
Posted by Chuck Vose
Fri, 12 Feb 2010 22:56:00 GMT
Cross posted on the Metal Toad Blog
Convincing Yourself
Convincing yourself of the value in 10% time is critical and is sometimes the most overlooked step in the process. Most people look at 10% time as nothing time; time you can’t bill for must surely be worth nothing financially right? And worse you might think, not only are you not billing but you’re robbing yourself of time that you could be actually developing so it feels like a double-whammy.
But can you think of a time when you’ve thought something like, “Man, this is repetitive but I don’t really have the time to script this.” Or maybe something like, “This code is just awful but I don’t really have any time to refactor it.” Or even better, “I should really release this to the community so that other people can help patch and debug for me but it’s not quite ready yet.”
Convincing people that 10% time is valuable can sometimes be a chore but I’m probably one of the luckiest guys in the country because my bosses actually want to do 10% time, but aren’t quite sure how to get to the point where we can afford it. But more than just being able to afford you it’s important to convince yourself, your coworkers, and your bosses that you can’t afford not to do something like 10% time.
Every one of those statements I find myself saying, but I never do anything about it and my perfectly good Open Source code languishes, my code goes unrefactored and I hate looking at it, and I often don’t script even some of the most business critical things because it just seems like it would take an insurmountable amount of time. But in my heart of hearts I know that this is completely insane. Releasing your Open Source code brings a notoriety that can’t even be approached by a fleet of skilled salesmen, refactoring your code so you can understand it or bear looking at it makes you a better programmer and makes you happier, and scripting business critical applications almost always saves time and certainly performs much more consistently in my experience.
So, I’m losing out on valuable press, writing hairy code, and doing things inconsistently. I think in most companies that should be considered grounds for expulsion but here we find ourselves doing it constantly. It’s not that we hate ourselves or our companies, but it is important to understand that to not improve yourself, to not invest in yourself, is to lose out on the most valuable resource that you have, yourself.
Now, if I’m feeling especially saucy I’ll continue this post later on. If not, I hope I’ve presented something half-cogent to chew on while drinking beers this evening with your friends and colleagues. Happy Friday!
no comments
Posted by Chuck Vose
Mon, 25 Jan 2010 22:58:00 GMT
Cross posted on the Metal Toad Blog
The toad just finished a small project involving the Facebook API and by far the most confusing aspect of the project was the terminology regarding the many, many different API’s and what each one actually does. I thought this merited a write-up and I hope you find it helpful.
FBML, Frames and the Canvas
“I’m interested in making a Facebook app that will live on Facebook.com but store data on my webserver.”
A long time ago Facebook was limited to creating apps only in their website which they called the frame. The components you were allowed to use on your section of the app (called your canvas) were instantiated by using FBML tags. In this style you would create an app on Facebook’s website and point your canvas url at your webserver. Your webserver would be interpreted through the rosie glasses of Facebook’s website; FB would make a call to your webserver and deliver the results to the end-user after making subtle alterations to your code.
XFBML and Facebook Connect
“I’m interested in making my website more interactive and allowing users to log in via their Facebook login.”
Not surprisingly making apps is not enough for many people and they want to extend their own websites instead of making Facebook better. To get Facebook integration into your site you’re going to be looking at XFBML, FBJS and FQL, together they can be called Facebook Connect though I don’t know if it’s officially defined as such.
Unlike FBML there is no frame and you’re relying on magic to get your XFBML parsed into a usable format. Rather than sitting in between your end-user and your webserver, this time your webserver uses the Facebook loader JS to construct iFrames which call out to Facebook.com, subtly change your XFBML tags into real HTML, and display the results.
What’s important to know though, is that the three different ways of interacting with Facebook (XFBML, FBJS, and the PHP/Language specific libraries) essentially do the same exact things so you can do whichever you feel most comfortable with or whichever gives you the most bang for your particular project. If you’re doing AJAX there’s a good chance you’ll end up with XFBML tags due to the limited interaction you’ll have with PHP/Whatever on the backend, but that’s totally up to you.
XFBML is not everything though and you’ll find yourself wanting to use fairly often to get at some of the juicier bits from FBML. Be not afraid, it is a kind layer once you get it developed. Best part is that serverfbml elements do know about each other so if you render a login and a hidden friend finder at the same time once people log in you can show the friend finder and it will have the friends of the now logged in user. Magic.
Glossary
FBML – Facebook.com specific tags for building Facebook apps.
XFBML – Facebook Connect markup language for adding Facebook functionality into your website.
FBJS – Facebook’s JS library that works on both Facebook.com apps and for Facebook Connect apps.
FQL – Facebook’s weird little query language for getting data quickly with no interface.
Facebook Connect – Namespace to describe stuff that you may want to do with your server.
Canvas – The main space on Facebook.com where you can find objects like your Wall.
Frame – The stuff around the frame like your notifications and the myriad ads.
Posted in Facebook | Tags api, facebook | no comments
Posted by Chuck Vose
Wed, 02 Dec 2009 22:59:00 GMT
Cross posted on the Metal Toad Blog
If you’ve ever worked with a rich data model in drupal you know it can be a pain
to load up all the children and parents of a node within the templating engine.
One method that could save you a lot of time is to load the data recursively
in node_load and save your poor front-end guy some wrist pain (or yourself if you’re
that guy!)
Edit: Please look at the get_metadata() definition towards the bottom or none of this is going to make sense.
Here is the initial bit which loads on details about a node. Here of course you
could load all manner of things like read/write attributes, cck fields, etc.
/**
* Implementation of hook_load().
*/
function wrapper_load($node) {
$metadata = get_metadata($node);
// Load our attributes without children. We'll load children later.
$node = wrapper_load_without_friends($node);
One thing we found was that search would break when trying to index since it was
trying to load all the data from associated nodes as well. One way of dealing with
this is to use hook_nodeapi(‘update index’) to only load a subset of data instead
of the whole shebang. But I didn’t do it like that and I’m not going to put untested
code on the blog (well, aside from slightly edited code).
This is what I did instead. It’s interesting for its hackishness. There must be a
better way though.
$backtrace = debug_backtrace();
if ($backtrace[4]['function'] == 'node_update_index') {
return $node;
}
Here we get to the fun part though. This bit loads up the parents, those that this
node belongs to. Due to performance constraints when doing this you need to pick
a direction to load infinitely. If you choose to load parents recursively you have
easier code and it’s actually a lot faster as far as SQL is concerned. If you choose
to load children recursively, as I will do in a moment, the SQL is a little slower
but on-the-fly SQL is easier to write for a belongs_to relationship. When all the
data necessary is already in the db row you load to build yourself it’s easy to
include your parents too.
So you’ll note that here we load up a collection of parents being careful to make
sure that the recursive function knows who called it by the parent attribute on $obj.
// Load up any parents
if ($metadata['belongs_to']) {
foreach ($metadata['belongs_to'] as $drupal_attr => $legacy_attr) {
// When we load up a child it shouldn't load its parents
if ($node->parent != $drupal_attr) {
$node->{$drupal_attr."_collection"} = array();
// Get all the node attributes for our new object
$obj = db_fetch_object(db_query(
"SELECT * FROM %s as extra WHERE node.nid = %s",
$node->$drupal_attr
));
// This ensures that a child doesn't reverse and load its parent in the
// next call
$obj->parent = $node->type;
// Push our recursively loaded object into the empty collection. Here we
// choose to only load one level but you could use hook_load again to load
// deeper structures. The parent attribute should prevent us from getting
// into loops.
array_push(
$node->{$drupal_attr."_collection"},
wrapper_load_without_friends($obj)
);
}
}
}
Very similarly, here we load up the children. This time we load recursively with
no end condition. This is prone to cycles so you may have to go with a non-recursive
loader here if you have a cyclic loading cycle or some other way of terminating
the recursion.
// Load any children
if ($metadata['has_many'] && $node->nid) {
foreach ($metadata['has_many'] as $drupal_attr => $legacy_attr) {
// Bail out if this node has a parent at all. We just want to keep it
// simple for now.
if (!$node->parent) {
// Get the list of things this object owns
$res = db_query(
"SELECT * FROM $drupal_attr as extra WHERE extra.%s = %s",
$node->type,
$node->nid
);
// node_load all children and drop them in an array
$node->{$drupal_attr."_collection"} = array();
while ($obj = db_fetch_object($res)) {
// Once again, prevent our children from loading us and creating loops.
$obj->parent = $node->type;
// Push our recursively loaded children and their children onto the empty
// collection. This time we go all the way and create a much deeper data
// model. These two could be reversed, loading belongs_to indefinitely
// but I find this way easier.
array_push($node->{$drupal_attr."_collection"}, wrapper_load($obj));
}
}
}
}
return $node;
}
Here’s where we load extra attributes from the node addon table that you see associated
with every custom content-type. These attributes just get added onto the node directly
so there is some concern about columns named with php reserved words. Conflicting
column names like title just need to be carefully considered, they may not actually
be bad a bad idea.
// Hook load that doesn't recursively load children/parents, just the attributes
// of another table
function wrapper_load_without_friends($node) {
$metadata = get_metadata($node);
// Load up extra info from the node addon table
if ($node->nid) {
$extra_attributes = db_fetch_object(db_query(
"SELECT * FROM %s WHERE nid = %s",
$node->type, $node->nid)
);
// Foreach of the linking attributes which will allow us to find children
// load them onto the node directly.
foreach ($extra_attributes as $key => $value) {
$node->$key = $value;
}
}
return $node;
}
This is just a sample of our metadata loader. Naturally you could do this some other
way but it works pretty well for us. There is room for improvement though, using
a different format like YAML could buy some extra win for instance.
// Sample metadata describing has_many and belongs_to relationships as well as
// the read/write attributes each table has.
function get_metadata($node=NULL) {
$metadata = array(
'belongs_to' => array(
'staff' => array(
'office' => 'office_id',
)
),
'has_many' => array(
'office' => array(
'staff' => 'office_id',
)
),
);
$return['belongs_to'] = $metadata['belongs_to'][$node->type];
$return['has_many'] = $metadata['has_many'][$node->type];
return $return;
}
Posted in Programming, Drupal | Tags data, drupal, model, node_load, recursive, rich | no comments
Posted by Chuck Vose
Fri, 30 Oct 2009 20:22:00 GMT
Cross-posted at metaltoad.com: The Power of Yes
Of all the powerful words in any language assertion is in my opinion the absolute most powerful. With affermative language we are able to create trust, enact people’s will, and begin processes. Silly of course, to take one word to mean so much but I believe everyone can agree that when a client asks for something ‘yes’ should be the thing that jumps to your lips immediately, hopefully without qualification but at times you have to add on things like ‘and it will cost x’ or ’, now lets look at the plan and see how we can make this a reality’.
But this post isn’t about clients, it’s about our language and framework choices.
When I was new to programming I said no a lot. This was in part an aspect of my first language PHP, which was perhaps as immature as I was at the time. ‘No’ was probably my biggest buzzword, maybe followed by ‘can’t’ which I justified by saying that PHP couldn’t do it. Turns out I was wrong, PHP really can do almost anything, but at the time I couldn’t. But ‘No’ was certainly an artifact of the culture. The things I do in PHP are often much harder than they need to be, at least they feel unnecessarily difficult to me now, and in this environment it is easy to say that it’s to hard. I’ve noticed since starting to use PHP again I’ve noticed ‘can’t’ creeping into my language more and more.
Ruby was the first language that was strongly oriented towards ‘yes’ both in the language and the culture. It could have been that I learned later in life or in a place that had a very strong Ruby culture (Seattle), but I suspect that the language had more to do with it than anything else. Ruby positively wants to make things faster for you so that the time between your thought and your expression of said thought grows shorter and shorter as you learn more Ruby. As such, when your language is saying ‘yes’ so often you naturally begin to start thinking this way yourself.
Drupal was a bit of a revelation to me in this regard. Drupal, as everyone knows, is a PHP app, yet despite the underlying language constructs the ui says ‘yes’ even more often than Ruby does. As my coworker pointed out, the amount of a website that gets finished before you hit the code is absolutely staggering. Maybe 90% of any given Drupal website could be accomplished by Drupal using only CCK/Views (et al) with the rest of the customizations being fairly quick using the copious APIs available to us. This is assuming that you find the correct project, in all things of course selecting the best tool for the job yields the best results.
So what are your languages saying to you every day? As products of our surroundings it is absolutely critical that we evaluate what our languages and tools say to us no just what they say about us. PHP may have a stigma, and Drupal through association, but I believe that the way it works with us is a much stronger reason to invest in exploring Drupal.
Posted in Programming, Ruby | Tags can, client, drupal, languages, Ruby, t, yes | no comments
Posted by Chuck Vose
Fri, 02 Oct 2009 16:57:00 GMT
Cross-posted at company blog: http://metaltoad.com/blog/multiple-dynamic-tabs-brightcove-3
This week I had the wonderful opportunity to work on an interesting problem that as far as I can tell hasn’t been documented. The call came out that we needed to generate a couple dynamic tabs on the top of our player for smart playlists. Now, we already have one dynamic playlist so I thought it was going to be a fairly simple logical step up to three but I was really, really wrong.
If you read issue 3 of the pragprog magazine you’re probably thinking a lot about parallel and asynchronous processing. So it was extremely exciting to have come to a parallelization problem in my day to day activities. I’ll explain the problem:
In order to load a playlist into a brightcove player with the Player API you have to have already fetched the videos from the server using getMediaCollectionAsynch, getMediaInGroupAsynch, or getMediaAsynch. With one dynamic tab it’s pretty easy to use getMediaInGroupAsynch because you can assume that the only time the MEDIA_COLLECTION_LOAD event is going to fire is when your call has returned. But when you’re loading up two or more media items asynchronously you can no longer make that assumption.
If you’re just pulling down pre-defined playlists it’s super easy to drop something like the following in your onMediaCollectionLoad listener:
function onMediaCollectionLoad (e) {
if (e.mediaCollection === null) {}
else {
tabBar.insertTabAt(e.mediaCollection, 0);
}
}
However if you’re using getMediaInGroupAsynch or getMediaAsynch it can be difficult to figure out why the listener is getting called. For us we had three playlists of dynamic data but outside of comparing the resulting array to the original array (inefficient and error-prone) there is no way of knowing which asynch call asked for these videos.
But maybe we don’t care. All we care about is that the videos are loaded completely when we insert the tabs into the player right? So we stop relying on order and just make sure that all the videos are loaded into the player before adding some tabs. To do this I maintained a request counter (which could be called a counting semaphone were you so inclined. Before each request I increment the counter and when the request is filled I decrement the counter. When the counter hits 0 I know that all requests have been filled and I can safely add all the tabs at once.
Your code could probably look something like this:
var tab_count = 0;
var popular_tab;
function onTemplateReady (e) {
tabBar = exp.getElementById("playlistTabs");
if (popular.length > 0) {
tab_count = tab_count + 1;
popular_tab = true;
content.getMediaInGroupAsynch(popular);
}
}
function onMediaCollectionLoad (e) {
if (e.mediaCollection === null) {
// Do nothing
}
else {
// Make sure that these playlists coming back are coming from
// getMediaInGroupAsynch. Otherwise their id would be positive.
if (e.mediaCollection.id < 0) {
// Add all media to the player's memory.
var mediaDTOs = new Array();
jQuery.each(e.mediaCollection.mediaIds, function(i, val) {
mediaDTOs.push(content.getMedia(val));
});
// Decrement the counter. When this hits zero we'll have filled all reqs.
tab_count = tab_count - 1;
if (tab_count == 0) {
// Make sure this is one of the playlists we were going to add.
if (popular_tab) {
popular_playlist = {
displayName: "Most Viewed Videos",
mediaIds: popular
};
tabBar.insertTabAt(content.createRuntimeMediaCollection(popular_playlist, 'playlist'), 0);
}
}
}
}
}
Here’s hoping this helps you out in your quest for multi-tabbed players and managing asynchronous loading.
Posted in Programming, Brightcove | Tags asynchronous, brightcove, dynamic, jquery, parallel, processing, requests, tabs | no comments
Posted by Chuck Vose
Fri, 25 Sep 2009 20:49:00 GMT
Cross-posted from the Company Blog
In my experiences in the past working with external contractors is often a pain, especially the final merge where you try to incorporate all their code. I’ve always been a huge proponent of Subversion for SCM but today I saw an example where Git knocked the socks off of Subversion.
Time came and code needed to be merged into trunk. While discussing our options someone noticed that there was a .git file in the project directory and put the pieces together. While Subversion relies on a central repo Git has no such requirement and the contractor had included his git repo with the file which meant that we had his complete revision history available to us as well as an extremely easy method of applying diffs.
Normally merging was a pain but today we issued these commands, watched the output, then shared high-fives and a beer.
cd imported_files
git log
*write down the first couple chars of the SHA1 of the first commit
git diff --no-prefix {sha of first commit}..HEAD > patchfile
cd trunk
patch -p0 < ../imported_files/patchfile
Who knew it was going to be so easy!?
So the moral of the story: If you’re working with people that may not be in your office or you expect to get a tarball/zip of their work you could do no better than Git for version control. You get to see their complete history and work with it.
I’ll bet that someone out there smarter than myself actually knows how to replay the git commits so you can keep the revision history as well. Git is cool!
Note: Patch instructions based on these directions
Posted in Programming, Git | Tags contractors, git | 1 comment