Advertisement
faubiguy

musicsync

Jul 29th, 2016
169
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 5.28 KB | None | 0 0
  1. #!/usr/bin/python3
  2. import os, sys, argparse, re, subprocess, fnmatch
  3.  
  4. parser = argparse.ArgumentParser(description='Sync music library and playlists to sd card')
  5. parser.add_argument('-l', '--library', default=os.path.expanduser('~/Music/Library'), help='Path to music library')
  6. parser.add_argument('-s', '--sd-path', default='/mnt/sd', help='Directory to sync to. Parent directory of library and playlists directories')
  7. parser.add_argument('-p', '--playlist-dir', default=os.path.expanduser('~/.quodlibet/playlists'), help='Path to playlist directory')
  8. parser.add_argument('--library-only', action='store_true', help='Only sync the library and not the playlists')
  9. parser.add_argument('--playlists-only', action='store_true', help='Only sync the playlists and not the library')
  10. parser.add_argument('--dry-run', action='store_true', help='Test run without making any changes')
  11. parser.add_argument('--confirm', action='store_true', help='Don\'t ask for confirmation interactively')
  12. parser.add_argument('--no-progress', action='store_true', help='Don\'t show progress while syncing library')
  13. parser.add_argument('--ignore-nosync', action='store_true', help='Don\'t use .nosync file if it exists')
  14. args = parser.parse_args()
  15.  
  16. library = os.path.join(os.path.abspath(args.library), '')
  17. sd_path = os.path.abspath(args.sd_path)
  18. src_playlist_dir = os.path.abspath(args.playlist_dir)
  19.  
  20. def get_confirm(message):
  21.     response = input(message)
  22.     return re.match('y|yes', response, re.IGNORECASE)
  23.  
  24. excludes = []
  25. nosync = os.path.join(library, '.nosync')
  26. if os.path.exists(nosync) and not args.ignore_nosync:
  27.     with open(nosync) as nosync_lines:
  28.         for line in nosync_lines:
  29.             line = line[:-1]
  30.             if line.startswith(library):
  31.                 line = '/' + line[len(library):]
  32.             excludes.append(line)
  33.  
  34. exclude_re = []
  35. for ex in excludes:
  36.     exclude_re.append(re.compile(fnmatch.translate(os.path.join(library, ex))[:-7]))
  37.  
  38. def is_excluded(song):
  39.     return any(exre.match(song) for exre in exclude_re)
  40.  
  41. def sync_library():
  42.     print('Beginning library sync')
  43.     rsync_args = ['rsync', '--recursive', '--times', '--verbose', '--delete', '--compress', '--modify-window=2']
  44.     if args.dry_run:
  45.         rsync_args.append('--dry-run')
  46.     if not args.no_progress:
  47.         rsync_args.extend(['--info=progress2'])
  48.     for ex in excludes:
  49.         rsync_args.extend(['--exclude', ex])
  50.     rsync_args.extend(['--', library, os.path.join(sd_path, 'library')])
  51.     if not (args.confirm or get_confirm("Command: {2}\nSync library from {0} to {1}? [y/n] ".format(library, sd_path, ' '.join(rsync_args)))):
  52.         print('Library sync cancelled')
  53.         return
  54.     try:
  55.         returncode = subprocess.run(rsync_args).returncode
  56.     except OSError as e:
  57.         print('Unable to start rsync process: {0}'.format(e))
  58.     if returncode != 0:
  59.         print('Error: rsync exited with code {0}'.format(returncode))
  60.         return
  61.     print('Successfully synced library')
  62.    
  63. def sync_playlists():
  64.     print('Beginning playlist sync')
  65.     dst_playlist_dir = os.path.join(sd_path, 'playlists')
  66.     if not (args.confirm or get_confirm("Sync playlists from {0} to {1}? [y/n] ".format(src_playlist_dir, dst_playlist_dir))):
  67.         print('Playlist sync cancelled')
  68.         return
  69.     if not os.path.isdir(dst_playlist_dir) and not args.dry_run:
  70.         try:
  71.             os.mkdir(dst_playlist_dir)
  72.         except OSError as e:
  73.             print('Unable to create directory {0}: {1}. Skipping playlist sync'.format(dst_playlist_dir, e))
  74.             return
  75.     try:
  76.         with open(os.path.join(sd_path, 'playlists.txt'), 'r') as playlists:
  77.             for playlist_name in playlists:
  78.                 playlist_name = playlist_name.strip()
  79.                 print('Attempting to sync playlist: {0}'.format(playlist_name))
  80.                 src_playlist_path = os.path.join(src_playlist_dir, playlist_name)
  81.                 if not os.path.isfile(src_playlist_path):
  82.                     print('Playlist not found, skipping')
  83.                     continue
  84.                 dst_playlist_path = os.path.join(dst_playlist_dir, playlist_name) + '.m3u'
  85.                 added_songs = set()
  86.                 ignored_songs = 0
  87.                 if not args.dry_run:
  88.                     with open(src_playlist_path, 'r') as src_playlist:
  89.                         with open(dst_playlist_path, 'w') as dst_playlist:
  90.                             for line in src_playlist:
  91.                                 if line.startswith(library) and line not in added_songs and not is_excluded(line):
  92.                                     dst_playlist.write(line.replace(library, '../library/'))
  93.                                     added_songs.add(line)
  94.                                 else:
  95.                                     ignored_songs += 1
  96.                 print('Successfuly synced playlist. {0} songs ignored as duplicates or not in library'.format(ignored_songs))
  97.     except OSError:
  98.         print('Playlists file not found, skipping playlist sync')
  99.         return
  100.     print('Finished syncing playlists')
  101.  
  102. if args.library_only and args.playlists_only:
  103.     sys.exit('--only-library and --only-playlist must not both be provided')
  104.    
  105. if not args.playlists_only:
  106.     sync_library()
  107.    
  108. if not args.library_only:
  109.     sync_playlists()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement