From 9b46a7b92722b0f08e176452403a423d4578f598 Mon Sep 17 00:00:00 2001 From: Ethan Dalool Date: Tue, 13 Mar 2018 01:45:23 -0700 Subject: [PATCH] else --- Downloady/downloady.py | 12 ++- SpinalTap/spinal.py | 3 + ThreadQueue/threadqueue.py | 2 +- Toolbox/drawn_quartered.py | 73 ++++++++++++++++ Toolbox/mp3slice.py | 171 +++++++++++++++++++++++++++++++++++++ Toolbox/reverse.py | 7 ++ Toolbox/search.py | 2 +- 7 files changed, 266 insertions(+), 4 deletions(-) create mode 100644 Toolbox/drawn_quartered.py create mode 100644 Toolbox/mp3slice.py create mode 100644 Toolbox/reverse.py diff --git a/Downloady/downloady.py b/Downloady/downloady.py index 126da41..8f50963 100644 --- a/Downloady/downloady.py +++ b/Downloady/downloady.py @@ -375,9 +375,17 @@ def safeprint(*texts, **kwargs): print(*texts, **kwargs) def sanitize_filename(text, exclusions=''): - bet = FILENAME_BADCHARS.replace(exclusions, '') - for char in bet: + to_remove = FILENAME_BADCHARS + for exclude in exclusions: + to_remove = to_remove.replace(exclude, '') + + for char in to_remove: text = text.replace(char, '') + + (drive, path) = os.path.splitdrive(text) + path = path.replace(':', '') + text = drive + path + return text def sanitize_url(url): diff --git a/SpinalTap/spinal.py b/SpinalTap/spinal.py index fb2f188..b6e21c5 100644 --- a/SpinalTap/spinal.py +++ b/SpinalTap/spinal.py @@ -84,6 +84,7 @@ def copy(source, file_args=None, file_kwargs=None, dir_args=None, dir_kwargs=Non def copy_dir( source, destination=None, + *, bytes_per_second=None, callback_directory=None, callback_exclusion=None, @@ -277,6 +278,7 @@ def copy_dir( def copy_file( source, destination=None, + *, destination_new_root=None, bytes_per_second=None, callback_progress=None, @@ -565,6 +567,7 @@ def verify_hash(path, known_size, known_hash, callback=None): def walk_generator( path='.', + *, callback_exclusion=None, callback_permission_denied=None, depth_first=True, diff --git a/ThreadQueue/threadqueue.py b/ThreadQueue/threadqueue.py index 2a055e6..815df3a 100644 --- a/ThreadQueue/threadqueue.py +++ b/ThreadQueue/threadqueue.py @@ -46,7 +46,7 @@ class ThreadQueue: break lam = self._lambdas.pop(0) thread = threading.Thread(target=lam) - #thread.daemon = True + thread.daemon = True thread.start() self._threads.append(thread) diff --git a/Toolbox/drawn_quartered.py b/Toolbox/drawn_quartered.py new file mode 100644 index 0000000..1e22412 --- /dev/null +++ b/Toolbox/drawn_quartered.py @@ -0,0 +1,73 @@ +''' +This script takes an image and splits it up into pieces as separate files. + +drawn_quartered test.jpg --width 2 --height 2 +drawn_quartered test.jpg outputname.jpg --width 3 --height 4 +''' + +import argparse +import math +import os +import PIL.Image +import sys + +from voussoirkit import pathclass + +def drawquarter(image, width=2, height=2): + pieces = [] + (image_width, image_height) = image.size + step_x = image_width / width + step_y = image_height / height + if (step_x != int(step_x)): + print('Warning: Imperfect x', step_x) + if (step_y != int(step_y)): + print('Warning: Imperfect y', step_y) + step_x = math.ceil(step_x) + step_y = math.ceil(step_y) + for y in range(height): + end_y = y + 1 + for x in range(width): + end_x = x + 1 + coords = (step_x * x, step_y * y, step_x * end_x, step_y * end_y) + piece = image.crop(coords) + pieces.append(piece) + return pieces + +def drawquarter_argparse(args): + image = PIL.Image.open(args.input_filename) + + if args.output_filename is not None: + output_filename = args.output_filename + else: + output_filename = args.input_filename + + output_path = pathclass.Path(output_filename) + output_directory = output_path.parent + os.makedirs(output_directory.absolute_path, exist_ok=True) + output_filename_format = output_path.basename + output_filename_format = output_filename_format.rsplit('.', 1)[0] + output_filename_format += '_%dx%d_{ycoord}-{xcoord}.' % (args.width, args.height) + output_filename_format += args.input_filename.rsplit('.', 1)[1] + + pieces = drawquarter(image, width=args.width, height=args.height) + for (index, piece) in enumerate(pieces): + (ycoord, xcoord) = divmod(index, args.height) + output_filename = output_filename_format.format(xcoord=xcoord, ycoord=ycoord) + output_filename = output_directory.with_child(output_filename) + print(output_filename.relative_path) + piece.save(output_filename.absolute_path) + +def main(argv): + parser = argparse.ArgumentParser() + + parser.add_argument('input_filename') + parser.add_argument('output_filename', nargs='?', default=None) + parser.add_argument('--width', dest='width', type=int, default=2) + parser.add_argument('--height', dest='height', type=int, default=2) + parser.set_defaults(func=drawquarter_argparse) + + args = parser.parse_args(argv) + args.func(args) + +if __name__ == '__main__': + main(sys.argv[1:]) diff --git a/Toolbox/mp3slice.py b/Toolbox/mp3slice.py new file mode 100644 index 0000000..bcf7a11 --- /dev/null +++ b/Toolbox/mp3slice.py @@ -0,0 +1,171 @@ +''' +This script cuts an audio file into multiple files when you provide the +timestamps and titles for each. + +> mp3slice bigfile.mp3 00:00-01:00 part1.mp3 01:00-02:00 part2.mp3 +''' + +import argparse +import os +import sys + +from voussoirkit import bytestring + + +def parse_rules(lines): + rules = [] + for (times, title) in lines[::-1]: + rule = {'title': title} + (start, end) = hyphen_range(times) + if start is None: + raise ValueError('Null start') + rule['start'] = start + if end is None and len(rules) > 0: + end = rules[-1]['start'] + rule['end'] = end + rules.append(rule) + rules.sort(key=lambda x: x.get('start')) + return rules + +def read_rulefile(filename): + text = None + for encoding in [None, 'utf-8']: + try: + with open(filename, 'r', encoding=encoding) as handle: + text = handle.read() + break + except UnicodeError: + pass + else: + raise UnicodeError() + + lines = [l.strip() for l in text.strip().splitlines()] + lines = [l for l in lines if l] + rules = [l.split(maxsplit=1) for l in lines] + return parse_rules(rules) + +def chunk_sequence(sequence, chunk_length, allow_incomplete=True): + ''' + Given a sequence, divide it into sequences of length `chunk_length`. + + allow_incomplete: + If True, allow the final chunk to be shorter if the + given sequence is not an exact multiple of `chunk_length`. + If False, the incomplete chunk will be discarded. + ''' + (complete, leftover) = divmod(len(sequence), chunk_length) + if not allow_incomplete: + leftover = 0 + + chunk_count = complete + min(leftover, 1) + + chunks = [] + for x in range(chunk_count): + left = chunk_length * x + right = left + chunk_length + chunks.append(sequence[left:right]) + + return chunks + +def hyphen_range(s): + ''' + Given a string like '1-3', return numbers (1, 3) representing lower + and upper bounds. + + Supports bytestring.parsebytes and hh:mm:ss format, for example + '1k-2k', '10:00-20:00', '4gib-' + ''' + s = s.strip() + s = s.replace(' ', '') + if not s: + return (None, None) + parts = s.split('-') + parts = [part.strip() or None for part in parts] + if len(parts) == 1: + low = parts[0] + high = None + elif len(parts) == 2: + (low, high) = parts + else: + raise ValueError('Too many hyphens') + + low = _unitconvert(low) + high = _unitconvert(high) + if low is not None and high is not None and low > high: + raise exceptions.OutOfOrder(range=s, min=low, max=high) + return low, high + +def hms_to_seconds(hms): + ''' + Convert hh:mm:ss string to an integer seconds. + ''' + hms = hms.split(':') + seconds = 0 + if len(hms) == 3: + seconds += int(hms[0])*3600 + hms.pop(0) + if len(hms) == 2: + seconds += int(hms[0])*60 + hms.pop(0) + if len(hms) == 1: + seconds += float(hms[0]) + return seconds + +def _unitconvert(value): + ''' + When parsing hyphenated ranges, this function is used to convert + strings like "1k" to 1024 and "1:00" to 60. + ''' + if value is None: + return None + if ':' in value: + return hms_to_seconds(value) + elif all(c in '0123456789.' for c in value): + return float(value) + else: + return bytestring.parsebytes(value) + +def example_argparse(args): + if len(args.rules) == 1 and os.path.isfile(args.rules[0]): + rules = read_rulefile(args.rules[0]) + else: + rules = args.rules + rules = chunk_sequence(rules, 2) + if len(rules[-1]) != 2: + raise ValueError('Odd-number parameters') + rules = parse_rules(rules) + + extension = os.path.splitext(args.input_filename)[1] + outputters = [] + for rule in rules: + outputter = [] + if not rule['title'].endswith(extension): + rule['title'] += extension + outputter.append('-ss') + outputter.append(str(rule['start'])) + if rule['end'] is not None: + outputter.append('-to') + outputter.append(str(rule['end'])) + outputter.append(' -c copy') + outputter.append('"%s"' % rule['title']) + print(outputter) + outputter = ' '.join(outputter) + outputters.append(outputter) + outputters = ' '.join(outputters) + command = 'ffmpeg -i "%s" %s' % (args.input_filename, outputters) + print(command) + os.system(command) + + +def main(argv): + parser = argparse.ArgumentParser() + + parser.add_argument('input_filename') + parser.add_argument('rules', nargs='+', default=None) + parser.set_defaults(func=example_argparse) + + args = parser.parse_args(argv) + args.func(args) + +if __name__ == '__main__': + main(sys.argv[1:]) diff --git a/Toolbox/reverse.py b/Toolbox/reverse.py new file mode 100644 index 0000000..bef9c56 --- /dev/null +++ b/Toolbox/reverse.py @@ -0,0 +1,7 @@ +import sys + +from voussoirkit import clipext + +arg = clipext.resolve(sys.argv[1]) +arg = ''.join(reversed(arg)) +print(arg) diff --git a/Toolbox/search.py b/Toolbox/search.py index e5bac9f..cd33666 100644 --- a/Toolbox/search.py +++ b/Toolbox/search.py @@ -54,7 +54,7 @@ def search( 'not_all': not_all, 'not_any': not_any } - terms = {k: (v or []) for (k, v) in terms.items()} + terms = {k: ([v] if isinstance(v, str) else v or []) for (k, v) in terms.items()} #print(terms, content_args) if all(v == [] for v in terms.values()) and not content_args: