Adium & Camino

I’m a latecomer to Instant Messaging. I’ve only just discovered it, and part of that is because I don’t have many people to talk to over it. I have written one thing though, and that’s an improvement to megalogeek’s %_camino script. You can download it from Adium Xtras: Camino Current Page. Source follows after the jump!

1
2
3
4
5
6
7
8
on substitute()     
    tell application "Camino"         
        URL of window 1         
        set myURL to the result         
        set myTitle to name of window 1     
    end tell     
    return "<HTML><A HREF=\"" & myURL & "\">" & myTitle & "</A></HTML>" 
end substitute

Open Terminal Here (Improved).

I have been using a Toolbar Script called “Open Terminal Here” for a while now (thanks Marc), and the one thing about it that annoyed me was that when Terminal was not running, it would start the application, and then create a second window with my path in it. I’ve fixed this, by altering the code slightly. Note that the only changes I have made are to the last function, process_item(). I have just inserted a check for if the program is running, and if it isn’t, I call the same do script, but with in window 1.

– when the toolbar script icon is clicked on run     tell application “Finder”         activate         try             set this_folder to (the target of the front window) as alias         on error             set this_folder to startup disk as alias         end try         my process_item(this_folder)     end tell end run – This handler processes folders dropped onto the toolbar script icon on open these_items     repeat with i from 1 to the count of these_items         set this_item to item i of these_items         my process_item(this_item)     end repeat end open – this subroutine processes does the actual work on process_item(this_item)     tell application “System Events”         try             get process “Terminal”                          tell application “Terminal”                 activate                 do script “cd " & (quoted form of POSIX path of this_item)             end tell         on error – Terminal Not running, launch and run in first window.             launch application “Terminal”             tell application “Terminal”                 activate                 do script “cd " & (quoted form of POSIX path of this_item) in window 1             end tell         end try     end tell end process_item

Find Missing Tracks

Continuing on from my last programming related post, I’ve been giving some thought to the opposite problem: when tracks are in the iTunes Library, but the file they refer to is not there. iTunes marks these with an exclaimation mark in front of the track, but only if it realises they are in fact missing. Often, just clicking on a track does not mean iTunes knows that track is missing. A Get Info on such a track prompts the user for a choice between removing the track from the library, or locating it. It might be possible to use the following AppleScript paradigm to get a list of the tracks that are missing:

1
2
3
4
5
6
7
8
9
tell application "iTunes"
    set theTracks to every track in playlist 1
    set missing to {}
    repeat with theTrack in theracks
        if location of theTrack is [unknown] then
            set missing to missing + theTrack
        end if
    end repeat
end tell

Of course, that would be dead slow, as it relies on a couple of AppleScript events for each track. (Also, it may not work, but some sort of try - else - end try clause might work.) A better solution might be to sort through the iTunes Music Library.xml file.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
    import os
        
    data = open("iTunes Music Library.xml").readlines()
    missing = []
        
    for line in data:
        if "<location>" in line:
            #extract location
            location = line.split("/Volumes")[1].split("</location>")[0]
            #check if location is not a file
            if not os.path.isfile(location):
                missing.append(location)

Assuming that the mount point is the same as the last time the XML file was written (and iTunes seems to be pretty clever about working out things like /Volumes/Media vs. /Volumes/Media-1, then your list missing should contain a list of files that are in the library, but not where iTunes expects them to be. It might be possible to then match these two lists (from this script), and see which ones are likely to be ‘modified tracks’, where one user has changed the track name, artist or album. It may even be worthwhile having a list of all track locations, and seeing if there are any duplicates, that will solve the problem of duplicates int he library that are the same file. I’d like to have python parse the XML file, and create data structures reflecting the XML data - but skip everything but the database ID and filename, as these are the things that we really need - but I haven’t had much luck with this type of process in the past. I jsut had a thought on how to do it, though.

Notification from AppleScript Studio

Christoph asked me for some advice on how to get notification on iTunes track changes in a comment, I said I would write a bit of a tutorial on how I got it to work. First thing was to create an AppleScript Studio program - you will need to have the Developer Tools installed for this. Now, open the main.m file. It should look something like the following:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
    #import <mach-o/dyld.h>
        
    extern int _NSApplicationMain_(int argc, const char *argv[]);
        
    int main(int argc, const char *argv[])
    {
        if (NSIsSymbolNameDefined("_ASKInitialize"))
        {
        NSSymbol *symbol = NSLookupAndBindSymbol("_ASKInitialize");
        if (symbol)
        {
        void (*initializeASKFunc)(void) = NSAddressOfSymbol(symbol);
            if (initializeASKFunc)
        {
        initializeASKFunc();
        }
        }
        }
        
    return _NSApplicationMain_(argc, argv);
    }

The file needs to have the following elements. At the start, the following imports are required:

1
2
3
    #import <Foundation/NSDistributedNotificationCenter.h>
    #import <Foundation/NSString.h>
    #import <AppKit/NSAppleScriptExtensions.h>

This should be all of the imports that are required - although I also had stdio.h for a while for testing. After the declaration of the NSApplicationMain, I put the following in:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
    @interface iTunesConnection : NSObject
     - (id) init;
    @end
    
    @implementation iTunesConnection
     - (id) init {
        NSDistributedNotificationCenter *nc = [NSDistributedNotificationCenter defaultCenter];
        [nc addObserver:self
               selector:@selector(updateNow:)
                   name:@"com.apple.iTunes.playerInfo"
                 object:nil];
    
        return self;
    }
    
     - (void) updateNow:(NSNotification *)notification {
        NSString *updateScript = [NSString stringWithFormat:@"tell application \nend tell"];
        NSAppleScript *as = [[[NSAppleScript alloc] initWithSource:updateScript] autorelease];
        [as executeAndReturnError:nil];
    }    
    @end

Copy and paste the stuff above - there is some that goes over the line in my browser, but it’s all there if you drag out the selection. You may need to change a couple of things depending on what you want to do. For instance, the @"tell application \nend tell" section needs to be the applescript code that is run whenever a new iTunes track is started, or iTunes is paused. You could fit the entire application into this place, but what I did was have a hidden button that this script “presses”, triggering the update process. Finally, I added the following declaration just after the first brace in the main function declaration:

1
2
    iTunesConnection *connection;
    connection = [[iTunesConnection **alloc**] init];

This gets everything running. I would suggest putting the following into the applescript string to test it and see that it works:

1
    @"tell application \"Finder\"\ndisplay dialog \"iTunes Changed\"\nend tell"

You need to have \" whenever your script would have a ", and \n wherever it would have a newline.

Finder AppleScript Freeze

While trying to bugfix a couple of issues in iTunesRater tonight, I found that the script stopped working altogether. I narrowed it down to a section of the code where I was asking the Finder to do something, and it was just freezing. I thought that maybe the latest security update had done something, but changing users proved that otherwise. It was something with my setup. I found it probably had to do with having a device mounted (the memory card in my Palm Zire), but having removed the device without unmounting it. The Finder had almost crashed, and wouldn’t respond to any AppleScript commands. Incidentally, I suspect that AppleScript is smart enough to not actually send any message (even an empty one) to an application when there is no command (ie, just a tell application/end tell clause with nothing between it).

Preferences for iTunesRater

I’m working on saved preferences for iTunesRater, after a timely hint on macosxhints.com about Applescript Studio and preferences. The program will load them fine, but just doesn’t seem to save them. Too tired now to care.

Age in AppleScript

Someone on the AppleScript Studio Mailing List wanted to know how to get an age; here’s my take:

set birthday to date “Thursday, 1 April 1976 12:00:00 AM” set age to (year of (current date)) - (year of birthday) if month of (current date) < month of birthday then     set age to age - 1 else if month of (current date) = month of birthday then     if day of (current date) < day of birthday then         set age to age - 1     end if end if get age

Blog with ecto.scpt

Yesterday I talked about blogging from my Palm Zire 21.

Here is the complete AppleScript for Blog with ecto.scpt. Copy this, paste it into Script Editor, and save it to ~/Library/Scripts/Folder Action Scripts/Blog with ecto.scpt

Then right-click on the folder you want monitored - I use ~/Documents/Palm/Matthew Schinckel/NoteTaker/Blog and away you go. For best results, use with MacNoteTaker. Obviously, ecto is required.

All code is ©2004 Matthew Schinckel, but may be freely used with attribution.

Note: the formatting is removed, because ecto wasn’t quite doing it right, but Script Editor will figure it out.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
on adding folder items to this_folder after receiving added_items
– Set this to true to automatically post the item(s)
– Leave as false so you can check the data.
set autopost to false
repeat with each_file in added_items
tell application “Finder”
set filename to (name of each_file) as text
open for access each_file
set entry_body to (read each_file)
close access each_file
end tell
try
set entry_title to filename
set date_stamp to word 3 of filename & “/” & word 2 of filename ¬
& “/” & word 1 of filename & ” ” & word 4 of filename
set entry_date to false
set entry_date to date date_stamp
set entry_title to first paragraph of entry_body
set entry_body to text ((count characters of entry_title) + 2) ¬
thru end of entry_body
end try
–—— You WILL need to change the next line!
set my_blog to “Life According to Matt”
tell application “ecto”
set my_doc to make new document at front with properties ¬
{entry title:entry_title, current blog:my_blog, ¬
body text:entry_body}
if false is not entry_date then
set entry date of my_doc to entry_date
end if
if true is autopost then
do post my_doc
end if
end tell
end repeat
end adding folder items to

Export Artwork and Playlists

Exporting Artwork

Seeing as how XBMC won’t read the id3 tags from AAC files, I had to grab the artwork from each album and place it in a file (folder.jpg/folder.png) inside the album folder. This was actually easier with Windows (aargh!), since iTunes for Windows exports album art as BMP files, which can then be processed. Still, it took me most of the afternoon to do that by hand. I had had a bash at doing it with an AppleScript, but could not work out how to get the data from a <<class PNG/JPEG *>> structure into an image file. Until I came across a script that does that. It was then a trivial matter to make it so that it worked for multiple files, and only processed those files it needed to, and ignored those without artwork. Tip: iTunes will give you an Out Of Memory Error if you try to get the data from a song that has no artworks! (Took me a while to figure this out!) So, the finished script can be found below. I optimised it a bit, but it still takes a while to run, especially if you run it on lots of tracks. Since it’s not something you’ll want to run every day, this shouldn’t be too much of a problem.

Exporting Playlists

I also thought it would be neat to get the playlists I use in iTunes (I use mostly smart playlists, such as 50’s - 00’s Music, Not Played Today, Recently Added and so on), and use them on XBMC. So, I wrote a script that does this, and then ftp’s the resulting files to the xbox. You will need to edit the first few lines: it should be obvious what needs to go where. Anyway, you can download Export Artwork and Export Playlists. You can also download Bracket Change, a small script that swaps the round & square brackets in a track name. (Right-click and choose to download them, else they will just open in your browser window). All of these files should go into ~/Library/iTunes/Scripts (User only) or /Library/iTunes/Scripts (System-wide) for best effect. Note: The first Script is no longer required, since more recent builds of XBMC can handle the data from AAC files.

Sharing iTunes Songs

I finally got around to downloading the 10.3.4 OS X update, SharePoints, and Xbox Media Centre (XBMC). My aim for some time has been to share my iTunes library to my Xbox, which is connected up to the TV and Stereo in the lounge.

The MacOS X update provided (apparently) updated Samba sharing, but I still couldn’t get XMP (Xbox Media Player - the previous incarnation of XBMC) to connect to the SMB share I had set up, with all of my MP3s on it. SharePoints fixed that - all I had to do was create a publicly available share.

Setting up XBMC was a breeze, and it even has a cool interface! UnleashX is (almost) banished from my system, and XBMC is my main Dash.

Then I discovered that XCMC can play AAC files! So, I started (and have not yet finished) converting all of my MP3s to AAC. And not just converting, but re-importing, at a bitrate of 128 (small file size, but apparently equivalent to 256 in MP3). See this hint for some tips (and my comments).

I also wanted to re-jig how I had set up sharing my iTunes songs between users - I had a couple of links to the relevant files in each user’s ~/Music/iTunes, and all files were located in ~shared/Music (which all of admin can RW, and all of the world can R). I needed to have seperate Library files, so that we can have our own ratings (I hate Abba, and she isn’t that big a fan of Paul Kelly, for instance). So, I knocked up a script that scanned the ~shared/Music folder and compared any files found to the iTunes LIbrary. If they weren’t in it, it added them.

Here we go:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
set _tracks to “”
tell application “iTunes”
    set sel to tracks of playlist 1
    –set sel to get a reference to selection
    repeat with t in sel
        set _tracks to _tracks & “
” & (location of t as string)
    end repeat
end tell

tell application “Finder”
    set _files to “”
    set _library to “Macintosh HD:Users:Shared:Music”
    set _artists to folders of folder _library
    repeat with _artist in _artists
        set _albums to folders of _artist
        repeat with _album in _albums
            set temp to files of _album
            repeat with _file in temp
                set _files to _files & “
” & _file
            end repeat
        end repeat
    end repeat
end tell
set _missing to “”
repeat with para in paragraphs of _files
    set para to para as string
    if para is not in _tracks then
        set _missing to _missing & “
” & para
    end if
end repeat

tell application “iTunes”
    repeat with para in paragraphs of _missing
        set para to para as string
        if para is not “” then
            set _file to para
            add _file
        end if
    end repeat
end tell

Which worked, but was a bit slow. So I came up with the following system instead:

Create a file called ~/.last_check

Enter the following into a script, and then run it. (I’ve had to modify some lines to get it to look good - particularly the fp=os.popen… line).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#! /usr/bin/env python 
# Check for songs newer than ~/.last_check, and add them to iTunes

import os
import sys

fp = os.popen(
"find ~shared/Music -name *.m?? -newer ~/.last_check")

data = fp.readlines()

if len(data) == 0:
    print "No New Songs."
    sys.exit(0)
    
for line in data:
    filename = "Macintosh HD"+line[:-1]
    filename = filename.replace("/",":")
    print "Adding", filename
    os.system('''osascript < <END
tell application "iTunes"
add "'''+filename+'''"
end tell
END
''')

Should be pretty quick - I only tested it with a few files, but seems to work okay. iTunes is even smart enough to not re-add files that are already there (I think!), so it won’t add duplicates!

My library lives in ~shared/Music, yours may vary!

You will, however, need to use something like Super Remove Dead Tracks if you update one library with a new encoder!

I accept no responsibility if it screws up your iTunes Library file!