Year: 2008

The hunt for a Twitter client

I hadn’t jumped on to the Twitter bandwagon for a while. I’m not much of a conversationalist, nor am I a very sociable. I also tend to stay away from social networks. But I figured I would try Twitter out for a while, mostly because it’s an outlet for short comments. For long articles, I have my blog. For sharing links, I have Google Reader and del.icio.us. I don’t quite have anything for that occasional moment when I want to say, "Hey! A great way to shred mint leaves is to freeze them!"

The question is what client to use. I wanted something free, portable and featherweight (as in lighter than lightweight: no additional memory usage.)

SMS is the classic Twitter channel. But I don’t like being bothered by SMS messages often. Besides, it’s not free. So that’s out.

The next best would be e-mail via my BlackBerry. The problem is, Twitter doesn’t accept tweets via e-mail. So when looking for alternatives, I found Identi.ca, which is even better than Twitter except for the fact that it doesn’t have Twitter’s user base. Anyway, it accepted e-mail, so that was fine.

On the desktop, the browser is the obvious choice. But somehow, going to the Twitter home page and typing out a tweet felt so… Web 1.0. I didn’t fashion installing a client just for tweeting, like Twhirl. The closest was instant messenger software. Since Identi.ca accepts messages via XMPP, I could install Google Talk and send messages via instant messenger.

That worked for a couple of weeks. Then I pulled out. Instant messenger has the disadvantage of making you accessible, and I honestly don’t have the time. Plus, I don’t fancy running apps persistently, not even something as light as Google Talk. So back to square one.

In the meantime, I was having another problem with sending updates via BlackBerry. My corporate mails have a HUGE disclaimer attached to them. Doesn’t make sense to have 140 character message followed by a 940 character disclaimer. I’d have to get rid of those anyway.

After a bit of digging around, I came across mail handlers. I can write a program on my server to handle mails. So I wrote one that strips out the disclaimer and forwards it to my identi.ca e-mail ID. (Now I’ve modified it to use the API.) So that solves my mobile twittering problem.

It also solves my cross-posting problem. I maintain a twitter.com/sanand0 and an identi.ca/sanand0 account and keep them updated in parallel. My mail handler updates the post on both services.

As for the desktop, I have the best solution of all. I use the browser address bar to twitter. I’ve created a keyword search with the keyword "twitter" with is keyed to a URL like http://www.s-anand.net/twitter/%s. So if I say "twitter Some message" on the address bar and press enter, it contacts the server, which updates Identi.ca and Twitter using the API.

Of course, you don’t really need to do that to update Twitter. Just create a keyword search with a keyword "twitter" and a URL http://twitter.com/home?status=%s, and you’re done. Remember: you can create keyword searches in Internet Explorer as well (read how). With this, you can update twitter from the address bar by just typing "twitter your message goes here".


Anyway, that was a long-winded way of saying just two things.

  1. Mail handlers are cool.
  2. Keyword searches let you update Twitter from the address bar using the URL http://twitter.com/home?status=%s

Bound methods in Javascript

The popular way to create a class in Javascript is to define a function and add methods to its prototype. For example, let’s create a class Node that has a method hide().

1
2
3
4
5
6
var Node = function(id) {
    this.element = document.getElementById(id);
};
Node.prototype.hide = function() {
    this.style.display = "none";
};

If you had a header, say <h1 id="header">Heading</h1>, then this piece of code will hide the element.

1
2
var node = new Node("header");
node.hide();

If I wanted to hide the element a second later, I am tempted to use:

3
4
var node = new Node("header");
setTimeout(node.hide, 1000);

… except that it won’t work. setTimeout has no idea that the function node.hide has anything to do with the object node. It just runs the function. When node.hide() is called by setTimeout, the this object isn’t set to node, it’s set to window. node.hide() ends up trying to hide window, not node.

The standard way around this is:

3
4
var node = new Node("header");
setTimeout(function() { node.hide()}, 1000);

I’ve been using this for a while, but it gets tiring. It’s so easy to forget to do this. Worse, it doesn’t work very well inside a loop:

1
2
3
4
for (var id in ["a", "b", "c"]) {
    var node = new Node(id);
    setTimeout(function() { node.hide(); }, 1000);
}

This actually hides node "c" thrice, and doesn’t touch nodes "a" and "b". You’ve got to remember to wrap every function that contains a function in a loop.

1
2
3
4
5
6
for (var id in ["a", "b", "c"]) {
    (function() {
        var node = new Node(id);
        setTimeout(function() { node.hide(); }, 1000);
    })();
}

Now, compare that with this:

1
2
3
for (var id in ["a", "b", "c"]) {
    setTimeout((new Node(id)).hide, 1000);
}

Wouldn’t something this compact be nice?

To do this, the method node.hide must be bound to the object node. That is, node.hide must know that it belongs to node. And when we call another_node.hide, it must know that it belongs to another_node. This, incidentally, is the way most other languages behave. For example, on python, try the following:

>>> class Node:
...     def hide():
...             pass
...
>>> node = Node()
>>> node
<__main__.Node instance at 0x00BA32D8>
>>> node.hide
<bound method Node.hide of <__main__.Node instance at 0x00BA32D8>>

The method hide is bound to the object node.

To do this in Javascript, instead of adding the methods to the prototype, you need to do two things:

  1. Add the methods to the object in the constructor
  2. Don’t use this. Set another variable called that to this, and use that instead.
1
2
3
4
5
6
7
var Node = function(id) {
    var that = this;
    that.element = document.getElementById(id);
    that.hide = function() {
        that.element.style.display = "none";
    }
};

Now node.hide is bound to node. The following code will work.

8
9
var node = new Node("header");
setTimeout(node.hide, 1000);

I’ve taken to using this pattern almost exclusively these days, rather than prototype-based methods. It saves a lot of trouble, and I find it makes the code a lot compacter and easier to read.

The courage to be honest

Some months ago, I was working with a client who wanted to set up a website with social commerce elements. (That’s Web 2.0 in fancy words.) They only seemed to have a very rough idea of what they wanted, so asked them right at the start of the meeting: "Why do you want social commerce?"

Their answer was interesting, and one that I had not expected. They said, "We want to project the image of an honest an open organisation."

Hmm. Fair enough.

So we went on with the meeting, discussing what they could do with blogs, how commenting would work, and so on. The main thing was to open up the site for the bank’s customers to talk freely.

At some point, one of the client’s team indicated that profanity and abusive comments would need to be filtered out, so moderation becomes important. "We don’t want to become liable for content that is on our site."

Fair point. While discussing that, another chipped in, saying "True. We will also need to monitor negative comments. We don’t want our site to have negative comments about our products."

A brief silence.

Many nods.

And the conversation continued.

I was too stunned to butt in immediately. But after a few minutes, I raised the point about negative comments. "You want to project the image of an honest and open organisation. If you filter out comments that say anything bad about you, how are you going to achieve that?"

They thought for a short while, and someone said: "Yes, the users will probably find out about it."

You can’t project an honest image unless you are honest. That means being honest about the good as well as the bad. Honesty is irrelevant for good news — no one lies about good news. Are you honest when delivering bad news? That requires courage.

This is worrisome. Someone saying "We don’t want negative comments" is a bit of an issue. But also worrying is their reason for why not. I had hoped that it would be "That’s not what an open an honest organisation does". Instead, it was "The users will probably find out about it."

This sort of behaviour stems from insecurity. It’s what keeps us late at work, not want to be the first to leave. It’s what makes us say "Yes" to things we would really rather say "No" to.

I remember a time when we were making slides late in the night. I finished mine quickly, and took printouts for the project leader to review. He word-smithed it on paper, I typed it back in, and took a printout again. (Yeah, he could’ve edited it himself. But…) And when all of that’s done, I’m still waiting, not wanting to "leave the team behind". I’m a team player after all.

It’s like drugs. You want to fit in. Be a team player. If the team’s doing it, you do it too.

These days, I’m the first to leave from work. Sometimes, it’s late when I leave, but I’m always the first to leave. And it hasn’t made any difference. At least not that I can tell.

A lot of the fear is in the mind, frankly.

There also was a time when I couldn’t say "No". When I left BCG, I spoke to a partner during an exit interview about how I wanted to work less hours. He thought the problem was more fundamental.

"Anand, knowing you, you’re the kind of person that will end up working hard no matter where you are. So will the move really make a difference?"

"The difference, James, is that here I’ve set up an expectation of saying ‘Yes’. I’ve gotten into the habit, and I’ve gotten others into the habit. At least in a new place, I’ll have a fresh start and set new expectations."

That’s happened, fortunately. These days, I consistently say ‘No’. With some folks, it’s easy.

"Anand, would you be able to help out with this?"

"No, sorry." (with an "please excuse me" smile on my face.)

Some people still scare me, though. (These are the aggressive Type A personalities that it is my occasional misfortune to come in contact with.) And when it does, I lie.

"Anand, we need this proposal out by Monday. Could you help out over the weekend?"

"Sorry, have plans for the weekend. I’m visiting a cousin at Brighton."

No, I don’t have a cousin at Brighton. I’m just scared to just say, "Sorry". It didn’t matter, though. The fear is real, but it still is only in your mind.

It’s the same with businesses. We’re collectively scared to admit something’s wrong with us, or that we can’t do something. I went for a meeting with a partner recently. The client mistook us for operations consultants. (We’re IT consultants.) So he asked us what operations experience we have. Our response should’ve been "None. We’re IT consultants."

Instead, our response was "Oh, we have several years of experience in the organisation. We’re this, we’re that, we’re great."

I don’t think we’d have been thought less of if we’d said "None". And we were found out in the next meeting anyway.

It’s the same about opening up to negative comments. If somebody makes a negative comment, it’s okay! Not that many people care. Hushing it up makes it worse (like for instance with BA and Virgin recently). Lying about it might work, but only for a while. The real problem with lying not that you might get caught out — it’s that you’ll get into the habit and in the long run, will get caught out.

For me, the best cure for this sort of fear is the firm belief that the world cares a lot less about us than we think. It’s okay to be a loser. No one cares but us. But it’s less okay to be a liar.

Dilbert search statistics

It’s been three weeks since I initiated the effort to type in the Dilbert strips and the results are encouraging. About 2 years worth of strips have been typed out. So this Dilbert viewer now has a reasonably sized index for searching.

Many thanks are in order here. The first is due to geek.nl, whose images I have taken the liberty of hotlinking. Thanks also to those who’ve taken the time out to type strips:

  • granger95
  • bthangaraj
  • gdibyo
  • adrienbernard
  • sundar.ramakrishnan
  • pistohl
  • waywardone
  • balamurugan.cse
  • sruppenthal

… and several others.

When I initially planned to share the typing of the Dilbert strips, I anticipated that I would probably type in the most, and almost no one would pitch in. While I still have typed in the most, the contributions of the above have been of great help in more than the obvious way. When I typed out 10 years of Calvin & Hobbes, it took me 5 years. This is 2 years of Dilbert in 3 weeks. If nothing else, it’s pushing me to work harder on this. So thanks again for the motivation.

Here’s my request again to all you Dilbert fans.

  1. Please go to dilbert-search.appspot.com
  2. Log in using your Google account and type in as many strips as you like
  3. Bookmark it for the future, whenever you’re bored

Here’s a Wordle cloud of all the strips typed out so far (with Dilbert and Pointy Haired Boss removed.)

Dilbert

(Seeing that there’s more "good" than "bad", and more "like" than "dislike" or "hate", you might even call Dilbert an optimistic strip.)

Recording online songs

In the 1980s, we rarely used to buy audio cassettes. It was a lot cheaper to record songs from the radio. It’s amazing that in the 2000s, this technique seems to be less used than before.

If you wanted to record a song that was streamed online, you could go through the complex procedures I’d mentioned earlier to download online songs, or you could use the 1980s technologies. Get a tape recorder, connect the headphones of your PC to the tape recorder’s microphone using a stereo cable, and record to your heart’s content.

Except, of course, that tape recorders are rather outdated. And with the right software, your PC can act like a tape recorder. Here’s how you can go about it.

  1. Download Audacity and install it
  2. Download Lame and save it
  3. Open Audacity and select "Wave Out" as the source
  4. Play a song online and click on the Record button. Press the Stop button when done
  5. File – Export as MP3. (The first time, you need to tell Audacity where you’ve saved Lame)

That’s it. You can convert anything your computer plays into an MP3 file. (The general rule in digital media is: if you can see / hear it, you can copy it.)

OK, lets’ do this more slowly.

1. Download Audacity and install it.

Audacity is a program lets you record and edit music. Just visit the link above (or search on Google for "Download Audacity") and install the program. This is what it looks like.

Audacity

2. Download Lame and save it

When you record something with Audacity, you’ll usually want to save it as an MP3 file. Lame is another software that lets you do that. Go to the link above, download the ZIP file, and unzip it in some folder. (Remember where you unzipped it.)

3. Open Audacity and select "Wave Out" as the source

You can choose which source to record from in Audacity. Do you see the "Line In" in the screenshot below? That’s the source from which Audacity will record sound from. Usually, your PC will have a "Microphone" socket, and may have a "Line in" socket. It may also have a built-in microphone. Depending on what sockets and capabilities your PC has, you may see different things.

Audacity source

One of these sources will probably be "Wave Out". That lets you record any sound played by your computer. So if you want to record a song your computer’s playing, what’s what you should choose.

Not all sound cards have the "Wave Out" option, though. Many laptops that I have used don’t seem to have this option. If that’s the case with you, there’s a fairly simple solution. Just buy a stereo-to-stereo cable (shown below) and connect your headphone socket to your microphone socket.

Stereo to stereo cable

This transfers everything your computer plays back into the microphone, and you can select "External Mic" as your source.

Buying this stereo cable has another advantage. Rather than connect one end to your computer’s headphones, you can connect it to anything: your old cassette player, your radio, a microphone, whatever. So that means you can now:

  • Convert your old tapes to MP3
  • Record songs on the radio as MP3
  • Record songs from the TV / DVD player as MP3
  • Record live conversations as MP3
  • Record phone conversations as MP3
  • etc.

4. Play a song online and click on the Record button. Press the Stop button when done

That’s easy. The Record button is the red circular button that’s third from the left. The Stop button is the yellow square button that’s second from the right.

5. File – Export as MP3

When you’ve stopped recording, you can actually do a bunch of useful things with Audacity.

The first is to adjust the volume level. Go to the Effect menu and select Amplify. Then you can try different amplification levels to see how it sounds.

The next is to trim the audio. Unless you’re really fast with the keyboard, you probably have some unwanted sound recorded at the beginning or the end. You can select these pieces by dragging the mouse over the wiggly blue lines, and go to the Edit menu and pick Delete.

Lastly, you’ll want to set the sound quality. Go to Edit – Preferences and under the File Formats tab, set the bit rate under the MP3 Export Setup section. (If you don’t know what rate to put in there, 128 is a safe number. If you want better quality, increase it. If you’re short of disk space or want to mail it to someone, decrease it. Based on my experiments, even a good ear can’t tell the difference at 128. I use 64 or 96. My ear is pretty bad.)

