#! /usr/bin/env python2 # -*- coding: utf-8 -*- # # script to sync a mpd playlist to a mass storage audio player # # needs # - python-sqlite2 # - eyed3 # - python-argparser # - python-mpd import argparse import os import sys import hashlib import subprocess #from time import time import time from mpd import (MPDClient, CommandError) import shutil # set encoding to utf-8 reload(sys) sys.setdefaultencoding( "utf-8" ) config_dir = '/home/konni/.config/pmsync' music = '/net/media/music' playlist = 'sync' mpdhost = 'innocence' destination = '/run/media/konni/ville/music' verbose = False def _recode(string): string = string.replace('"','') # byte2utf8 # string = unicode( string, "utf-8", errors="ignore" ) # utf2byte string = string.encode('utf-8') return string def main(): global verbose, playlist, mpdhost, destination parser = argparse.ArgumentParser(prog='pmsync') parser.add_argument('--version', action='version', version='%(prog)s 0.1') parser.add_argument('-v', '--verbose', action='store_true', dest='verbose', help='verbose mode', default=False) parser.add_argument('action', metavar='sync | list | import') parser.add_argument('-m', dest='host', metavar='mpdhost', action='store', help='mpd host', default=mpdhost) parser.add_argument('-p', dest='pl', metavar='playlist', action='store', help='playlist to use', default=playlist) parser.add_argument('-d', dest='dest', metavar='destination', action='store', help='destination', default=destination) parser.add_argument('-e', dest='exact', action='store_true', help='exact checking (slow)', default=False) args = parser.parse_args() if args.verbose: verbose = args.verbose print args ## action sync if args.action == 'sync': print '>> syncing \"{0}/{1}\"to \"{2}\"'.format(args.host, args.pl, args.dest) _sync(args.host, args.pl, args.dest, args.exact) ## action list elif args.action == 'list': print '>> listing from \"{0}/{1}\"'.format(args.host, args.pl) _list(args.host, args.pl) ## action import elif args.action == 'import': print '>> importing from \"{0}\" to playlist \"{1}/{2}\"'.format(args.dest, args.host, args.pl) port='6600' CON_ID = {'host':args.host, 'port':port} client = MPDClient() mpdConnect(client, CON_ID) playlistinfo = client.listplaylistinfo(playlist) for dirname, dirnames, filenames in os.walk(args.dest): for filename in filenames: destfile = os.path.join(dirname, filename) path=destfile.replace(args.dest+"/","") found = False for item in playlistinfo: if item['file'] == path: found = True if not found: print '[x] {0}'.format(path) client.add(path) client.rm(args.pl) client.save(args.pl) ## show help if no action called else: parser.print_help() def _sync(host, pl, dest, exact): _checkdir(dest) port='6600' CON_ID = {'host':host, 'port':port} client = MPDClient() mpdConnect(client, CON_ID) playlistinfo = client.listplaylistinfo(pl) ## copy files print '> copy files' for item in playlistinfo: path = item['file'] srcfile=os.path.join(music, path) destfile=os.path.join(dest, path) destdir=os.path.dirname(destfile) if not os.path.isdir(destdir): print '[^] {0}'.format(destdir) os.makedirs(destdir) if not os.path.isfile(destfile): shutil.copyfile(srcfile,destfile) print '[+] {0}'.format(path) else: ## hash based on md5sum or just file size if exact: srchash = hashlib.md5(open(srcfile, 'rb').read()).hexdigest() desthash = hashlib.md5(open(destfile, 'rb').read()).hexdigest() else: srchash = os.path.getsize(srcfile) desthash = os.path.getsize(destfile) if srchash != desthash: shutil.copyfile(srcfile,destfile) print '[o] {0}'.format(path) else: if verbose: print '[ ] {0}'.format(path) print 'done' ## clean files print '> clean files and directories' for dirname, dirnames, filenames in os.walk(dest): for filename in filenames: destfile = os.path.join(dirname, filename) path=destfile.replace(dest+"/","") found = False for item in playlistinfo: if item['file'] == path: found = True; if not found: try: os.remove(destfile) print '[-] {0}'.format(path) except OSError: print 'error removing {0}'.format(destfile) ## clean directories for dirname, dirnames, filenames in os.walk(dest,topdown=False): if os.listdir(dirname) == [] and dirname != dest: try: os.rmdir(dirname) print '[v] {0}'.format(destdir) except OSError: print 'error removing {0} '.format(dirname) print 'done' print '> writing filesystem changes' subprocess.call("/bin/sync") def _list(host, pl): port='6600' CON_ID = {'host':host, 'port':port} client = MPDClient() mpdConnect(client, CON_ID) playlistinfo = client.listplaylistinfo(pl) ## output print '> copy files' prev_album = '' for item in playlistinfo: artist = item['artist'] album = item['album'] if album != prev_album: print '{0}/{1}'.format(artist, album) prev_album = album def _output(artist, album, track, title, symbol='', extra=''): output_format = '{symbol:4}{artist:20.20} :: {album:20.20} :: {track:2d} - {title:25.25} {extra}' print output_format.format(symbol=symbol, artist=artist, album=album, track=track, title=title, extra=extra) def mpdConnect(client, con_id): """ Simple wrapper to connect MPD. """ try: client.connect(**con_id) except SocketError: return False return True def _checkdir(dir): if not os.path.isdir(dir): print "destination not mounted" sys.exit(1) if __name__ == '__main__': main()