155 lines
5.3 KiB
Python
Executable File
155 lines
5.3 KiB
Python
Executable File
#!/usr/bin/env python
|
|
#
|
|
# 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>
|
|
|
|
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'],
|
|
time = get_status['time'].split(":")[0] if get_status['time'] else '',
|
|
volume = get_status['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 :
|