All of the above was optional. If you just wanted to save the file, go to the File menu and select "Export as MP3". The first time you do that, you’ll be asked to mention the folder where you saved lame_enc.dll (which is where you unzipped Lame.) Show Audacity the folder, and that’s it.

Dilbert search engine

Wouldn’t it be cool to be able to search through the Dilbert archives using text?

This used to be possible at Dilbert.com some years ago, as a paid service. In late 2003, I needed to find some Dilbert strips for a client, so I’d subscribed for a year. I could then search for the quotes (I happened to be looking for "outsourcing", so you can guess the context).

But I can’t seem to find the feature any more, even as a paid service. The site looks a lot better, of course. But I can’t find strips.

Well, why not type them out? After all, I’d done that with Calvin and Hobbes.

This would be a much larger exercise, though. And I’m hoping to take your help. I’ve set up a site at dilbert-search.appspot.com. You can type in a comic randomly, starting from 2000. These will be made searchable on my Dilbert page. You can export the data and use it yourself, of course.

When typing in Calvin and Hobbes, I did have a few volunteers willing to pitch in, but collaboration tools weren’t easy to set up, and I ended up typing the whole thing myself. This time, I’d be delighted if even 10 people typed in just a strip each.


So, here’s my request, to all you Dilbert fans.

  1. Please go to dilbert-search.appspot.com
  2. Log in using your Google account and type in as many strips as you like
  3. Bookmark it for the future, whenever you’re bored

As I said, the data is readily exportable from the page, so if you’re looking to do cool mash-ups with it, great! And if you want the data exported in other formats, please let me know.

Incidentally, I created the site using Google AppEngine. The source code is at dilbert-search.googlecode.com.

Downloading online songs

You know those songs on Raaga, MusicIndiaOnline, etc? The ones you can listen to but can’t download?

Well, you can download them.

It’s always been possible to download these files. After all, that’s how you get to listen to them in the first place. What stopped you is security by obscurity. You didn’t know the location where the song was stored, but if you did, you could download them.

So how do you figure out the URL to download the file from?

Let’s take a look at MusicIndiaOnline first. When you click on a song, it pops up in a new window. You can’t figure out the address of that window because the address bar is hidden, but you can get it by hovering over the link before clicking, or by right-clicking and copying the link location. It’s always something like this:

http://www.musicindiaonline.com/p/x/FAfgqz0HzS.As1NMvHdW/

It always has the same structure: http://www.musicindiaonline.com/p/x/something/. Let’s call that the something the song’s key.

Now, what this page does is play a .SMIL file. An SMIL file is a text file that has a list of songs to play. This file is always stored at

http://www.musicindiaonline.com/g/r/FAfgqz0HzS.As1NMvHdW/play2.smil

(Notice that the key remains the same.) If you type this into your browser, you’ll get a text file that you can open in Notepad. You’ll find that it has a bunch of lines, two of which are interesting. There’s one that reads:

<meta name="base" content="http://205.134.247.2/QdZmq-LL/">

The content="…" part gives you the first part of the song’s address. Then there’s a line that reads:

<audio src="ra/film/…"/>

The src="…" part gives you the rest of the address. Putting the two together, you have the full URL at which to download the song.

Except, they’re a bit smart about it. These songs are meant to be played on RealPlayer, and not downloaded. So if you try to access the page using your browser, you get a 404 Page not found error. But if you typed the same page into RealPlayer, you can hear the song play.

To actually download the song, you need to fool the site into thinking that your browser is RealPlayer. So first, you need to get a good browser like Firefox. Then download the User Agent Switcher add-on. Change your user agent to "RMA/1.0 (compatible; RealMedia)" and try the same song: you should be able to download it.

Let me summarise:

  1. Right-click on the song you want to play, and copy the URL
  2. Extract the key. In the URL http://www.musicindiaonline.com/p/x/FAfgqz0HzS.As1NMvHdW/ the key is FAfgqz0HzS.As1NMvHdW
  3. Open http://www.musicindiaonline.com/g/r/<your_key>/play2.smil in your browser. Open it with Notepad
  4. Switch the user agent on your browser to "RMA/1.0 (compatible; RealMedia)"
  5. Put the content="…" and audio src="…" pieces together to form the URL
  6. Type the URL and save the file

I’ve automated this in my music search engine. So if you go to the Hindi, Tamil or any other online music page and click on the blue ball next to the song, you’ll see a menu with a download option. The download opens a Java program that does these steps for you and saves the song in your PC.

So now, you’re probably thinking:

  1. How did he figure this out?
  2. What about other sites?
  3. How does that Java program work?
  4. How do I listen to these on my iPod?

How did I figure this out?

Fiddler.

I believe a good measure of a tool’s power is it’s ability to be the one-word answer to a relatively broad question. For example, "Where can I find more about something?" "Google it." "How do I improve my pictures?" "Photoshop it".

Fiddler’s like that. "How do I find out what I’m downloading?" "Use Fiddler".

It’s a proxy you can install on your machine. It automatically configures your browsers when you run it. Thereafter, it tells you about all the HTTP requests that are being sent by your machine. So if you patiently walk through the logs, you’ll find all the URLs that MusicIndiaOnline or any other site uses, as well as the other headers (like User-Agent) that are needed to make it work.

What about other sites?

I’ll list a couple here.

Smashits:

  1. Right-click on the song you want to play, and copy the URL
  2. View the source and hunt for the URL fragment "player/ra/ondemand/launch_player.cfm?something".  The key is is something.
  3. Open http://ww.smashits.com/player/ra/ondemand/playlist.asp?6;giftsToIndia1.rm;1,something in your browser, using Notepad
  4. Switch the user agent on your browser to "RMA/1.0 (compatible; RealMedia)"
  5. Type in what’s inside the src="…" in your browser and save the file

Oosai:

  1. Right-click on the song you want to play, and copy the URL
  2. View the source and hunt for the URL fragment onclick="setUsrlist(something)".  The key is is something.
  3. Open http://www.oosai.com/oosai_plyr/playlist.cfm?sng_id=something in your browser, using Notepad
  4. Switch the user agent on your browser to "RMA/1.0 (compatible; RealMedia)"
  5. Type in the URL that you see in Notepad and save the file.

Try figuring out the others yourself.

How does the Java program work?

It does most of these steps automatically. The applet itself is fairly straightforward, and you can view it here. It takes two parameters: db, which indicates the server from which to download (M for MusicIndiaOnline, S for Smashits, etc.) and num, which is the key.

But in order for an applet to be able to download files from any site, and to be able to save this on your PC, it needs to be a signed applet. Since Sun offers a tool to sign JAR files, this wasn’t much of an issue.

There is one pending problem with Windows Vista, however. Signed applets can’t save files anywhere on Vista. They are saved in a special folder. This is being fixed, but until then, if you use Internet Explorer on Vista, you probably will be unable to find your saved files.

How do I listen to these on my iPod?

The saved files are in RealMedia format. Your best bet is to convert them to MP3 files using MediaCoder. Then transfer them using iTunes.

Gadgets

Some gadgets I’ve bought / got over the last few years.


SDHC Card Reader on 17 March 2009

16GB USB Flash Drive on 8 Jan 2009

16GB SD Card on 14 March 2009

USB MIDI cable on 30 Dec 2008

Creative Labs EP-630/A Earphones on 30 Dec 2008

Recta Micro Compass Accessory on 30 Dec 2008

Strand iPod Cassette adapter on 30 Dec 2008

Keysonic Compact Notebook Layout Wireless 2.4Ghz Radio Frequency Keyboard With Integrated Touch Pad on 6 Sep 2008

TomTom ONE v3 Great Britain on 31 Aug 2008

BlackBerry Curve 8320 on 10 Aug 2008

Acer Aspire 5715Z Notebook Laptop, Intel Pentium Dual Core T2330 1.6GHz, 15.4″ TFT, 2GB RAM, 80GB Hard-drive, DVD±RW, Intel Graphics Media Accelerator X3100, WiFi, Vista Home Premium on 29 Jul 2008

BlueNEXT BN-909 GPS Receiver SiRF Star III on 6 Jul 2008

TDK Recordable Blank 16x DVD+R Discs 25pack Cakebox on 6 Jul 2008

Sandisk MicroSDHC 4GB Card in 6 Jul 2008

HTC S620 (Excalibur) on 1 Jul 2008

Masterplug 4 Gang Switched Extension Lead 2m 13 Amp Fused on 31 Mar 2008

TRUST HU-4440P 4 PORT USB2 MINI HUB on 31 Mar 2008

Hama Compact USB 2.0 Hub 1:4 on 31 Mar 2008

Nintendo Wii Remote on 1 Mar 2008

Sandisk 2GB Secure Digital Card on 29 Feb 2008

Canon IXUS 70 Digital Camera – Silver (7.1MP, 3x Optical Zoom) 2.5″ LCD on 29 Feb 2008

Verbatim DVD+R 25Pk 16x Spindle on 15 Feb 2008

Western Digital My Book Essential 500GB External USB 2.0 Hard Drive on 15 Feb 2008

LUPO DIGITAL TV DVB-T USB ADAPTER/DONGLE/STICK FREEVIEW RECEIVER & AERIAL FOR PC AND LAPTOP on 6 Jan 2008

Bontempi Keyboard – 61 Full Key GM, Midi, Stereo (AD177.12) on 4 Jan 2008

Mini-Headphone Splitter (Stereo) on 1 Jan 2008

Microsoft LifeCam VX-6000 on 26 Dec 2007

Kenwood FP580 Food Processor 2 Speed White on 26 Dec 2007

SanDisk Sansa m240 1Gb MP3 Player on 24 Jul 2007

Sennheiser CX300 Eco Ear Canal Headphones Black on 24 Jul 2007

Uniross AAA 1000mAh (4)Rechargeable Battery Ni-Mh on 24 Jul 2007

Logitech EX110 Wireless Desktop on 5 Mar 2007

Cordless Skype Phone Kit NON VISTA on 5 Mar 2007

LG 42PC1D 42″ Plasma TV on 20 Jan 2007

Sony Ericsson Standard Travel Charger (UK) CST-13 on 12 Jan 2007

Uniross Sprint 1 Hour Battery Charger inc 4 x AA 2700 mAh Rechargeable Batteries – batteries upgraded from 2500 mAh on 11 Jan 2007

Fuji FinePix S5600 Zoom Digital Camera [5.0MP,10xOptical Zoom] on 17 Nov 2006

Fuji 1GB XD Type M Picture Card on 17 Nov 2006

DIGIHOME DVB915 FREEVIEW Digital Terrestrial Receiver with SCART Lead on 17 Nov 2006

Sony 80min/700MB Thermo printable CD-R spin 50pk on 28 Jul 2006

CyberHome DVD 401/0 Multi-region Capable DVD Player with DIVX on 28 Jul 2006

Rivision DVD+R 8x 4.7Gb 100 Cake Box on 15 Jun 2006

Dynamode 3.5″ IDE Interface Disk Enclosure on 1 May 2006

SANDISK CRUZER MICRO 1GB on 3 Jan 2006

BenQ 16x External Dual Format, Double Layer DVD Writer – EW162I, Beige on 3 Jan 2006

Emtec DVD+R Cake Box 100pk on 3 Jan 2006

Panasonic NV-GS17B MiniDV Digital Camcorder [24x Optical, 2.5″ LCD, DV out] on 9 Nov 2005