Advertisement
Guest User

txtp_maker.py

a guest
Jan 22nd, 2019
14
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 11.58 KB | None | 0 0
  1. #!/usr/bin/env python3
  2.  
  3. # ################################################### #
  4. # TXTP MAKER by bnnm
  5. # v20190122: initial release
  6. # ################################################### #
  7.  
  8. from __future__ import division
  9. import subprocess
  10. import zlib
  11. import os.path
  12. import sys
  13.  
  14.  
  15. if (len(sys.argv) <= 1):
  16.     print("Usage: {} (filename) [options]".format(os.path.basename(sys.argv[0])) + "\n"
  17.           "\n"
  18.           "Creates a (filename)_(subsong).txtp for every subsong in (filename).\n"
  19.           "Options:\n"
  20.           " -c (name): set path to CLI (default: test.exe)\n"
  21.           " -n (name): use (name)_(subsong).txtp format\n"
  22.           " -z N: zero-fill subsong number (default: auto fill up to total subsongs)\n"
  23.           " -d (dir): prepend dir to name (if the file will reside in a subdir)\n"
  24.           " -m: create mini-txtp\n"
  25.           " -o: overwrite existing .txtp (beware when using with internal names)\n"
  26.           " -in: name TXTP using the subsong's internal name if found\n"
  27.           " -ie: remove internal name's extension\n"
  28.           " -ii: add subsong number when using internal name\n"
  29.           " -l N: create multiple TXTP per subsong layers, every N channels\n"
  30.           " -fd: filter duplicates (slower)\n"
  31.           " -fcm N: filter min channels\n"
  32.           " -fcM N: filter max channels\n"
  33.           " -frm N: filter min sample rate\n"
  34.           " -frM N: filter max sample rate\n"
  35.           " -fsm N.N: filter min seconds\n"
  36.           " -fsM N.N: filter max seconds\n"
  37.           )
  38.     exit()
  39.  
  40. # ####################################################
  41.  
  42. def make_cmd(cfg, fname_in, fname_out, target_subsong):
  43.     if (cfg.test_dupes):
  44.         cmd = "{} -s {} -i -o {} {}".format(cfg.cli, target_subsong, fname_out, fname_in)
  45.     else:
  46.         cmd = "{} -s {} -m -i -o {} {}".format(cfg.cli, target_subsong, fname_out, fname_in)
  47.     return cmd
  48.  
  49.  
  50. class ConfigParser(object):
  51.     cli = "test.exe"
  52.  
  53.     base_name = ''
  54.     zero_fill = -1
  55.     subdir = ''
  56.     mini_txtp = False
  57.     overwrite = False
  58.     layers = 0
  59.  
  60.     use_internal_name = False
  61.     use_internal_ext = False
  62.     use_internal_index = False
  63.  
  64.     test_dupes = False
  65.     min_channels = 2
  66.     max_channels = 0
  67.     min_sample_rate = 0
  68.     max_sample_rate = 0
  69.     min_seconds = 0.0
  70.     max_seconds = 0.0
  71.  
  72.     argv_len = 0
  73.     index = 0
  74.  
  75.  
  76.     def read_bool(self, command, default):
  77.         if self.index > self.argv_len - 1:
  78.             return default
  79.         if self.argv[self.index] == command:
  80.             val = True
  81.             self.index += 1
  82.             return val
  83.         return default
  84.    
  85.     def read_value(self, command, default):
  86.         if self.index > self.argv_len - 2:
  87.             return default
  88.         if self.argv[self.index] == command:
  89.             val = self.argv[self.index+1]
  90.             self.index += 2
  91.             return val
  92.         return default
  93.  
  94.     def read_string(self, command, default):
  95.         return str(self.read_value(command, default))
  96.  
  97.     def read_int(self, command, default):
  98.         return int(self.read_value(command, default))
  99.  
  100.     def read_float(self, command, default):
  101.         return float(self.read_value(command, default))
  102.  
  103.     #todo improve this poop
  104.     def __init__(self, argv):
  105.        
  106.         self.index = 2 #after file and
  107.         self.argv = argv
  108.         self.argv_len = len(argv)
  109.  
  110.         prev_index = self.index
  111.         while self.index < len(self.argv):
  112.             self.cli = self.read_string('-c', self.cli)
  113.             self.base_name = self.read_string('-n', self.base_name)
  114.             self.zero_fill = self.read_int('-z', self.zero_fill)
  115.             self.subdir = self.read_string('-d', self.subdir)
  116.  
  117.             self.test_dupes = self.read_bool('-fd', self.test_dupes)
  118.             self.min_channels = self.read_int('-fcm', self.min_channels)
  119.             self.max_channels = self.read_int('-fcM', self.max_channels)
  120.             self.min_sample_rate = self.read_int('-frm', self.min_sample_rate)
  121.             self.max_sample_rate = self.read_int('-frM', self.max_sample_rate)
  122.             self.min_seconds = self.read_float('-fsm', self.min_seconds)
  123.             self.max_seconds = self.read_float('-fsM', self.max_seconds)
  124.  
  125.             self.mini_txtp = self.read_bool('-m', self.mini_txtp)
  126.             self.overwrite = self.read_bool('-o', self.overwrite)
  127.             self.layers = self.read_int('-l', self.layers)
  128.  
  129.             self.use_internal_name = self.read_bool('-in', self.use_internal_name)
  130.             self.use_internal_ext = self.read_bool('-ie', self.use_internal_ext)
  131.             self.use_internal_index = self.read_bool('-ii', self.use_internal_index)
  132.  
  133.             if prev_index == self.index:
  134.                 self.index += 1
  135.             prev_index = self.index
  136.  
  137.         if (self.subdir != '') and not (self.subdir.endswith('/') or self.subdir.endswith('\\')):
  138.             self.subdir += '/'
  139.  
  140.     def __str__(self):
  141.         return str(self.__dict__)
  142.  
  143.  
  144. class Cr32Helper(object):
  145.     crc32_map = {}
  146.     dupe = False
  147.    
  148.  
  149.     def get_crc32(self, fname):
  150.         buf_size = 0x8000
  151.         with open(fname, 'rb') as file:
  152.             buf = file.read(buf_size)
  153.             crc32 = 0
  154.             while len(buf) > 0:
  155.                 crc32 = zlib.crc32(buf, crc32)
  156.                 buf = file.read(buf_size)
  157.         return crc32 & 0xFFFFFFFF
  158.  
  159.     def update(self, fname):
  160.         self.dupe = False
  161.         if cfg.test_dupes == 0:
  162.             return
  163.         if not os.path.exists(fname):
  164.             return
  165.  
  166.         crc32_str = format(self.get_crc32(fname),'08x')
  167.         if (crc32_str in self.crc32_map):
  168.             self.dupe = True
  169.             return
  170.         self.crc32_map[crc32_str] = True
  171.  
  172.         os.remove(fname)
  173.         return
  174.  
  175.     def is_dupe(self):
  176.         return self.dupe
  177.  
  178.     def __init__(self, cfg):
  179.         self.cfg = cfg
  180.  
  181.  
  182. class TxtpMaker(object):
  183.     channels = 0
  184.     sample_rate = 0
  185.     num_samples = 0
  186.     stream_count = 0
  187.     stream_index = 0
  188.     stream_name = ''
  189.     stream_seconds = 0
  190.  
  191.     def get_string(self, str):
  192.         find_pos = self.output.find(str)
  193.         if (find_pos == -1):
  194.             return ''
  195.         cut_pos = find_pos + len(str)
  196.         str_cut = self.output[cut_pos:]
  197.         return str_cut.split()[0]
  198.  
  199.     def get_value(self, str):
  200.         res = self.get_string(str)
  201.         if (res == ''):
  202.            return 0;
  203.         return int(res)
  204.  
  205.     def is_ignorable(self):
  206.         if (self.channels < cfg.min_channels):
  207.             return True;
  208.         if (cfg.max_channels > 0 and self.channels > cfg.max_channels):
  209.             return True;
  210.         if (self.sample_rate < cfg.min_sample_rate):
  211.             return True;
  212.         if (cfg.max_sample_rate > 0 and self.sample_rate > cfg.max_sample_rate):
  213.             return True;
  214.         if (self.stream_seconds < cfg.min_seconds):
  215.             return True;
  216.         if (cfg.max_seconds > 0 and self.stream_seconds > cfg.max_seconds):
  217.             return True;
  218.         return False
  219.  
  220.     def get_stream_mask(self, layer):
  221.         mask = '#c'
  222.  
  223.         loops = cfg.layers
  224.         if layer + cfg.layers > self.channels:
  225.             loops = self.channels - cfg.layers
  226.         for ch in range(0,loops):
  227.             mask += str(layer+ch) + ','
  228.  
  229.         mask = mask[:-1]
  230.         return mask
  231.  
  232.     def get_stream_name(self):
  233.         if not cfg.use_internal_name:
  234.             return ''
  235.         txt = self.stream_name
  236.  
  237.         # remove paths #todo maybe config/replace?
  238.         pos = txt.rfind("\\")
  239.         if (pos != -1):
  240.             txt = txt[pos+1:]
  241.         pos = txt.rfind("/")
  242.         if (pos != -1):
  243.             txt = txt[pos+1:]
  244.         # remove bad chars
  245.         txt = txt.replace("%", "_")
  246.         txt = txt.replace("*", "_")
  247.         txt = txt.replace("?", "_")
  248.         txt = txt.replace(":", "_")
  249.         txt = txt.replace("\"", "_")
  250.         txt = txt.replace("|", "_")
  251.         txt = txt.replace("<", "_")
  252.         txt = txt.replace(">", "_")
  253.    
  254.         if not cfg.use_internal_ext:
  255.             pos = txt.rfind(".")
  256.             if (pos != -1):
  257.                 txt = txt[:pos]
  258.         return txt
  259.        
  260.     def write(self, outname, line):
  261.         outname += '.txtp'
  262.         if not cfg.overwrite and os.path.exists(outname):
  263.             raise ValueError('TXTP exists in path: ' + outname)
  264.         ftxtp = open(outname,"w+")
  265.         if line != '':
  266.             ftxtp.write(line)
  267.         ftxtp.close()
  268.         print("created: " + outname)
  269.         return
  270.  
  271.     def make(self, fname):
  272.         if self.is_ignorable():
  273.             return
  274.  
  275.         index = str(self.stream_index)
  276.         if cfg.zero_fill < 0:
  277.             index = index.zfill(len(str(self.stream_count)))
  278.         else:
  279.             index = index.zfill(cfg.zero_fill)
  280.  
  281.         if cfg.mini_txtp:
  282.             outname = "{}#{}".format(fname, index)
  283.  
  284.             if cfg.layers > 0 and cfg.layers < self.channels:
  285.                 for layer in range(0, self.channels, cfg.layers):
  286.                     mask = self.get_stream_mask(layer)
  287.                     self.write(outname + mask, '')
  288.             else:
  289.                 self.write(outname, '')
  290.  
  291.         else:
  292.             stream_name = self.get_stream_name()
  293.             if stream_name != '':
  294.                 outname = stream_name
  295.                 if cfg.use_internal_index:
  296.                     outname += "_{}".format(index)
  297.             else:
  298.                 if cfg.base_name != '':
  299.                     txt = cfg.base_name
  300.                 else:
  301.                     txt = fname
  302.                     pos = txt.rfind(".")
  303.                     if (pos != -1):
  304.                         txt = txt[:pos]
  305.                 outname = "{}_{}".format(txt, index)
  306.  
  307.             line = ''
  308.             if cfg.subdir != '':
  309.                 line += cfg.subdir
  310.             line += "{}#{}".format(fname, self.stream_index)
  311.  
  312.             if cfg.layers > 0 and cfg.layers < self.channels:
  313.                 done = 0
  314.                 for layer in range(0, self.channels, cfg.layers):
  315.                     sub = chr(ord('a') + done)
  316.                     done += 1
  317.                     mask = self.get_stream_mask(layer)
  318.                     self.write(outname + sub, line + mask)
  319.             else:
  320.                 self.write(outname, line)
  321.  
  322.     def has_more_subsongs(self, target_subsong):
  323.         return target_subsong < self.stream_count
  324.  
  325.     def __init__(self, cfg, output_b):
  326.         self.cfg = cfg
  327.  
  328.         self.output = str(output_b).replace("\\r","").replace("\\n","\n")
  329.         self.channels = self.get_value("channels: ")
  330.         self.sample_rate = self.get_value("sample rate: ")
  331.         self.num_samples = self.get_value("stream total samples: ")
  332.         self.stream_count = self.get_value("stream count: ")
  333.         self.stream_index = self.get_value("stream index: ")
  334.         self.stream_name = self.get_string("stream name: ")
  335.  
  336.         if self.channels == 0:
  337.             raise ValueError('Incorrect command result')
  338.  
  339.         self.stream_seconds = self.num_samples / self.sample_rate
  340.  
  341.     def __str__(self):
  342.         return str(self.__dict__)
  343.  
  344. # ####################################################
  345.  
  346. cfg = ConfigParser(sys.argv)
  347.  
  348. fname_in = sys.argv[1]
  349. fname_out = fname_in + ".wav"
  350. target_subsong = 1
  351. crc32 = Cr32Helper(cfg)
  352.  
  353. while 1:
  354.     cmd = make_cmd(cfg, fname_in, fname_out, target_subsong)
  355.     output_b = subprocess.check_output(cmd, shell=True)
  356.  
  357.     maker = TxtpMaker(cfg, output_b)
  358.  
  359.     crc32.update(fname_out)
  360.     if not crc32.is_dupe():
  361.         maker.make(fname_in)
  362.  
  363.     if not maker.has_more_subsongs(target_subsong):
  364.         break
  365.     target_subsong += 1
  366.  
  367. print("done!")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement