Compare commits

...

2 commits

Author SHA1 Message Date
860ed5015f
Updated dependancies in documentation 2018-08-26 14:31:13 -04:00
20d7d2c6d2
Fixed more code, added includes 2018-08-26 14:26:21 -04:00
3 changed files with 84 additions and 36 deletions

View file

@ -34,5 +34,5 @@ and request your pull requests towards develop.
- Python 3.3 and up. - Python 3.3 and up.
- mutagen - mutagen
- ffmpeg - ffmpeg
- fdkaac

View file

@ -50,7 +50,7 @@ def clearLine():
def sanitize(text): def sanitize(text):
newtext = text newtext = text
newtext = newtext.replace('<', '').replace('>', '').replace(':', '').replace('"', '').replace('|', '').replace('?', '').replace('*', '') newtext = newtext.replace('<', '').replace('>', '').replace(':', '').replace('"', '').replace('|', '').replace('?', '').replace('*', '').replace('$', '')
newtext = newtext.replace('/', '-') newtext = newtext.replace('/', '-')
newtext = newtext.strip() newtext = newtext.strip()
#if newtext != text: #if newtext != text:
@ -59,11 +59,26 @@ def sanitize(text):
# print(" new:", newtext) # print(" new:", newtext)
return newtext return newtext
def getLibrary(path, excludeDirs=[]): def escape(text):
newtext = text
newtext = newtext.replace('$', '\$')
return newtext
def getLibrary(path, libFormat=None, excludeDirs=[], includeDirs=[]):
#print("DEBUG: libFormat={0}".format(libFormat))
global originFormat;
if os.path.isdir(path): if os.path.isdir(path):
for root, directories, filenames in sorted(os.walk(path)): for root, directories, filenames in sorted(os.walk(path)):
if root not in excludeDirs: if root not in excludeDirs:
#if any(root in s for s in includeDirs):
#if [s for s in [root] if any (xs in s for xs in includeDirs)] or len(includeDirs) == 0:
# print("DEBUG: Matched: {0}".format(root))
#print("DEBUG: Found: {0}".format(root))
#if any(root in s for s in includeDirs) or len(includeDirs) == 0:
if [s for s in [root] if any (xs in s for xs in includeDirs)] or len(includeDirs) == 0:
for filename in filenames: for filename in filenames:
#print("DEBUG: filename: {0}".format(filename))
if filename.endswith(libFormat):
yield os.path.join(root, filename) yield os.path.join(root, filename)
# files=[] # files=[]
@ -95,6 +110,16 @@ def getLibrary(path, excludeDirs=[]):
# print("Scan complete!") # print("Scan complete!")
# return files # return files
def ensure_dir(file_path):
directory = os.path.dirname(file_path)
if not os.path.exists(directory):
os.makedirs(directory)
def copy_file(src, dest):
from shutil import copyfile
ensure_dir(dest)
copyfile(src, dest)
def getEmptyDirs(path, excludeDirs=[]): def getEmptyDirs(path, excludeDirs=[]):
if os.path.isdir(path): if os.path.isdir(path):
for root, directories, filenames in sorted(os.walk(path)): for root, directories, filenames in sorted(os.walk(path)):
@ -148,13 +173,11 @@ def getSong(file, orgDir=None, tmpDir=None, dstDir=None):
if song['totaldiscs'] > 1: if song['totaldiscs'] > 1:
song['outPath'] = os.path.join(song['artistName'], song['albumName']) song['outPath'] = os.path.join(song['artistName'], song['albumName'])
song['outFile'] = '{0:d}-{1:02d}-{2}'.format(song['discnumber'], song['outFile'] = '{0:d}-{1:02d}-{2}'.format(song['discnumber'], song['tracknumber'], song['titleName'])
song['tracknumber'],
song['titleName'])
else: else:
if metadata.tags.get("tracknumber") is not None: if metadata.tags.get("tracknumber") is not None:
song['outPath'] = os.path.join(song['artistName'], song['albumName']) song['outPath'] = os.path.join(song['artistName'], song['albumName'])
song['outFile'] = '{0:02d}-{1}'.format(song['tracknumber'], song['titleName']) song['outFile'] = '{0:d}-{1:02d}-{2}'.format(song['discnumber'], song['tracknumber'], song['titleName'])
else: else:
song['outPath'] = os.path.join(song['artistName'], song['albumName']) song['outPath'] = os.path.join(song['artistName'], song['albumName'])
outFile = '{0}'.format(song['titleName']) outFile = '{0}'.format(song['titleName'])
@ -164,6 +187,7 @@ def getSong(file, orgDir=None, tmpDir=None, dstDir=None):
song['targetFile'] = os.path.join(dstDir, song['outPath'], song['outFile']) song['targetFile'] = os.path.join(dstDir, song['outPath'], song['outFile'])
song['originExt'] = os.path.splitext(file)[1] song['originExt'] = os.path.splitext(file)[1]
song['targetExt'] = '.' + targetFormat song['targetExt'] = '.' + targetFormat
song['targetDir'] = targetDir
return song return song
else: else:
@ -202,14 +226,16 @@ def cleanLibrary(rootDir, excludeDirs=[], act=False, verbose=0):
#print("Processing Complete!") #print("Processing Complete!")
def renameLibrary(rootDir, excludeDirs=[], act=False, verbose=0): def renameLibrary(rootDir, libFormat, excludeDirs=[], includeDirs=[], act=False, verbose=0):
global config global config
if excludeDirs is None: if excludeDirs is None:
excludeDirs=[] excludeDirs=[]
if includeDirs is None:
includeDirs=[]
print("Renaming:", rootDir) print("Renaming:", rootDir)
for file in getLibrary(rootDir, excludeDirs): for file in getLibrary(rootDir, libFormat, excludeDirs, includeDirs):
if (os.path.isdir(os.path.dirname(file)) and os.path.isfile(file)): if (os.path.isdir(os.path.dirname(file)) and os.path.isfile(file)):
clearLine() clearLine()
print("Processing:", os.path.dirname(os.path.dirname(file)), next(spinner), end="\r") print("Processing:", os.path.dirname(os.path.dirname(file)), next(spinner), end="\r")
@ -239,14 +265,16 @@ def renameLibrary(rootDir, excludeDirs=[], act=False, verbose=0):
clearLine() clearLine()
#print("Processing Complete!") #print("Processing Complete!")
def findUntagged(rootDir, excludeDirs=[], verbose=0): def findUntagged(rootDir, libFormat, excludeDirs=[], includeDirs=[], verbose=0):
global config global config
if excludeDirs is None: if excludeDirs is None:
excludeDirs=[] excludeDirs=[]
if includeDirs is None:
includeDirs=[]
print("Find Untagged:", rootDir) print("Find Untagged:", rootDir)
for file in getLibrary(rootDir, excludeDirs): for file in getLibrary(rootDir, libFormat, excludeDirs, includeDirs):
if (os.path.isdir(os.path.dirname(file)) and os.path.isfile(file)): if (os.path.isdir(os.path.dirname(file)) and os.path.isfile(file)):
clearLine() clearLine()
print("Processing:", os.path.dirname(os.path.dirname(file)), next(spinner), end="\r") print("Processing:", os.path.dirname(os.path.dirname(file)), next(spinner), end="\r")
@ -260,14 +288,16 @@ def findUntagged(rootDir, excludeDirs=[], verbose=0):
clearLine() clearLine()
#print("Processing Complete!") #print("Processing Complete!")
def findNew(rootDir, tmpDir, dstDir, dstFormat, excludeDirs=[], verbose=0): def findNew(rootDir, libFormat, tmpDir, dstDir, dstFormat, excludeDirs=[], includeDirs=[], verbose=0):
global config global config
if excludeDirs is None: if excludeDirs is None:
excludeDirs=[] excludeDirs=[]
if includeDirs is None:
includeDirs=[]
print("Find New Media") print("Find New Media")
for file in getLibrary(rootDir, excludeDirs): for file in getLibrary(rootDir, libFormat, excludeDirs, includeDirs):
if (os.path.isdir(os.path.dirname(file)) and os.path.isfile(file)): if (os.path.isdir(os.path.dirname(file)) and os.path.isfile(file)):
clearLine() clearLine()
print("Processing:", os.path.dirname(os.path.dirname(file)), next(spinner), end="\r") print("Processing:", os.path.dirname(os.path.dirname(file)), next(spinner), end="\r")
@ -292,15 +322,17 @@ def findNew(rootDir, tmpDir, dstDir, dstFormat, excludeDirs=[], verbose=0):
#clearLine() #clearLine()
#print("Processing Complete!") #print("Processing Complete!")
def syncWorking(tmpDir, excludeDirs=[], act=False, verbose=0): def syncWorking(tmpDir, libFormat, excludeDirs=[], includeDirs=[], act=False, verbose=0):
global config global config
if excludeDirs is None: if excludeDirs is None:
excludeDirs=[] excludeDirs=[]
if includeDirs is None:
includeDirs=[]
print("Sync Target Media") print("Sync Target Media")
for file in getLibrary(tmpDir, excludeDirs): for file in getLibrary(tmpDir, libFormat, excludeDirs, includeDirs):
if (os.path.isdir(os.path.dirname(file)) and os.path.isfile(file)): if (os.path.isdir(os.path.dirname(file)) and os.path.isfile(file)):
clearLine() clearLine()
print("Processing:", os.path.dirname(os.path.dirname(file)), next(spinner), end="\r") print("Processing:", os.path.dirname(os.path.dirname(file)), next(spinner), end="\r")
@ -315,13 +347,14 @@ def syncWorking(tmpDir, excludeDirs=[], act=False, verbose=0):
filename = os.path.basename(file) filename = os.path.basename(file)
file_ext = os.path.splitext(filename)[1] file_ext = os.path.splitext(filename)[1]
if not os.path.isfile(song['targetDir'] + song['originExt']): if not os.path.isfile(song['targetFile'] + song['originExt']):
print("Sync:", file) print("Sync:", file)
if verbose > 0: if verbose > 0:
print(" To:", song['targetFile'], song['originExt']) print(" To:", song['targetFile'] + song['originExt'])
if act: if act:
print("would've acted") copy_file(file, song['targetFile'] + song['originExt'])
#print("would've acted")
#if not os.path.isfile(os.path.join(destDir, song['outPath'], song['outFile'] + file_ext)): #if not os.path.isfile(os.path.join(destDir, song['outPath'], song['outFile'] + file_ext)):
@ -335,7 +368,7 @@ def syncWorking(tmpDir, excludeDirs=[], act=False, verbose=0):
#clearLine() #clearLine()
#print("Processing Complete!") #print("Processing Complete!")
def convertMedia(rootDir, tmpDir, dstDir, dstFormat, excludeDirs=[], act=False, verbose=0): def convertMedia(rootDir, originFormat, tmpDir, dstDir, dstFormat, excludeDirs=[], includeDirs=[], act=False, verbose=0):
import subprocess import subprocess
from musicman.utils.copytags import ( from musicman.utils.copytags import (
copy_tags copy_tags
@ -343,10 +376,12 @@ def convertMedia(rootDir, tmpDir, dstDir, dstFormat, excludeDirs=[], act=False,
if excludeDirs is None: if excludeDirs is None:
excludeDirs=[] excludeDirs=[]
if includeDirs is None:
includeDirs=[]
print("Convert Library Media") print("Convert Library Media")
for file in getLibrary(rootDir, excludeDirs): for file in getLibrary(rootDir, originFormat, excludeDirs, includeDirs):
if (os.path.isdir(os.path.dirname(file)) and os.path.isfile(file)): if (os.path.isdir(os.path.dirname(file)) and os.path.isfile(file)):
clearLine() clearLine()
print("Processing:", os.path.dirname(os.path.dirname(file)), next(spinner), end="\r") print("Processing:", os.path.dirname(os.path.dirname(file)), next(spinner), end="\r")
@ -374,7 +409,9 @@ def convertMedia(rootDir, tmpDir, dstDir, dstFormat, excludeDirs=[], act=False,
os.makedirs(os.path.dirname(song['workingFile'])) os.makedirs(os.path.dirname(song['workingFile']))
#subprocess.call("ffmpeg", "-loglevel", "quiet", "-stats", "-i", file, "-vn", "-c:a", "libfdk_aac", "-vbr", "5", "-nostdin", "-y", song['workingFile'] + song['targetExt']) #subprocess.call("ffmpeg", "-loglevel", "quiet", "-stats", "-i", file, "-vn", "-c:a", "libfdk_aac", "-vbr", "5", "-nostdin", "-y", song['workingFile'] + song['targetExt'])
try: try:
subprocess.call('/usr/bin/ffmpeg -loglevel quiet -stats -i "{0}" -vn -c:a libfdk_aac -vbr 5 -nostdin -y "{1}"'.format(file, song['workingFile'] + song['targetExt']), shell=True) #subprocess.call('/usr/bin/ffmpeg -loglevel quiet -stats -i "{0}" -vn -c:a libfdk_aac -vbr 5 -nostdin -y "{1}"'.format(file, song['workingFile'] + song['targetExt']), shell=True)
#subprocess.call('/usr/bin/ffmpeg -i "{0}" -f caf - | /usr/bin/fdkaac -I -p 29 -m 5 -o "{1}" -'.format(file, song['workingFile'] + song['targetExt']), shell=True)
subprocess.call('/usr/bin/ffmpeg -i "{0}" -f caf - | /usr/bin/fdkaac -I -p 2 -m 5 -o "{1}" -'.format(escape(file), escape(song['workingFile'] + song['targetExt'])), shell=True)
#new = MetaData(song['workingFile'] + song['targetExt']) #new = MetaData(song['workingFile'] + song['targetExt'])
#new.update(song['metadata']) #new.update(song['metadata'])
#new.save() #new.save()
@ -397,7 +434,7 @@ def convertMedia(rootDir, tmpDir, dstDir, dstFormat, excludeDirs=[], act=False,
if __name__ == '__main__': if __name__ == '__main__':
global opt global opt
global config global config
global originPath, targetPath, targetFormat, workPath global originPath, originFormat, targetPath, targetFormat, workPath
import configparser import configparser
opt = musicman.utils.parse_args() opt = musicman.utils.parse_args()
@ -410,6 +447,11 @@ if __name__ == '__main__':
except AttributeError: except AttributeError:
originDir = config['origin']['path'] originDir = config['origin']['path']
try:
originFormat = config['origin']['format'] if opt.origFormat is None else opt.origFormat
except AttributeError:
originFormat = config['origin']['format']
try: try:
targetDir = config['target']['path'] if opt.targetDir is None else opt.targetDir targetDir = config['target']['path'] if opt.targetDir is None else opt.targetDir
except AttributeError: except AttributeError:
@ -432,30 +474,30 @@ if __name__ == '__main__':
try: try:
if opt.mode == 'clean': if opt.mode == 'clean':
cleanLibrary(originDir, opt.excludeDirs, opt.act, opt.verbose) cleanLibrary(originDir, originFormat, opt.excludeDirs, opt.includeDirs, opt.act, opt.verbose)
cleanLibrary(targetDir, opt.excludeDirs, opt.act, opt.verbose) cleanLibrary(targetDir, targetFormat, opt.excludeDirs, opt.includeDirs, opt.act, opt.verbose)
cleanLibrary(workingDir, opt.excludeDirs, opt.act, opt.verbose) cleanLibrary(workingDir, targetFormat, opt.excludeDirs, opt.includeDirs, opt.act, opt.verbose)
elif opt.mode == 'rename': elif opt.mode == 'rename':
renameLibrary(originDir, opt.excludeDirs, opt.act, opt.verbose) renameLibrary(originDir, originFormat, opt.excludeDirs, opt.includeDirs, opt.act, opt.verbose)
renameLibrary(targetDir, opt.excludeDirs, opt.act, opt.verbose) renameLibrary(targetDir, targetFormat, opt.excludeDirs, opt.includeDirs, opt.act, opt.verbose)
renameLibrary(workingDir, opt.excludeDirs, opt.act, opt.verbose) renameLibrary(workingDir, targetFormat, opt.excludeDirs, opt.includeDirs, opt.act, opt.verbose)
elif opt.mode == 'scan': elif opt.mode == 'scan':
if opt.scanMode is None: if opt.scanMode is None:
print("ERROR: Subcommand for scan not provided.") print("ERROR: Subcommand for scan not provided.")
sys.exit(1) sys.exit(1)
elif opt.scanMode == 'untagged': elif opt.scanMode == 'untagged':
findUntagged(originDir, opt.excludeDirs, opt.verbose) findUntagged(originDir, originFormat, opt.excludeDirs, opt.includeDirs, opt.verbose)
findUntagged(targetDir, opt.excludeDirs, opt.verbose) findUntagged(targetDir, targetFormat, opt.excludeDirs, opt.includeDirs, opt.verbose)
findUntagged(workingDir, opt.excludeDirs, opt.verbose) findUntagged(workingDir, targetFormat, opt.excludeDirs, opt.includeDirs, opt.verbose)
elif opt.scanMode == 'new': elif opt.scanMode == 'new':
findNew(originDir, workingDir, targetDir, targetFormat, opt.excludeDirs, opt.verbose) findNew(originDir, originFormat, workingDir, targetDir, targetFormat, opt.excludeDirs, opt.includeDirs, opt.verbose)
elif opt.mode == 'sync': elif opt.mode == 'sync':
syncWorking(workingDir, targetDir, opt.excludeDirs, opt.act, opt.verbose) syncWorking(workingDir, targetFormat, opt.excludeDirs, opt.includeDirs, opt.act, opt.verbose)
elif opt.mode == 'convert': elif opt.mode == 'convert':
convertMedia(originDir, workingDir, targetDir, targetFormat, opt.excludeDirs, opt.act, opt.verbose) convertMedia(originDir, originFormat, workingDir, targetDir, targetFormat, opt.excludeDirs, opt.includeDirs, opt.act, opt.verbose)
except KeyboardInterrupt: except KeyboardInterrupt:
clearLine() clearLine()
print(end='\r') print(end='\r')

View file

@ -26,6 +26,7 @@ def parse_args():
clean_lib.add_argument('-t', '--target', help="Target Directory for Library (overrides config)", metavar="DIR", type=str, dest="targetDir") clean_lib.add_argument('-t', '--target', help="Target Directory for Library (overrides config)", metavar="DIR", type=str, dest="targetDir")
clean_lib.add_argument('-w', '--work', help="Working Directory for new processed files (overrides config)", metavar="DIR", type=str, dest="workingDir") clean_lib.add_argument('-w', '--work', help="Working Directory for new processed files (overrides config)", metavar="DIR", type=str, dest="workingDir")
clean_lib.add_argument('-e', '--exclude', help="Exclude Directory from origin (can be used multiple times)", metavar="DIR", dest='excludeDirs', action='append') clean_lib.add_argument('-e', '--exclude', help="Exclude Directory from origin (can be used multiple times)", metavar="DIR", dest='excludeDirs', action='append')
clean_lib.add_argument('-i', '--include', help="Include Directory from origin (can be used multiple times)", metavar="DIR", dest='includeDirs', action='append')
clean_act = clean.add_argument_group('Action Options') clean_act = clean.add_argument_group('Action Options')
clean_act.add_argument('-g', '--go', help="Clean up library (default just shows what would be done)", dest="act", action='store_true') clean_act.add_argument('-g', '--go', help="Clean up library (default just shows what would be done)", dest="act", action='store_true')
clean.set_defaults(act=False) clean.set_defaults(act=False)
@ -37,6 +38,7 @@ def parse_args():
convert_lib.add_argument('-w', '--work', help="Working Directory for new processed files (overrides config)", metavar="DIR", type=str, dest="workingDir") convert_lib.add_argument('-w', '--work', help="Working Directory for new processed files (overrides config)", metavar="DIR", type=str, dest="workingDir")
convert_lib.add_argument('--format', help="Target directory library format", metavar="FORMAT", type=str, dest="targetFormat") convert_lib.add_argument('--format', help="Target directory library format", metavar="FORMAT", type=str, dest="targetFormat")
convert_lib.add_argument('-e', '--exclude', help="Exclude Directory from origin (can be used multiple times)", metavar="DIR", dest='excludeDirs', action='append') convert_lib.add_argument('-e', '--exclude', help="Exclude Directory from origin (can be used multiple times)", metavar="DIR", dest='excludeDirs', action='append')
convert_lib.add_argument('-i', '--include', help="Include Directory from origin (can be used multiple times)", metavar="DIR", dest='includeDirs', action='append')
convert_act = convert.add_argument_group('Action Options') convert_act = convert.add_argument_group('Action Options')
convert_act.add_argument('-g', '--go', help="Convert media (default just shows new items)", dest="act", action='store_true') convert_act.add_argument('-g', '--go', help="Convert media (default just shows new items)", dest="act", action='store_true')
convert.set_defaults(act=False) convert.set_defaults(act=False)
@ -47,7 +49,9 @@ def parse_args():
rename = subparsers.add_parser('rename', help='Rename Mode', description='Library rename tool renames media into their respective Artist/Album/[Disc-][Track-]Title in relation to their metadata.') rename = subparsers.add_parser('rename', help='Rename Mode', description='Library rename tool renames media into their respective Artist/Album/[Disc-][Track-]Title in relation to their metadata.')
rename_lib = rename.add_argument_group('Library Options') rename_lib = rename.add_argument_group('Library Options')
rename_lib.add_argument('-o', '--origin', help="Origin Directory for Library (overrides config)", metavar="DIR", type=str, dest="originDir") rename_lib.add_argument('-o', '--origin', help="Origin Directory for Library (overrides config)", metavar="DIR", type=str, dest="originDir")
rename_lib.add_argument('-f', '--format', help="Origin Format (flac, m4a, mp3, ...) (overrides config)", metavar="FMT", type=str, dest="origFormat")
rename_lib.add_argument('-e', '--exclude', help="Exclude Directory from origin (can be used multiple times)", metavar="DIR", dest='excludeDirs', action='append') rename_lib.add_argument('-e', '--exclude', help="Exclude Directory from origin (can be used multiple times)", metavar="DIR", dest='excludeDirs', action='append')
rename_lib.add_argument('-i', '--include', help="Include Directory from origin (can be used multiple times)", metavar="DIR", dest='includeDirs', action='append')
rename_act = rename.add_argument_group('Action Options') rename_act = rename.add_argument_group('Action Options')
rename_act.add_argument('-g', '--go', help="Process renaming (default just shows what would be done", dest="act", action='store_true') rename_act.add_argument('-g', '--go', help="Process renaming (default just shows what would be done", dest="act", action='store_true')
rename.set_defaults(act=False) rename.set_defaults(act=False)
@ -59,6 +63,7 @@ def parse_args():
scan_lib.add_argument('-w', '--work', help="Working Directory for new processed files (overrides config)", metavar="DIR", type=str, dest="workingDir") scan_lib.add_argument('-w', '--work', help="Working Directory for new processed files (overrides config)", metavar="DIR", type=str, dest="workingDir")
scan_lib.add_argument('--format', help="Target directory library format", metavar="FORMAT", type=str, dest="targetFormat") scan_lib.add_argument('--format', help="Target directory library format", metavar="FORMAT", type=str, dest="targetFormat")
scan_lib.add_argument('-e', '--exclude', help="Exclude Directory from origin (can be used multiple times)", metavar="DIR", dest='excludeDirs', action='append') scan_lib.add_argument('-e', '--exclude', help="Exclude Directory from origin (can be used multiple times)", metavar="DIR", dest='excludeDirs', action='append')
scan_lib.add_argument('-i', '--include', help="Include Directory from origin (can be used multiple times)", metavar="DIR", dest='includeDirs', action='append')
scan_subparsers = scan.add_subparsers(title='Scan Modes', dest='scanMode') scan_subparsers = scan.add_subparsers(title='Scan Modes', dest='scanMode')
scan_untagged = scan_subparsers.add_parser('untagged', help='Find untagged media', description='Scans for untagged or insufficiently tagged media in the library.') scan_untagged = scan_subparsers.add_parser('untagged', help='Find untagged media', description='Scans for untagged or insufficiently tagged media in the library.')
scan_new = scan_subparsers.add_parser('new', help='Find new unconverted media', description='Scans for new media that is not in the target library for conversion.') scan_new = scan_subparsers.add_parser('new', help='Find new unconverted media', description='Scans for new media that is not in the target library for conversion.')
@ -69,6 +74,7 @@ def parse_args():
sync_lib.add_argument('-t', '--target', help="Target Directory for Library (overrides config)", metavar="DIR", type=str, dest="targetDir") sync_lib.add_argument('-t', '--target', help="Target Directory for Library (overrides config)", metavar="DIR", type=str, dest="targetDir")
sync_lib.add_argument('-w', '--work', help="Working Directory for new processed files (overrides config)", metavar="DIR", type=str, dest="workingDir") sync_lib.add_argument('-w', '--work', help="Working Directory for new processed files (overrides config)", metavar="DIR", type=str, dest="workingDir")
sync_lib.add_argument('-e', '--exclude', help="Exclude Directory from origin (can be used multiple times)", metavar="DIR", dest='excludeDirs', action='append') sync_lib.add_argument('-e', '--exclude', help="Exclude Directory from origin (can be used multiple times)", metavar="DIR", dest='excludeDirs', action='append')
sync_lib.add_argument('-i', '--include', help="Include Directory from origin (can be used multiple times)", metavar="DIR", dest='includeDirs', action='append')
sync_act = sync.add_argument_group('Action Options') sync_act = sync.add_argument_group('Action Options')
sync_act.add_argument('-g', '--go', help="Move media (default just what would be done)", dest="act", action='store_true') sync_act.add_argument('-g', '--go', help="Move media (default just what would be done)", dest="act", action='store_true')