#!/usr/bin/env python # Back up the state of MPD (current playlist and so on) import ast import datetime import os import re import sys import pprint import mpd from optparse import OptionParser from subprocess import Popen, PIPE parser = OptionParser() parser.add_option("-s", "--server", dest="host", help="set the MPD hostname (default: localhost)", metavar="HOST") parser.add_option("-p", "--port", dest="port", help="set the MPD port (default: 6600)", metavar="6600") parser.add_option("-d", dest="dest", help="save or load the backup to directory ", metavar="DIR") parser.add_option("-l", dest="load", action="store_true", help="load the last backup") parser.add_option("-f", "--file", dest="load_file", help="load the backup in file ", metavar="FILE") (options, args) = parser.parse_args() host = options.host or 'localhost' port = options.port or '6600' #backup_dir = options.dest or '/var/lib/mpd/backups' backup_dir = options.dest or '/home/tim/.mpd/backups' now = datetime.datetime.now().strftime("%Y%m%d@%H:%M") ps = Popen(['ps', 'ax'], shell=False, stdout=PIPE).communicate()[0] if not re.search("\d+\s+mpd\s+", ps): sys.exit() client = mpd.MPDClient() client.timeout = 10 client.idletimeout = 10 client.connect(host, port) # Get the contents of the most recent backup all_backups = [os.path.join(backup_dir, f) for f in os.listdir(backup_dir) if f.startswith('mpd_state')] last_backup = max(all_backups, key=os.path.getctime) if all_backups else [] backup_output = open(last_backup).read() if last_backup else '' if options.load or options.load_file: # Override the last backup if the user desires if options.load_file: if not os.path.exists(options.load_file): sys.exit("No such file: " + options.load_file) backup_output = open(options.load_file).read() # load file into backup_output backup = ast.literal_eval(backup_output) # Check server and port if backup['src'] != host+":"+port: if not sys.stdin.isatty(): sys.exit("Error: Backup is from a different server or port.") c = raw_input("Error: Backup is from a different server or port. Continue? (y/N) ").rstrip('\n') if c != 'Y' and c != 'y': sys.exit("Exiting.") # Clear playlist client.clear() # Load songs for song in backup['playlist']: try: client.add(song) except mpd.CommandError as e: print("File not found: %s" % e) pass # Parse and communicate extra info if 'repeat' in backup: client.repeat(backup['repeat']) if 'random' in backup: client.random(backup['random']) if 'single' in backup: client.single(backup['single']) if 'consume' in backup: client.consume(backup['consume']) id = None if 'current' in backup: for song in client.playlistinfo(): if song['file'] == backup['current']: id = song['id'] print song['file'] print id next if not id: print "Error: Can't find currently-playing song on playlist." if 'time' in backup and id: client.seekid(id, backup['time']) if 'state' in backup: if backup['state'] == 'play': client.play() elif backup['state'] == 'pause': client.pause(1) elif backup['state'] == 'stop': client.stop() # setvol returns an error, for some reason # mpd.CommandError: [52@0] {setvol} problems setting volume #if 'volume' in backup: # client.setvol(backup['volume']) else: # Get current state get_status = client.status() get_playlist = client.playlistinfo() get_currentsong = client.currentsong() state = {} playlist = [] for song in get_playlist: playlist.append(song.get('file', '')) state = dict( playlist = playlist, src = host+":"+port, consume = get_status.get('consume', ''), current = get_currentsong.get('file', ''), time = get_status.get('time', '').split(":")[0], random = get_status.get('random', ''), repeat = get_status.get('repeat', ''), single = get_status.get('single', ''), state = get_status.get('state', ''), volume = get_status.get('volume', ''), ) pp = pprint.PrettyPrinter(indent=4) current_output = pp.pformat(state) # We don't care if the volume or currently-playing song has changed, only # the playlist strip = re.compile(r'\s*?[\'\"](consume|current|random|repeat|single|state|time|volume)[\'\"]:\s*?[\'\"].*?[\'\"],?'); curr = strip.sub('', current_output) prev = strip.sub('', backup_output) # Only copy if the current playlist has changed if curr != prev: new_backup_file = os.path.join(backup_dir, 'mpd_state' + "-" + now) # Write backup new_backup = open(new_backup_file, 'w') new_backup.write(current_output) new_backup.close() client.close() client.disconnect() # vim: set expandtab shiftwidth=4 softtabstop=4 textwidth=79: