Hack:mpdipod
From Music Player Daemon Community Wiki
Small script to sync up ipods to mpd playlists.
Requires libgpod's python bindings and python-mpd.
#!/usr/bin/env python
# Python mpd syncer
# Copyright (c) 2009 Matan Peled <chaosite@gmail.com>
import os
# Constants
# iPod mount point (make sure it's properly mounted)
MOUNT_POINT = '/mnt/ipod'
# Safety factor, in gigabytes
SIZE_FUDGE = 0.4
# mpd host, port
MPD_CONNECTION = ('localhost', 6600, )
# mpd root directory
MPD_ROOT = os.path.expanduser('~/mp3')
# Playlists (mpd_name, ipod_name)
PLAYLISTS = [('ipod_Rock', 'Rock'),
]
# Covers dir
COVERS_DIR = os.path.expanduser('~/.covers/')
# Code
import gpod
import mpd
def ipod_capacity(mountpoint = MOUNT_POINT):
if not os.path.exists(mountpoint):
raise ValueError("Mount point does not exist")
device = gpod.itdb_device_new()
gpod.itdb_device_set_mountpoint(device, mountpoint)
info = gpod.itdb_device_get_ipod_info(device)
return int((info.capacity - SIZE_FUDGE) * 1024 * 1024 * 1024)
def compare_tracks(a, b):
return (a['size'] == b['size'] and
a['title'] == b['title'] and
a['artist'] == b['artist'] and
a['album'] == b['album'])
class FreeSpaceException(Exception): pass
class iPod(object):
def __init__(self, path = MOUNT_POINT):
self.path = path
self.db = gpod.Database(self.path)
# simplistic, but OK
def used_space(self):
size = 0
for track in self.db.Master:
size += track['size']
for track in self.db.Podcasts:
size += track['size']
return size
def free_space(self):
return ipod_capacity(self.path) - self.used_space()
def sync_playlist(self, name, tracks):
for playlist in self.db.Playlists:
if playlist.name == name:
for i, track in enumerate(tracks):
if i >= len(playlist):
playlist.add(track)
continue
if not compare_tracks(track, playlist[i]):
to_del = playlist[i]
playlist.remove(to_del)
self.db.Master.remove(to_del)
playlist.add(track, pos = i)
if len(playlist) - 1 > i:
for track in playlist[i + 1:]:
playlist.remove(track)
self.db.Master.remove(track)
return True
playlist = self.db.new_Playlist(title = name)
for track in tracks:
playlist.add(track)
return True
def check_freespace(self, tracks):
size = sum([ track['size'] for track in tracks ])
return size < self.free_space()
def close(self):
self.db.copy_delayed_files()
self.db.close()
def track_factory(self, filename):
_track = gpod.Track(filename)
for track in self.db.Master:
if compare_tracks(_track, track):
return track
t = self.db.new_Track(filename = filename)
t.set_coverart_from_file(os.path.join(COVERS_DIR, t['artist'],
"%s.jpg" % (t['album'], )))
return t
# mpd code (move to different file when it gets too complex)
def get_filenames(mpd_playlist):
client = mpd.MPDClient()
client.connect(*MPD_CONNECTION)
return [ os.path.join(MPD_ROOT, filename)
for filename in client.listplaylist(mpd_playlist) ]
def sync(ipod, playlists):
for mpd_playlist, ipod_playlist in playlists:
tracks = [ ipod.track_factory(filename)
for filename in get_filenames(mpd_playlist) ]
if not ipod.check_freespace(tracks):
raise FreeSpaceException("Not enough free space!")
ipod.sync_playlist(ipod_playlist, tracks)
return True
def main():
ipod = iPod(MOUNT_POINT)
sync(ipod, PLAYLISTS)
ipod.close()
if __name__ == '__main__':
main()
