# # Syncronize two folders. # # Notes: # # - There is no master folder # - No files or folders are erased or overwritten. If file does not exist in one # folder we copy it from second source, if files exist in both folders and are # different we just report those differences. # - We compare name and size ( and ignore differences smaller than 4224 bytes for mp3 files => ID3 tags ) # # I guess you can also do this task using rsync. :) # import os import sys import shutil TYPE_FILE = 0 TYPE_DIR = 1 # tip: set enable_sync as False to see report without really sync files enable_sync = True def compare_folders( folder_a, folder_b ): objs = {} log = [] # build list of objects for folder in [ folder_a, folder_b ]: for fname in os.listdir( folder ): p = os.path.join( folder, fname ) t = { 'size' : -1, 'type' : -1 } if os.path.isfile( p ): t['size'] = os.path.getsize( p ) t['type'] = TYPE_FILE elif os.path.isdir( p ): t['size'] = 0 t['type'] = TYPE_DIR if fname not in objs.keys(): objs[ fname ] = {} objs[ fname ][ folder ] = t # analize list of objects for fname, props in objs.items(): # in both folders if folder_a in props.keys() and folder_b in props.keys(): # check type if props[ folder_a ]['type'] != props[ folder_b ]['type']: log.append( " a <> b :: " + fname + " ( different types! not sync'ed! )" ) continue # check size if props[ folder_a ]['size'] != props[ folder_b ]['size']: # fix: mp3 files with different ID3 tags will be ignored! if fname[ -4 : ].lower() == '.mp3' and abs( props[ folder_a ]['size'] - props[ folder_b ]['size'] ) <= 4224 : continue log.append( " a <> b :: " + fname + " ( different sizes! not sync'ed! )" ) continue # analize folders if props[ folder_a ]['type'] == TYPE_DIR: compare_folders( os.path.join( folder_a, fname ), os.path.join( folder_b, fname ) ) # only on folder_a ( => copy to folder_b ) elif folder_a in props.keys(): # is a file? if props[ folder_a ]['type'] == TYPE_FILE: log.append( " a => b :: " + fname ) if enable_sync: shutil.copy2( os.path.join( folder_a, fname ), folder_b ) continue # is a folder? if props[ folder_a ]['type'] == TYPE_DIR: log.append( " a => b :: " + fname ) if enable_sync: shutil.copytree( os.path.join( folder_a, fname ), os.path.join( folder_b, fname ) ) continue print "unknown object type: '%s'\n" % ( os.path.join( folder_a, fname ) ) # only on folder_b ( => copy to folder_a ) else: # is a file? if props[ folder_b ]['type'] == TYPE_FILE: log.append( " b => a :: " + fname ) if enable_sync: shutil.copy2( os.path.join( folder_b, fname ), folder_a ) continue # is a folder? if props[ folder_b ]['type'] == TYPE_DIR: log.append( " b => a :: " + fname ) if enable_sync: shutil.copytree( os.path.join( folder_b, fname ), os.path.join( folder_a, fname ) ) continue print "unknown object type: '%s'\n" % ( os.path.join( folder_b, fname ) ) # print log if necessary if log: print "folder a: %s\nfolder b: %s\n" % ( folder_a, folder_b ) for line in log: print line print if __name__ == '__main__': # check if we have the needed arguments if len( sys.argv ) != 3: print "usage: python %s " % ( sys.argv[ 0 ] ) sys.exit( 0 ) # those two provided folders must exist! if not os.path.exists( sys.argv[ 1 ] ) or not os.path.exists( sys.argv[ 2 ] ): print "unable to find provided folders!" sys.exit( 0 ) compare_folders( sys.argv[ 1 ], sys.argv[ 2 ] )