aboutsummaryrefslogtreecommitdiffstats
path: root/bin/syncmpdpl
diff options
context:
space:
mode:
Diffstat (limited to 'bin/syncmpdpl')
-rw-r--r--bin/syncmpdpl209
1 files changed, 209 insertions, 0 deletions
diff --git a/bin/syncmpdpl b/bin/syncmpdpl
new file mode 100644
index 0000000..85cf723
--- /dev/null
+++ b/bin/syncmpdpl
@@ -0,0 +1,209 @@
+#! /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()