Synthbot

ponka.py

Jan 17th, 2021 (edited)
107
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 8.86 KB | None | 0 0
  1. #!/usr/bin/python
  2.  
  3. """ Utility script to convert PPP animation files.
  4.  
  5. This script is for parsing PPP animation filenames, filtering, renaming, and
  6. restructuring files.
  7.  
  8. Example uses:
  9.  # Clone files from orig_directory into all_files.
  10.  python ponka.py --input "C:/orig_directory"
  11.            --output "C:/new_directory"
  12.            --keep-structure
  13.  
  14.  # Copy all files from orig_directory into new_directory without copying the
  15.  # directory structure.
  16.  python ponka.py --input "C:/orig_directory"
  17.            --output "C:/new_directory"
  18.  
  19.  # Move all files from orig_directory into new_directory without copying the
  20.  # directory structure.
  21.  python ponka.py --input "C:/orig_directory"
  22.            --output "C:/new_directory"
  23.            --migrate
  24.  
  25.  # Move all files with "char" in the path or filename (case-insensitive) into
  26.  # new_directory.
  27.  python ponka.py --input "C:/orig_directory"
  28.            --output "C:/new_directory"
  29.            --filter char
  30.            --migrate
  31.  
  32.  # Copy files from orig_directory to new_directory and rename them so their
  33.  # corresponding FLA filename comes first, followed by their symbol id, and set the
  34.  # extension to .png.
  35.  python ponka.py --input "C:/orig_directory" --output "C:/new_directory" --rename "{symbol}_{fla}.png"
  36.  
  37.  # Move files with "char" in their filename from orig_directory to a chars/ directory,
  38.  # and rename them so they're prefixed with "clean_". Don't actually make the changes,
  39.  # just show which changes would be made.
  40.  python ponka.py --input "C:/orig_directory"
  41.            --output "C:/chars"
  42.            --filter char
  43.            --rename "clean_{fla}_{symbol}.png"
  44.            --migrate
  45.            --dry-run
  46.  
  47. Run "python ponka.py --help" from the command line or Powershell for the short version.
  48. """
  49.  
  50. import argparse
  51. import os
  52. import re
  53. import shutil
  54.  
  55.  
  56. _EXPLICIT_FLA = re.compile(r'f-(.*)\.fla', re.IGNORECASE)
  57. _IMPLICIT_FLA = re.compile(r'(.*)\.fla', re.IGNORECASE)
  58.  
  59. _EXPLICIT_SYM = re.compile(r's-(.*)\.sym', re.IGNORECASE)
  60. _IMPLICIT_SYM = re.compile(r'(.*_f[0-9]{0,4})\.png', re.IGNORECASE)
  61.  
  62. class SymbolFile:
  63.     def __init__(self, full_path, rel_path):
  64.         self.full_path = full_path
  65.         self.rel_path = rel_path
  66.         self._rel_dir = None
  67.         self._fla_name = None
  68.         self._symbol_name = None
  69.         self._full_name = None
  70.         self._base_name = None
  71.         self._ext = None
  72.  
  73.     @property
  74.     def rel_dir(self):
  75.         if not self._rel_dir:
  76.             self._rel_dir = os.path.dirname(self.rel_path)
  77.         return self._rel_dir
  78.    
  79.  
  80.     @property
  81.     def fla_name(self):
  82.         if self._fla_name:
  83.             return self.fla_name
  84.  
  85.         for file_part in self.full_path.split(os.sep)[::-1]:
  86.             matches = _EXPLICIT_FLA.search(file_part)
  87.             if matches:
  88.                 self._fla_name = matches.group(1)
  89.                 return self._fla_name
  90.  
  91.         for file_part in self.full_path.split(os.sep)[::-1]:
  92.             matches = _IMPLICIT_FLA.search(file_part)
  93.             if matches:
  94.                 self._fla_name = matches.group(1)
  95.                 return self._fla_name
  96.  
  97.         raise Exception("Missing FLA file in path: %s" % (self.full_path,))
  98.  
  99.     @property
  100.     def symbol_name(self):
  101.         if self._symbol_name:
  102.             return self._symbol_name
  103.  
  104.         for file_part in self.full_path.split(os.sep)[::-1]:
  105.             matches = _EXPLICIT_SYM.search(file_part)
  106.             if matches:
  107.                 self._symbol_name = matches.group(1)
  108.                 return self._symbol_name
  109.  
  110.         for file_part in self.full_path.split(os.sep)[::-1]:
  111.             matches = _IMPLICIT_SYM.search(file_part)
  112.             if matches:
  113.                 self._symbol_name = matches.group(1)
  114.                 return self._symbol_name
  115.  
  116.         raise Exception("Missing symbol name in path: %s" % (self.full_path,))
  117.  
  118.     @property
  119.     def full_name(self):
  120.         if not self._full_name:
  121.             self._full_name = os.path.basename(self.rel_path)
  122.         return self._full_name
  123.  
  124.     def _parse_base_ext(self):
  125.         base_name, ext = os.path.splitext(self.full_name)
  126.         self._base_name = base_name
  127.         self._ext = ext[1:]
  128.  
  129.     @property
  130.     def base_name(self):
  131.         if self._base_name:
  132.             return self._base_name
  133.  
  134.         self._parse_base_ext()
  135.         return self._base_name
  136.  
  137.     @property
  138.     def extension(self):
  139.         if self._ext:
  140.             return self._ext
  141.  
  142.         self._parse_base_ext()
  143.         return self._ext
  144.  
  145.  
  146. class SymbolFileCopier:
  147.     def __init__(self, output_dir, keep_structure=False, rename_pattern=None, migrate=False, dry_run=False):
  148.         self.output_dir = output_dir
  149.         self.keep_structure = keep_structure
  150.         self.rename_pattern = rename_pattern
  151.         self.migrate = migrate
  152.         self.dry_run = dry_run
  153.  
  154.     def copy(self, pppfile):
  155.         if self.rename_pattern:
  156.             output_filename = (
  157.                 self.rename_pattern.replace("{file}", pppfile.full_name)
  158.                 .replace("{base}", pppfile.base_name)
  159.                 .replace("{ext}", pppfile.extension)
  160.                 .replace("{fla}", f"f-{pppfile.fla_name}.fla")
  161.                 .replace("{symbol}", f"s-{pppfile.symbol_name}.sym")
  162.             )
  163.         else:
  164.             output_filename = pppfile.full_name
  165.  
  166.         if self.keep_structure:
  167.             output_path = os.path.join(self.output_dir, pppfile.rel_dir, output_filename)
  168.         else:
  169.             output_path = os.path.join(self.output_dir, output_filename)
  170.  
  171.         if self.dry_run:
  172.             op = 'moving' if self.migrate else 'copying'
  173.             print(op, pppfile.full_path, "to", output_path)
  174.             return
  175.  
  176.         output_dir = os.path.dirname(output_path)
  177.         os.makedirs(output_dir, exist_ok=True)
  178.  
  179.         try:
  180.             op = os.rename if self.migrate else shutil.copyfile
  181.             op(pppfile.full_path, output_path)
  182.         except:
  183.             print('duplicate file:', pppfile.full_path)
  184.  
  185.  
  186. def _allow_all(x):
  187.     return True
  188.  
  189.  
  190. def collect_files(input_path, filter_regex=None):
  191.     if not filter_regex:
  192.         filter_regex = _allow_all
  193.     else:
  194.         filter_regex = re.compile(filter_regex, re.IGNORECASE).search
  195.  
  196.     for root, dirs, files in os.walk(input_path):
  197.         for fn in files:
  198.             full_path = os.path.join(root, fn)
  199.             rel_path = os.path.relpath(full_path, start=input_path)
  200.  
  201.             if not filter_regex(rel_path):
  202.                 continue
  203.  
  204.             yield SymbolFile(full_path, rel_path)
  205.  
  206.  
  207. class SmartFormatter(argparse.HelpFormatter):
  208.     def _split_lines(self, text, width):
  209.         if text.startswith("R|"):
  210.             return text[2:].splitlines()
  211.         return argparse.HelpFormatter._split_lines(self, text, width)
  212.  
  213. def _main():
  214.     parser = argparse.ArgumentParser(
  215.         formatter_class=SmartFormatter,
  216.         description='File conversion tool for PPP animation data.')
  217.  
  218.     parser.add_argument(
  219.         "-i",
  220.         "--input",
  221.         metavar="folder",
  222.         required=True,
  223.         help="Input folder containing the animation data.",
  224.     )
  225.     parser.add_argument(
  226.         "-o",
  227.         "--output",
  228.         metavar="folder",
  229.         required=True,
  230.         help="Output folder for dumping the results.",
  231.     )
  232.  
  233.     parser.add_argument(
  234.         "--migrate",
  235.         default=False,
  236.         action="store_true",
  237.         help="Include this flag to move files rather than copy them.",
  238.     )
  239.  
  240.     parser.add_argument(
  241.         "--keep-structure",
  242.         default=False,
  243.         action="store_true",
  244.         help="Replicate the input folder structure in the output folder.",
  245.     )
  246.  
  247.     parser.add_argument(
  248.         "-f",
  249.         "--filter",
  250.         metavar="pattern",
  251.         required=False,
  252.         help="Filter to include files by filename (regex).",
  253.     )
  254.  
  255.     parser.add_argument(
  256.         "-r",
  257.         "--rename",
  258.         metavar="template",
  259.         required=False,
  260.         help="""R|Rename files based on the existing name. You can use the following
  261. template variables:
  262.  {file}: The full filename including the extension.
  263.  {base}: The file name without the extension.
  264.  {ext}: The file extension.
  265.  {fla}: The FLA base name associated with a file.
  266.  {symbol}: The symbol name associated with a file.""",
  267.     )
  268.  
  269.     parser.add_argument(
  270.         "--dry-run",
  271.         default=False,
  272.         action="store_true",
  273.         help="Dump of the list of changes to be made without actually making them.",
  274.     )
  275.  
  276.     args = parser.parse_args()
  277.  
  278.     filter_pattern = args.filter
  279.     rename_template = args.rename
  280.  
  281.     input_files = collect_files(args.input, filter_pattern)
  282.     copier = SymbolFileCopier(args.output, args.keep_structure, args.rename, args.migrate, args.dry_run)
  283.  
  284.     for pppfile in input_files:
  285.         copier.copy(pppfile)
  286.  
  287.  
  288. if __name__ == "__main__":
  289.     _main()
  290.  
Advertisement
Add Comment
Please, Sign In to add comment