utility-scripts/backup_mpd_playlist.py

155 lines
5.3 KiB
Python
Raw Normal View History

2015-06-14 23:58:20 +00:00
#!/usr/bin/env python
2015-06-15 07:43:46 +00:00
#
# Back up the state of MPD (current playlist and so on); this allows you
# to recover when you accidentally clear your playlist.
#
# This uses the python-mpd2 library from <https://pypi.python.org/pypi/python-mpd2>:
# sudo pip3 install python-mpd2
#
# I run it from cron every 20 minutes or so (as user mpd, but you can
# run it as any user, passing in the backup directory with -d):
# cat /etc/cron.d/mpd_backup
# */20 * * * * mpd [ -x /var/lib/mpd/backup_mpd_playlist.py ] && python /var/lib/mpd/backup_mpd_playlist.py
#
# By Timothy Allen <tim@treehouse.org.za>
2015-06-14 23:58:20 +00:00
import ast
import datetime
import os
import re
import sys
import pprint
import mpd
from optparse import OptionParser
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 <DIR>", 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 <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'
now = datetime.datetime.now().strftime("%Y%m%d@%H:%M")
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["file"])
state = dict(
playlist = playlist,
src = host+":"+port,
consume = get_status['consume'],
current = get_currentsong['file'] if get_currentsong['file'] else '',
random = get_status['random'],
repeat = get_status['repeat'],
single = get_status['single'],
state = get_status['state'],
2015-06-15 00:10:10 +00:00
time = get_status['time'].split(":")[0] if get_status['time'] else '',
2015-06-14 23:58:20 +00:00
volume = get_status['volume'],
)
pp = pprint.PrettyPrinter(indent=4)
current_output = pp.pformat(state)
2015-06-15 00:12:19 +00:00
# We don't care if the volume or currently-playing song has changed, only the playlist
2015-06-14 23:58:20 +00:00
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
2015-06-15 06:48:49 +00:00
if curr != prev:
2015-06-14 23:58:20 +00:00
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()
2015-06-15 00:12:19 +00:00
# vim: set expandtab shiftwidth=4 softtabstop=4 :