sampsyo/beets — pull request #742 by tsigo (2014-05-02 00:50:32+00:00)
Title: Add before_item_moved event

This event gets called just before `util.move` with the same arguments as the `item_moved` event.

This might seem useless, but I had a specific use case for it. I still use iTunes to organize and play my music (I know, I know), but beets' organization and renaming really appeals to me, so I wanted to start moving towards that. Problem is, when beets moves a file on the filesystem iTunes picks up the change but then immediately forgets it as soon as the app closes. The next time you play the file it says it can't find it.

I've found the only way to get around this is to tell iTunes, through Applescript, to set the new location for the file after it's moved. I wrote a beetsplugin to do this on each `item_moved` event, but the problem is Applescript provides no good way to look up a file by its path. The only reliable way to find a track by its path is to "add" the file to iTunes. If the file already exists, iTunes returns a reference to the track which we can then update.

Still following? :hand: You can't add a file to iTunes that doesn't exist, even a file it already has in its library, so by the time we get to `item_moved` it's already too late. I needed `before_item_moved` so I can add the file in its old location, store its reference, and then after it's moved tell iTunes its new location.

If anyone's interested, this is the plugin I came up with:

```python
import logging
import subprocess
import appscript
from beets.plugins import BeetsPlugin

# Global logger.
log = logging.getLogger('beets')

# Global variable to store the iTunes track reference of a file before it's moved
track_ref = None

class ItunesMover(BeetsPlugin):
pass

def hfspath(posix):
return appscript.mactypes.File(posix).hfspath

@ItunesMover.listen('before_item_moved')
def before_move(item, source, destination):
global track_ref

# Convert the POSIX path to an HFS one
source_hfs = hfspath(source)

# iTunes fuckery:
#
# We tell iTunes to add the file. Hopefully it's already in iTunes, in
# which case iTunes just returns the track reference. This is the only way
# to look up a file by its path without looping through a bunch of tracks.
track_ref = appscript.app("iTunes").add(source_hfs)

@ItunesMover.listen('item_moved')
def moved(item, source, destination):
global track_ref

try:
track_ref.location.set(hfspath(destination))
track_ref = None
except appscript.CommandError:
log.debug(u"Could not set iTunes location to %s (probably doesn't exist)" % destination)
```