Advertisement
creamygoat

Shuffle

Oct 1st, 2017
1,048
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 4.47 KB | None | 0 0
  1. #!/usr/bin/python3
  2.  
  3. """shuffle, a script to shuffle the names of music files.
  4.  
  5. Description:
  6.  Shuffle renames all the files in a given directory so that they appear in
  7.  a random order on an MP3 player which lacks a shuffle feature. This is done
  8.  by generating a sequence of numbers, randomly assigning those numbers
  9.  to the files and renaming those files to begin with prefix strings based
  10.  on those numbers. Files which already have prefixes of the sort Shuffle
  11.  might generate have their existing prefixes recomputed.
  12.  
  13. Author:
  14.  Daniel Neville, creamygoat@gmail.com
  15.  
  16. Copyright:
  17.  None
  18.  
  19. Licence:
  20.  Public domain
  21.  
  22. """
  23.  
  24. #-------------------------------------------------------------------------------
  25.  
  26. import sys
  27. import os
  28. import re
  29. import random
  30. from os.path import join as pj
  31.  
  32. #-------------------------------------------------------------------------------
  33.  
  34. def main(args):
  35.  
  36.   rc = 0
  37.   do_clean = False
  38.   cmd_name = args[0]
  39.   margs = args[1:]
  40.  
  41.   if len(margs) >= 1 and "--help" in margs:
  42.     print("Usage: {} [--clean] [--help] path".format(cmd_name))
  43.     return 0
  44.  
  45.   s = "--clean"
  46.   while s in margs:
  47.     margs.remove(s)
  48.     do_clean = True
  49.  
  50.   if len(margs) > 1:
  51.     print("{}: Too many arguments.".format(cmd_name))
  52.     return 1
  53.  
  54.   if len(margs) < 1:
  55.     print("{}: A directory path must be supplied.".format(cmd_name))
  56.     return 1
  57.  
  58.   path = margs[0] if len(margs) >= 1 else "."
  59.   if os.path.exists(path):
  60.     if os.path.isdir(path):
  61.       if not os.access(path, os.R_OK | os.W_OK | os.X_OK):
  62.         print("{}: \"{}\" lacks full permissions.".format(cmd_name, path))
  63.         return 2
  64.     else:
  65.       print("{}: \"{}\" is not a directory.".format(cmd_name, path))
  66.       return 2
  67.   else:
  68.     print("{}: The path \"{}\" does not exist.".format(cmd_name, path))
  69.     return 2
  70.  
  71.   # Build a list of file to be shiffled and sequence numbers to be avoided
  72.   # because they may contribute to renaming collisions.
  73.  
  74.   files = []
  75.   reserved_nums = []
  76.   seq_pat = re.compile("^_\d+_")
  77.  
  78.   for f in os.listdir(path):
  79.  
  80.     do_shuffle = False
  81.  
  82.     if os.path.isfile(pj(path, f)):
  83.       if len(f) > 0 and f[0] != '.':
  84.         do_shuffle = True
  85.  
  86.     if do_shuffle:
  87.       files.append(f)
  88.     else:
  89.       m = seq_pat.match(f)
  90.       if m:
  91.         s = f[m.span()[0] : m.span()[1]]
  92.         i = int(s[1:-1])
  93.         reserved_nums.append(i)
  94.  
  95.   # Exit if there is not even one file to shuffle.
  96.  
  97.   n = len(files)
  98.  
  99.   if n < 1:
  100.     return rc
  101.  
  102.   # Build a list of safe numbers, one for each file to be shuffled and
  103.   # one for use in a temporary filename in case a swap is needed. Take
  104.   # note of the maximum field width needed.
  105.  
  106.   seq_start = 1
  107.   nums = []
  108.  
  109.   x = seq_start
  110.  
  111.   for i in range(n + 1):
  112.     while x in reserved_nums:
  113.       x += 1
  114.     nums.append(x)
  115.     x += 1
  116.  
  117.   w = len(str(x - 1))
  118.   temp_num = nums.pop()
  119.   random.shuffle(nums)
  120.  
  121.   # Build a list of new filenames with sequence numbers prepended or
  122.   # re-written.
  123.  
  124.   nfiles = []
  125.  
  126.   for i, f in enumerate(files):
  127.  
  128.     s = "_{:0>{}}_".format(nums[i], w)
  129.     m = seq_pat.match(f)
  130.  
  131.     if do_clean:
  132.       if m:
  133.         b = f[m.span()[1]:]
  134.         if len(b) < 1 or b[0] == '.':
  135.           new_f = f
  136.         elif os.path.exists(pj(path, b)) or b in files or b in nfiles:
  137.           new_f = f
  138.         else:
  139.           new_f = b
  140.       else:
  141.         new_f = f
  142.     else:
  143.       new_f = s + f[m.span()[1]:] if m else s + f
  144.  
  145.     nfiles.append(new_f)
  146.  
  147.   temp_prefix = "_{:0>{}}_".format(temp_num, w)
  148.  
  149.   # Rename the files to be shuffled, taking care to swap files if necessary.
  150.   # Even if a name swap is interrupted, the files names must be left in a
  151.   # valid state for re-shuffling and their extensions must be preserved.
  152.   # The list of filenames is kept up-to-date.
  153.  
  154.   i = 0
  155.   while i < n:
  156.     f = files[i]
  157.     nf = nfiles[i]
  158.     if f != nf:
  159.       old_pf = pj(path, f)
  160.       new_pf = pj(path, nf)
  161.       if nf in files:
  162.         x = files.index(nf)
  163.         temp_pf = pj(path, temp_prefix + nf[len(temp_prefix):])
  164.         os.rename(new_pf, temp_pf)
  165.         os.rename(old_pf, new_pf)
  166.         os.rename(temp_pf, old_pf)
  167.         files[i], files[x] = files[x], files[i]
  168.       else:
  169.         os.rename(old_pf, new_pf)
  170.         files[i] = nf
  171.     i += 1
  172.  
  173.   return rc
  174.  
  175. #-------------------------------------------------------------------------------
  176.  
  177. if __name__ == "__main__":
  178.   exit(main(sys.argv))
  179.  
  180. #-------------------------------------------------------------------------------
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement