Tuesday, July 7, 2009

Procedure and ruby script to remove duplicated tracks from iPod in Last.fm client before scrobbling

Since I bought an iPod Touch, it became my main Last.fm scrobbler device. However, I am facing a recurrent problem, every track that I listen in my iPod is scrobbled between 2 and 20 times with the same timestamp! The picture bellow shows the result:

I am using the Last.fm official client with the iTunes plug-in. I have searched a solution in several sites (e.g, here), lots of people are facing the same problem, but I did not find any solution yet.

To make the problem even worse, lately the remove button in the Last.fm tracks page don’t remove a single occurrence of a track anymore, instead it removes all occurrences in a given timestamp. So, once a track is scrobbled multiple times, I can not remove the duplicates anymore, because once I remove one, it removes all the entries.

So, while I don’t find a good solution, I created a manual procedure that I am using to solve the problem:

  1. Turn off the Internet connection;
  2. Ask the last.fm client to scrobble the tracks;
  3. Since it cannot scrobble the tracks because the lack of connection, the tracks (with the multiple occurrences) are kept in the local history (C:\users\windows_user\Local Settings\Application Data\Last.fm\Client\lastfm_user_submissions.xml);
  4. Close the Last.fm client;
  5. Run the ruby script bellow that I have written that excludes the duplicates tracks from the local history xml file;
    • ruby scrobbler_fix.rb "C:\users\windows_user\Local Settings\Application Data\Last.fm\Client\lastfm_user_submissions.xml"
  6. Connect to the internet;
  7. Open the last.fm client and scrobble the history;
  8. Check the tracks page to see if everything was ok.

As usual, I kept the source code in my utility scripts repository at GitHub, but the code is listed bellow as well. I hope I find a better solution soon, because this problem even with this procedure sucks!

# Script to remove duplicate items from last.fm submission xml file
# author: Douglas Fernando da Silva

require "rexml/document"
include REXML

history = nil
File.open(ARGV[0]) do |file|
history = Document.new(file)
tracks_to_remove = []

last_timestamp = nil
history.elements.each("submissions/item") do |element|
ts = element.elements["timestamp"].text
if ts == last_timestamp
puts element.elements["track"].text
last_timestamp = ts

for i in 0..tracks_to_remove.length-1 do
element_to_rm = tracks_to_remove[i]
element = history.elements["submissions/item"].parent.delete(element_to_rm)


formatter = REXML::Formatters::Default.new
File.open(ARGV[0], "w") do |result|
formatter.write(history, result)


Monday, June 22, 2009

Ruby script for setting lyrics in iTunes Library tracks

One of the cool features of the iPod Touch and iPhone is that is possible to see the lyrics of a track while it’s playing. The picture bellow shows that.


But the problem is that you have to insert the lyrics manually using the iTunes, for each track you have to use the “Get Info” option and then set the track, just like the pictures bellow.


That’s a problem, because if you have hundreds of tracks, it will take lots of clicks and time. In order to solve that problem, I created a Ruby script that tries to set lyrics for all the tracks in my iTunes Library.

The script interacts with the iTunes COM components to get all the tracks in my Library, after that, it uses the LyricWiki web service to get the lyrics for each track and set to it.

The script works only on Windows and it’s available in my github repository:



Now, every time I get a new album, all I have to do is to run the script again,  like the following images :

The script code is also bellow:

require 'win32ole'
require 'soap/wsdlDriver'
require 'iconv'

class ITunesHelper
def initialize(lyrics_service)
@itunes = WIN32OLE.new('iTunes.Application')
@tracks = @itunes.LibraryPlaylist.Tracks
@lyrics_service = lyrics_service

def show_all_tracks
@tracks.each do |track|
print track.Name() + "\n"

def set_all_lyrics
puts "Number of tracks to be analyzed: #{@tracks.count}"
index = 1
@tracks.each do |track|
if self.is_lyrics_empty(track.lyrics)
lyrics = @lyrics_service.get_lyrics(track.Artist(), track.Name())

if self.is_lyrics_empty(lyrics)
self.print_track(index, @tracks.count, track, "lyrics WAS NOT found")
track.lyrics = @lyrics_service.get_lyrics(track.Artist(), track.Name())
self.print_track(index, @tracks.count, track, "lyrics WAS found and set")
self.print_track(index, @tracks.count, track, "already had lyrics")
index = index + 1

def is_lyrics_empty(lyrics)
lyrics.empty? or lyrics == "Not found"

def print_track(index, total, track, result)
puts "Track #{index}/#{total}: #{track.Artist()} - #{track.Name()}: #{result}"

class LyricsServiceProxy
def initialize
@driver = SOAP::WSDLDriverFactory.new("http://lyricwiki.org/server.php?wsdl").create_rpc_driver
puts "Lyrics web service initialized"

def get_lyrics(artist, track_name)
Iconv.iconv("LATIN1", "UTF-8", @driver.getSong(artist, track_name).lyrics).to_s

itunesH = ITunesHelper.new(LyricsServiceProxy.new)


Friday, June 12, 2009

Powershell script for converting Word documents to PDF format

In my job, we only send documents to clients in pdf formats. Sometimes we have to send several documents at once and it takes a good time to open each one and save it as a pdf, it’s not a very pleasant task.

In order to minimize this problem, I’ve created a simple powershell script that opens a Word document and saves it as pdf without any user interaction. With such script, I can convert a lot of files at once, just like this:

ls . *.doc* –Recurse | %{ ~\scripts\doc2ps1.ps1 $_.fullname }

With this command, I list recursively each doc/docx file in a directory and save each one as PDF.

By default, Office 2007 apps are not able to save files as PDF, so you have to install the add-in from Microsoft first to use the script. The add-in is available here.

Bellow is the script, it’s also available in my PowerShell scripts repository at GitHub (http://github.com/dougfernando/utility-scripts/tree/master) as doc2pdf.ps1.

param (
[string]$source = $(Throw "You have to specify a source path."))

$extensionSize = 3
if ($source.EndsWith("docx")) {
$extensionSize = 4

$destiny = $source.Substring(0, $source.Length - $extensionSize) + "pdf"
$saveaspath = [ref] $destiny
$formatPDF = [ref] 17

$word = new-object -ComObject "word.application"
$doc = $word.documents.open($source)
$doc.SaveAs($saveaspath, $formatPDF)

echo "Converted file: $source"

ps winword | kill

An important note, the script closes every Word instance in the end, so save any work previous using it.