Using xspf-player with IE

This is a major problem. It is a pain to make xspf-player work with Internet Explorer. Always the problem, Internet Explorer has exhibited this issue for a couple of versions now. Using standard xhtml to include the player means that IE ends up botching the song.

This has caused problems for me. On the CrucialMusic site, I used Wimpy Player (highly recommended, by the way) for IE and xspf-player for everyboby else. At the time, Wimpy didn't play well under Linux or Mac, don't know why. It was a Jack Sprat issue, and ended up working out fine albeit ugly since I had to program for two players.

With the latest site that I've completed (private site, so no URL) I decided to really spend some time and make one player, preferably xspf-player, work everywhere. And so I did. I used the excellent SWFObject code to do the player embedding. But I didn't want to use all those low-level calls in my code, which was already written around a simple javascript class that I'd put together for CrucialMusic.

So here's what I came up with. Released with a BSD license, have fun. This will make xspf-player work with Internet Explorer, which from reading the message boards at the SourceForge site seems to be a perpetual problem.

// XSPF Player JavaScript Control
//
// By Michael Chaney, Michael Chaney Consulting Corporation
// Copyright 2006 Michael Chaney Consulting Corporation
//
// This code is released under the same BSD-style license as the XSPF Player.
// For more info: http://musicplayer.sourceforge.net/

function XSPFPlayer(mdiv, width, height) {
        this.obj = new SWFObject("/flash/xspf_player_slim.swf", "single", width, height, "7");
        this.div = mdiv;
        this.obj.addVariable('autoplay','');
        this.obj.write(this.div);
}

XSPFPlayer.prototype.play_song = function(title, song_url) {
        this.obj.addVariable('autoplay','true');
        this.obj.addVariable('song_url', song_url);
        this.obj.addVariable('song_title', title);
        this.obj.write(this.div);
}

XSPFPlayer.prototype.stop = function() {
        this.obj.addVariable('autoplay','');
        this.obj.write(this.div);
}

In your HTML:

<div id="player1">If the player isn't here, you need to <a href="http://www.macromedia.com/go/getflashplayer">download Flash</a></div>
<script type="text/javascript">
      var xspfplayer = new XSPFPlayer('player1',150,15);
</script>

Then, if you want to play a song:

<a href="#" onclick="xspfplayer.play_song("My Song","/url/to/song.mp3"); return false;">Play My Song</a>
<a href="#" onclick="xspfplayer.stop(); return false;">Stop</a>

Ajax.InPlaceSelect + Ruby on Rails

The Scriptaculous Ajax.InPlaceSelect is very useful for simple data updates. Here's a little piece of code, based on the excellent editable_content helper.

def selectable_content(options)
options[:content] = { :element => 'span' }.merge(options[:content])
options[:url] = {}.merge(options[:url])
options[:ajax] ||= {}
script = Array.new
script << "new Ajax.InPlaceSelect("
script << " '#{options[:content][:options][:id]}',"
script << " '#{url_for(options[:url])}',"
script << " [" + options[:data][:collection].collect { |c| "'"+escape_javascript(c.send(options[:data][:value_method]).to_s)+"'" }.join(', ') + " ],"
script << " [" + options[:data][:collection].collect { |c| "'"+escape_javascript(c.send(options[:data][:text_method]).to_s)+"'" }.join(', ') + " ],"
script << " {"
script << options[:ajax].map{ |key, value| "#{key.to_s}: #{value}" }.join(", ")
script << " }"
script << ")"

content_tag(
options[:content][:element],
options[:content][:text],
options[:content][:options]
) + javascript_tag( script.join("\n") )
end

Use it like this:

<%= selectable_content(
:content => {
:text => cd.library.name,
:options => {
:id => "cd_library_id_#{cd.id}",
:class => 'editable-content'
}
},
:url => {
:controller => '/admin/cd',
:action => 'ajax_update_library_id',
:id => cd.id
},
:data => {
:collection => Library.find(:all, :order => 'name'),
:value_method => 'id',
:text_method => 'name'
}
) %>

Recovering gvinum mirror

I use gvinum under FreeBSD to perform simple mirroring of the drives on my servers. This has served me well, and in fact just last Friday saved me from a monumental headache as one of my drives crashed. Very hard crash, so bad that the computer was unable to see either drive if that one was just plugged in to the controller.

I set my drives up using the instructions at this web site and it's worked well, but the disaster recovery presented didn't work for me. So I did a little testing.

The problem involves bring back the missing drive - the kernel (this is 6.1) will panic if the drive is created again. The problem can be alleviated by removing the plexes and subdisks that were bound to the missing drive before bringing it back. In abbreviated form, here are the steps involved:

  1. Physically replace the drive.
  2. Boot off the good drive into single-user mode. Technically, you don't have to do this, but it just seems like a good idea, right?
  3. Use "gvinum rm" to remove the subdisks and plexes for the missing drive. Typically, you can grep for ".p0" or ".p1", whichever is the missing drive. Be really careful, of course.
  4. Use bsdlabel to "copy" the label from the good drive to the new drive.
  5. Add the drive back in. Remember that if you have any subdisks still pointing to it, the kernel will panic.
  6. Add the plexes and subdisks back.
  7. Start the plexes one by one, allowing time for it to rebuild the mirrors.

Now, if you have a setup like mine where everything is simply mirrored and the drives are identical (or setup identically), there's a very easy shortcut to bringing back the missing plexes and subdisks. When you run "gvinum create", it drops you into an editor with the current configuration commented out. Uncomment the plexes and subdisks, and change them to match the other drive, then save. As an "ed" script:

% s/^# plex/plex/

% s/^# sd/sd/

% s/p0/p1/g

% s/drive main/drive secondary/g

Of course, you might be replacing your "main" drive in which case you would reverse the drive names and "p0" and "p1". But that's it. After this, make sure your plexes and subdisks look correct, then use "gvinum start" one at a time to bring up the mirrors.

To test, you can either boot to the FixIt disk, or boot into single-user mode with geom_vinum.ko unloaded and your rootdev set to the proper bsdlabel device. Then, use:

dd if=/dev/zero of=/dev/da3 bs=65536 count=256000000

To clear out the first 15GB or so of the drive. At that point, you should reboot and start testing with your now blank drive.

I have a perl script that will "translate" the bsdlabel into gvinum setups, I will post it when I get a chance.

First Post

This is my first post in my new tech blog.