Niko Matsakis wrote:

>I have a program which identifies duplicates in iTunes and attempts  to delete 
>them automatically.  It seems to work like a charm, except  for one problem: 
>once I have identified the set of songs to delete, I  have to go through them 
>one-by-one and delete them.  So, if I have  accumulated the database_IDs for a 
>set of songs into a list  'songdbids', I do something like:
>
>       allsongs = app ('iTunes').sources['Library'].playlists 
> ['Library'].tracks
>       for songdbid in songdbids:
>               allsongs.filter (its.database_ID == songdbid).get()[0].delete ()
>
>This works fine when I test on small music collections, but when I  try to run 
>it across my full set of music it takes forever (as you  might imagine).

Running a filter test across all tracks is expensive. On a large playlist, 
doing it repeatedly is going to be murder. So this is the operation you want to 
minimize/eliminate.


>Now, the number one rule of appscript performance which has been  drilled into 
>my head is to avoid AppleEvents like the plague.

Yes, although dispatching the events themselves are relatively cheap these days 
(context switching in OS X doesn't suck like it did in OS 9); it's the cost of 
packing and unpacking their payloads, evaluating queries against the 
application's object model, etc. that sucks up most of the cycles. Of course, 
the fewer events you send, the less packing/unpacking/querying/etc. is going to 
be done as well, so it all pans out in the end. :)


>To  that end, it seems like what I want to do is something like:
>
>               allsongs.filter (its.database_ID in songdbids]).delete ()
>
>except I don't think that the 'in' operator works,

See ch.8 of the appscript manual for a list of supported comparison forms. Not 
all Python operators were amenable to overloading; in this case you need to use 
'its.database_ID.isin(songdbids)'.


>and it looks like  I'm missing a get() in there in any case.

Depends what you're trying to get. iTunes scripting interface is pretty 
extensive, but a lot of the implementation is rather crude and many commands 
won't work on more than one object at a time. So the above might work as-is, 
but it probably won't, in which case you need to get a list of track references 
and tell iTunes to delete each individually:

    for song in allsongs.filter (its.database_ID.isin(songdbids)): song.delete()

>Can anyone think of a better way to do this?  Perhaps I am on the  wrong track 
>altogether in working with database_IDs?

They seem to be the obvious way to check for dupes. You just don't want to 
perform multiple filter tests across your entire playlist to locate tracks by 
DB ID. But a single test shouldn't be too punishing (assuming iTunes can manage 
it ok).


>I can't select  by album or artist, however, as that would delete both copies 
>of the  duplicated album (naturally).

And you'd still be running tons of filter tests to do it.

Anyway, try the fixes shown above. If it still doesn't work out well for you, 
another option would be to try an alternative approach like:

tracksRef = app('iTunes').playlists['Library'].tracks
d = {}
for id, track in zip(tracksRef.database_ID(), tracksRef()):
    d[id] = d.get(id, []) + [track]
for dupes in [i for i in d.values() if len(i) > 1]:
    for dupe in dupes[1:]:
        dupe.delete()

No idea how that'll compare; getting a list of references to every single track 
won't be cheap due to packing/unpacking costs. You just have to experiment.

HTH

has
-- 
http://freespace.virgin.net/hamish.sanderson/
_______________________________________________
Pythonmac-SIG maillist  -  Pythonmac-SIG@python.org
http://mail.python.org/mailman/listinfo/pythonmac-sig

Reply via email to