From 8a0fcb09ca1cabc816f3a328d0fdd172b4c9dd67 Mon Sep 17 00:00:00 2001 From: Ethan Dalool Date: Tue, 11 Jun 2019 22:32:32 -0700 Subject: [PATCH] Delete toolbox folder in preference of new repository. --- Toolbox/README.md | 4 - Toolbox/allexecutables.py | 15 -- Toolbox/bitrate_chart.py | 50 ------ Toolbox/brename.py | 112 ------------- Toolbox/breplace.py | 24 --- Toolbox/clipboard.py | 14 -- Toolbox/contentreplace.py | 65 -------- Toolbox/crop.py | 21 --- Toolbox/drawn_quartered.py | 73 --------- Toolbox/eval.py | 30 ---- Toolbox/filenameorderedrandomness.pyw | 26 --- Toolbox/filenamescramble.py | 23 --- Toolbox/filenamescrambleint.pyw | 21 --- Toolbox/fileprefix.py | 112 ------------- Toolbox/filepull.py | 57 ------- Toolbox/forline.py | 14 -- Toolbox/getcrx.py | 142 ---------------- Toolbox/head.py | 16 -- Toolbox/hexpng.py | 42 ----- Toolbox/kbps.py | 75 --------- Toolbox/linenumbers.py | 12 -- Toolbox/mp3slice.py | 171 -------------------- Toolbox/randomfile.py | 46 ------ Toolbox/rejpg.py | 48 ------ Toolbox/repeat.py | 16 -- Toolbox/resize.py | 52 ------ Toolbox/reverse.py | 7 - Toolbox/search.py | 224 -------------------------- Toolbox/sorted.py | 19 --- Toolbox/subtitle_shift.py | 67 -------- Toolbox/subtitle_shift_pointsync.py | 197 ---------------------- Toolbox/timestampfilename.pyw | 25 --- Toolbox/touch.py | 24 --- Toolbox/unique.py | 20 --- 34 files changed, 1864 deletions(-) delete mode 100644 Toolbox/README.md delete mode 100644 Toolbox/allexecutables.py delete mode 100644 Toolbox/bitrate_chart.py delete mode 100644 Toolbox/brename.py delete mode 100644 Toolbox/breplace.py delete mode 100644 Toolbox/clipboard.py delete mode 100644 Toolbox/contentreplace.py delete mode 100644 Toolbox/crop.py delete mode 100644 Toolbox/drawn_quartered.py delete mode 100644 Toolbox/eval.py delete mode 100644 Toolbox/filenameorderedrandomness.pyw delete mode 100644 Toolbox/filenamescramble.py delete mode 100644 Toolbox/filenamescrambleint.pyw delete mode 100644 Toolbox/fileprefix.py delete mode 100644 Toolbox/filepull.py delete mode 100644 Toolbox/forline.py delete mode 100644 Toolbox/getcrx.py delete mode 100644 Toolbox/head.py delete mode 100644 Toolbox/hexpng.py delete mode 100644 Toolbox/kbps.py delete mode 100644 Toolbox/linenumbers.py delete mode 100644 Toolbox/mp3slice.py delete mode 100644 Toolbox/randomfile.py delete mode 100644 Toolbox/rejpg.py delete mode 100644 Toolbox/repeat.py delete mode 100644 Toolbox/resize.py delete mode 100644 Toolbox/reverse.py delete mode 100644 Toolbox/search.py delete mode 100644 Toolbox/sorted.py delete mode 100644 Toolbox/subtitle_shift.py delete mode 100644 Toolbox/subtitle_shift_pointsync.py delete mode 100644 Toolbox/timestampfilename.pyw delete mode 100644 Toolbox/touch.py delete mode 100644 Toolbox/unique.py diff --git a/Toolbox/README.md b/Toolbox/README.md deleted file mode 100644 index 7ae7581..0000000 --- a/Toolbox/README.md +++ /dev/null @@ -1,4 +0,0 @@ -Toolbox -======= - -These are just minor or single-purpose scripts that I use to automate some housework. They aren't really important enough to separate into folders. \ No newline at end of file diff --git a/Toolbox/allexecutables.py b/Toolbox/allexecutables.py deleted file mode 100644 index 1de07c3..0000000 --- a/Toolbox/allexecutables.py +++ /dev/null @@ -1,15 +0,0 @@ -import os - -paths = os.getenv('PATH').split(';') -paths = [p for p in paths if os.path.exists(p)] - -extensions = os.getenv('PATHEXT').split(';') -extensions = [e.lower() for e in extensions] -print('Extensions according to PATHEXT:', extensions) - -for path in paths: - print(path) - files = os.listdir(path) - files = [f for f in files if any(f.lower().endswith(e) for e in extensions)] - files = [' ' + f for f in files] - print('\n'.join(files)) diff --git a/Toolbox/bitrate_chart.py b/Toolbox/bitrate_chart.py deleted file mode 100644 index b143f29..0000000 --- a/Toolbox/bitrate_chart.py +++ /dev/null @@ -1,50 +0,0 @@ -''' - bitrate | 01 | 1:00 | 30:00 | 1:00:00 | 1:30:00 | 2:00:00 - -: | -: | -: | -: | -: | -: | -: - 128 kbps | 16.000 KiB | 960.000 KiB | 28.125 MiB | 56.250 MiB | 84.375 MiB | 112.500 MiB - 256 kbps | 32.000 KiB | 1.875 MiB | 56.250 MiB | 112.500 MiB | 168.750 MiB | 225.000 MiB - 320 kbps | 40.000 KiB | 2.344 MiB | 70.312 MiB | 140.625 MiB | 210.938 MiB | 281.250 MiB - 500 kbps | 62.500 KiB | 3.662 MiB | 109.863 MiB | 219.727 MiB | 329.590 MiB | 439.453 MiB - 640 kbps | 80.000 KiB | 4.688 MiB | 140.625 MiB | 281.250 MiB | 421.875 MiB | 562.500 MiB - 738 kbps | 92.250 KiB | 5.405 MiB | 162.158 MiB | 324.316 MiB | 486.475 MiB | 648.633 MiB - 1024 kbps | 128.000 KiB | 7.500 MiB | 225.000 MiB | 450.000 MiB | 675.000 MiB | 900.000 MiB - 2048 kbps | 256.000 KiB | 15.000 MiB | 450.000 MiB | 900.000 MiB | 1.318 GiB | 1.758 GiB - 2330 kbps | 291.271 KiB | 17.067 MiB | 512.000 MiB | 1.000 GiB | 1.500 GiB | 2.000 GiB - 3072 kbps | 384.000 KiB | 22.500 MiB | 675.000 MiB | 1.318 GiB | 1.978 GiB | 2.637 GiB - 4096 kbps | 512.000 KiB | 30.000 MiB | 900.000 MiB | 1.758 GiB | 2.637 GiB | 3.516 GiB - 4660 kbps | 582.543 KiB | 34.133 MiB | 1.000 GiB | 2.000 GiB | 3.000 GiB | 4.000 GiB - 6144 kbps | 768.000 KiB | 45.000 MiB | 1.318 GiB | 2.637 GiB | 3.955 GiB | 5.273 GiB - 8192 kbps | 1.000 MiB | 60.000 MiB | 1.758 GiB | 3.516 GiB | 5.273 GiB | 7.031 GiB -12288 kbps | 1.500 MiB | 90.000 MiB | 2.637 GiB | 5.273 GiB | 7.910 GiB | 10.547 GiB -16384 kbps | 2.000 MiB | 120.000 MiB | 3.516 GiB | 7.031 GiB | 10.547 GiB | 14.062 GiB -''' -import sys -import kbps - -from voussoirkit import bytestring - -times = ['01', '1:00', '30:00', '1:00:00', '1:30:00', '2:00:00'] -rates = [128, 256, 320, 500, 640, 738, 1024, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 2330.17, 4660.34] - -times.sort(key=lambda x: kbps.hms_s(x)) -rates.sort() - -table = [] -table.append('bitrate | ' + ' | '.join(times)) -table.append('-: | ' * (len(times)+1)) -for r in rates: - l = [] - l.append('%d kbps' % r) - for t in times: - l.append(kbps.kbps(time=t, kbps=r)) - l = ' | '.join(l) - table.append(l) - -#print('\n'.join(table)) -columns = [[b.strip() for b in a] for a in zip(*[x.split('|') for x in table])] -for (index, column) in enumerate(columns): - width = max((len(x) for x in column)) - columns[index] = [x.rjust(width, ' ') for x in column] -table = [' | '.join(a) for a in zip(*columns)] -#print(columns) -print('\n'.join(table)) \ No newline at end of file diff --git a/Toolbox/brename.py b/Toolbox/brename.py deleted file mode 100644 index 3706a7b..0000000 --- a/Toolbox/brename.py +++ /dev/null @@ -1,112 +0,0 @@ -''' -Batch rename files by providing a string to be `eval`ed, using variable `x` as -the current filename. -Yes I know this is weird, but for certain tasks it's just too quick and easy to pass up. - -For example: - -Prefix all the files: -brename.py "'Test_' + x" - -Keep the first word and extension: -brename.py "(x.split(' ')[0] + '.' + x.split('.')[-1]) if ' ' in x else x" -''' -import argparse -import os -import random -import re -import sys - -from voussoirkit import safeprint - -dot = '.' -quote = '"' -apostrophe = "'" -space = ' ' - -def brename(transformation, autoyes=False): - old = os.listdir() - new = [] - for (index, x) in enumerate(old): - (noext, ext) = os.path.splitext(x) - x = eval(transformation) - new.append(x) - pairs = [] - for (x, y) in zip(old, new): - if x == y: - continue - pairs.append((x, y)) - - if not loop(pairs, dry=True): - print('Nothing to replace') - return - - ok = autoyes - if not ok: - print('Is this correct? y/n') - ok = input('>').lower() in ('y', 'yes', 'yeehaw') - - if ok: - loop(pairs, dry=False) - -def excise(s, mark_left, mark_right): - ''' - Remove the text between the left and right landmarks, inclusive, returning - the rest of the text. - - excise('What a wonderful day [soundtrack].mp3', ' [', ']') -> - returns 'What a wonderful day.mp3' - ''' - return s.split(mark_left)[0] + s.split(mark_right)[-1] - -def longest_length(li): - longest = 0 - for item in li: - longest = max(longest, len(item)) - return longest - -def loop(pairs, dry=False): - has_content = False - for (x, y) in pairs: - if dry: - line = '{old}\n{new}\n' - line = line.format(old=x, new=y) - #print(line.encode('utf-8')) - safeprint.safeprint(line) - has_content = True - else: - os.rename(x, y) - return has_content - -def title(text): - (text, extension) = os.path.splitext(text) - text = text.title() - if ' ' in text: - (first, rest) = text.split(' ', 1) - else: - (first, rest) = (text, '') - rest = ' %s ' % rest - for article in ['The', 'A', 'An', 'At', 'To', 'In', 'Of', 'From', 'And']: - article = ' %s ' % article - rest = rest.replace(article, article.lower()) - rest = rest.strip() - if rest != '': - rest = ' ' + rest - text = first + rest + extension - return text - -def brename_argparse(args): - brename(args.transformation, autoyes=args.autoyes) - -def main(argv): - parser = argparse.ArgumentParser(description=__doc__) - - parser.add_argument('transformation', help='python command using x as variable name') - parser.add_argument('-y', '--yes', dest='autoyes', action='store_true', help='accept results without confirming') - parser.set_defaults(func=brename_argparse) - - args = parser.parse_args(argv) - args.func(args) - -if __name__ == '__main__': - raise SystemExit(main(sys.argv[1:])) diff --git a/Toolbox/breplace.py b/Toolbox/breplace.py deleted file mode 100644 index e65de2d..0000000 --- a/Toolbox/breplace.py +++ /dev/null @@ -1,24 +0,0 @@ -''' -Batch rename files by replacing the first argument with the second. -''' -import argparse -import brename -import sys - -def breplace_argparse(args): - command = f'x.replace("{args.replace_from}", "{args.replace_to}")' - brename.brename(command, autoyes=args.autoyes) - -def main(argv): - parser = argparse.ArgumentParser(__doc__) - - parser.add_argument('replace_from') - parser.add_argument('replace_to') - parser.add_argument('-y', '--yes', dest='autoyes', action='store_true', help='accept results without confirming') - parser.set_defaults(func=breplace_argparse) - - args = parser.parse_args(argv) - args.func(args) - -if __name__ == '__main__': - raise SystemExit(main(sys.argv[1:])) diff --git a/Toolbox/clipboard.py b/Toolbox/clipboard.py deleted file mode 100644 index 2355a6e..0000000 --- a/Toolbox/clipboard.py +++ /dev/null @@ -1,14 +0,0 @@ -''' -Dump the clipboard to stdout. I use this for redirecting to files. -''' -import pyperclip -import sys - -if len(sys.argv) > 1: - from voussoirkit import clipext - stuff = clipext.resolve(sys.argv[1]) - pyperclip.copy(stuff) -else: - text = pyperclip.paste() - text = text.replace('\r', '') - print(text) diff --git a/Toolbox/contentreplace.py b/Toolbox/contentreplace.py deleted file mode 100644 index fcdeb18..0000000 --- a/Toolbox/contentreplace.py +++ /dev/null @@ -1,65 +0,0 @@ -import argparse -import codecs -import glob -import sys -import pyperclip - - -def contentreplace(filename, replace_from, replace_to, autoyes=False): - f = open(filename, 'r', encoding='utf-8') - with f: - content = f.read() - - occurances = content.count(replace_from) - - print(f'{filename}: Found {occurances} occurences.') - if occurances == 0: - return - - permission = autoyes or (input('Replace? ').lower() in ('y', 'yes')) - if not permission: - return - - content = content.replace(replace_from, replace_to) - - f = open(filename, 'w', encoding='utf-8') - with f: - f.write(content) - -def contentreplace_argparse(args): - filenames = glob.glob(args.filename_glob) - - if args.clip_prompt: - replace_from = input('Ready from') - if not replace_from: - replace_from = pyperclip.paste() - replace_to = input('Ready to') - if not replace_to: - replace_to = pyperclip.paste() - else: - replace_from = codecs.decode(args.replace_from, 'unicode_escape') - replace_to = codecs.decode(args.replace_to, 'unicode_escape') - - for filename in filenames: - contentreplace( - filename, - replace_from, - replace_to, - autoyes=args.autoyes, - ) - -def main(argv): - parser = argparse.ArgumentParser(description=__doc__) - - parser.add_argument('filename_glob') - parser.add_argument('replace_from') - parser.add_argument('replace_to') - parser.add_argument('-y', '--yes', dest='autoyes', action='store_true', help='accept results without confirming') - parser.add_argument('--clip_prompt', dest='clip_prompt', action='store_true') - parser.set_defaults(func=contentreplace_argparse) - - args = parser.parse_args(argv) - args.func(args) - -if __name__ == '__main__': - raise SystemExit(main(sys.argv[1:])) diff --git a/Toolbox/crop.py b/Toolbox/crop.py deleted file mode 100644 index 60fbebe..0000000 --- a/Toolbox/crop.py +++ /dev/null @@ -1,21 +0,0 @@ -import os -import sys -from PIL import Image - -filename = sys.argv[1] -(name, extension) = os.path.splitext(filename) -newname = '%s_cropped%s' % (name, extension) - -crops = sys.argv[2:] -crops = ' '.join(crops) -crops = crops.replace(',', ' ') -crops = crops.replace(' ', ' ') -crops = crops.split(' ') -crops = [int(x) for x in crops] -crops = list(crops) -print(crops) -i = Image.open(filename) -if len(crops) == 2: - crops.extend(i.size) -i = i.crop(crops) -i.save(newname, quality=100) diff --git a/Toolbox/drawn_quartered.py b/Toolbox/drawn_quartered.py deleted file mode 100644 index 1e22412..0000000 --- a/Toolbox/drawn_quartered.py +++ /dev/null @@ -1,73 +0,0 @@ -''' -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/eval.py b/Toolbox/eval.py deleted file mode 100644 index 5968abb..0000000 --- a/Toolbox/eval.py +++ /dev/null @@ -1,30 +0,0 @@ -''' -Great for applying Python post-processing to the output of some other command. -Provide an input string (!i for stdin) and an eval string using `x` as the -variable name of the input. -''' -from voussoirkit import clipext -import math -import os -import random -import string -import sys -import time - -if '--lines' in sys.argv: - by_lines = True - sys.argv.remove('--lines') -else: - by_lines = False -text = clipext.resolve(sys.argv[1], split_lines=by_lines) -transformation = ' '.join(sys.argv[2:]) - -if by_lines: - for line in text: - x = line - result = eval(transformation) - print(result) -else: - x = text - result = eval(transformation) - print(result) diff --git a/Toolbox/filenameorderedrandomness.pyw b/Toolbox/filenameorderedrandomness.pyw deleted file mode 100644 index 142cca0..0000000 --- a/Toolbox/filenameorderedrandomness.pyw +++ /dev/null @@ -1,26 +0,0 @@ -''' -Drag multiple files on top of this .py file. The first file will have its -name randomly scrambled into 12 digits. The others will increment that number b -1. -''' - -print('hi') -import os -import random -import string -import sys - -argv = sys.argv[1:] -print(''.join(c for c in argv if c in string.printable)) - -randname = [random.choice(string.digits) for x in range(12)] -randname = int(''.join(randname)) -for filepath in argv: - folder = os.path.dirname(filepath) - basename = os.path.basename(filepath) - extension = os.path.splitext(basename)[1] - newname = str(randname).rjust(12, '0') - randname += 1 - newname = '%s\\%s%s' % (folder, newname, extension) - os.rename(filepath, newname) - print('%s -> %s' % (filepath, newname)) \ No newline at end of file diff --git a/Toolbox/filenamescramble.py b/Toolbox/filenamescramble.py deleted file mode 100644 index 86f4394..0000000 --- a/Toolbox/filenamescramble.py +++ /dev/null @@ -1,23 +0,0 @@ -''' -Drag a file on top of this .py file, and it will have its -filename scrambled into a combination of 16 upper and lowercase -letters. -''' - -import os -import random -import string -import sys - -argv = sys.argv[1:] -print(''.join(c for c in argv if c in string.printable)) -for filepath in argv: - folder = os.path.dirname(filepath) - basename = os.path.basename(filepath) - extension = os.path.splitext(basename)[1] - newname = [random.choice(string.ascii_lowercase) for x in range(9)] - newname = ''.join(newname) - newname = newname + extension - newname = os.path.join(folder, newname) - #os.rename(filepath, newname) - print('%s -> %s' % (filepath, newname)) diff --git a/Toolbox/filenamescrambleint.pyw b/Toolbox/filenamescrambleint.pyw deleted file mode 100644 index a102856..0000000 --- a/Toolbox/filenamescrambleint.pyw +++ /dev/null @@ -1,21 +0,0 @@ -''' -Drag a file on top of this .py file, and it will have its -filename scrambled into a combination of 12 digits. -''' - -import os -import random -import string -import sys - -argv = sys.argv[1:] -print(''.join(c for c in argv if c in string.printable)) -for filepath in argv: - folder = os.path.dirname(filepath) - basename = os.path.basename(filepath) - extension = os.path.splitext(basename)[1] - newname = [random.choice(string.digits) for x in range(12)] - newname = ''.join(newname) - newname = '%s\\%s%s' % (folder, newname, extension) - os.rename(filepath, newname) - print('%s -> %s' % (filepath, newname)) \ No newline at end of file diff --git a/Toolbox/fileprefix.py b/Toolbox/fileprefix.py deleted file mode 100644 index 4f08c72..0000000 --- a/Toolbox/fileprefix.py +++ /dev/null @@ -1,112 +0,0 @@ -''' -When you run this file from the commandline given a single argument, all -of the files in the current working directory will be renamed in the format -{argument}_{count} where argument is your cmd input and count is a zero-padded -integer that counts each file in the folder. -''' - -import argparse -import os -import random -import string -import re -import sys - -from voussoirkit import pathclass -from voussoirkit import safeprint - -IGNORE_EXTENSIONS = ['py', 'lnk', 'ini'] - - -def natural_sorter(x): - ''' - http://stackoverflow.com/a/11150413 - ''' - convert = lambda text: int(text) if text.isdigit() else text.lower() - alphanum_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key)] - return alphanum_key(x) - -def fileprefix( - prefix='', - sep='_', - ctime=False, - autoyes=False, - ): - current_directory = pathclass.Path('.') - - prefix = prefix.strip() - if prefix == ':': - prefix = current_directory.basename + ' - ' - elif prefix != '': - prefix += sep - - filepaths = current_directory.listdir() - filepaths = [f for f in filepaths if f.is_file] - filepaths = [f for f in filepaths if f.extension.lower() not in IGNORE_EXTENSIONS] - - try: - pyfile = pathclass.Path(__file__) - filepaths.remove(pyfile) - except ValueError: - pass - - # trust me on this. - zeropadding = len(str(len(filepaths))) - zeropadding = max(2, zeropadding) - zeropadding = str(zeropadding) - - format = '{{prefix}}{{index:0{pad}d}}{{extension}}'.format(pad=zeropadding) - - if ctime: - print('Sorting by time') - filepaths.sort(key=lambda x: x.stat.st_ctime) - else: - print('Sorting by name') - filepaths.sort(key=lambda x: natural_sorter(x.basename)) - - rename_pairs = [] - - for (index, filepath) in enumerate(filepaths): - extension = filepath.extension - if extension != '': - extension = '.' + extension - - newname = format.format(prefix=prefix, index=index, extension=extension) - if filepath.basename != newname: - rename_pairs.append((filepath.absolute_path, newname)) - for (oldname, newname) in rename_pairs: - message = f'{oldname} -> {newname}' - safeprint.safeprint(message) - - ok = autoyes - if not ok: - print('Is this correct? y/n') - ok = input('>').lower() in ('y', 'yes', 'yeehaw') - - if ok: - for (oldname, newname) in rename_pairs: - os.rename(oldname, newname) - - -def fileprefix_argparse(args): - return fileprefix( - prefix=args.prefix, - sep=args.sep, - ctime=args.ctime, - autoyes=args.autoyes, - ) - -def main(argv): - parser = argparse.ArgumentParser() - - parser.add_argument('prefix', nargs='?', default='') - parser.add_argument('--sep', dest='sep', default=' ', help='the character between the prefix and remainder') - parser.add_argument('--ctime', dest='ctime', action='store_true', help='sort by ctime instead of filename') - parser.add_argument('-y', '--yes', dest='autoyes', action='store_true', help='accept results without confirming') - parser.set_defaults(func=fileprefix_argparse) - - args = parser.parse_args(argv) - args.func(args) - -if __name__ == '__main__': - main(sys.argv[1:]) diff --git a/Toolbox/filepull.py b/Toolbox/filepull.py deleted file mode 100644 index 32e706c..0000000 --- a/Toolbox/filepull.py +++ /dev/null @@ -1,57 +0,0 @@ -''' -Pull all of the files in nested directories into the current directory. -''' -import argparse -import os -import sys - -from voussoirkit import spinal - -def filepull(pull_from='.', autoyes=False): - files = list(spinal.walk_generator(pull_from)) - cwd = os.getcwd() - files = [f for f in files if os.path.split(f.absolute_path)[0] != cwd] - - if len(files) == 0: - print('No files to move') - return - - duplicate_count = {} - for f in files: - basename = f.basename - duplicate_count.setdefault(basename, []) - duplicate_count[basename].append(f.absolute_path) - - duplicates = ['\n'.join(sorted(copies)) for (basename, copies) in duplicate_count.items() if len(copies) > 1] - duplicates = sorted(duplicates) - if len(duplicates) > 0: - raise Exception('duplicate names:\n' + '\n'.join(duplicates)) - - for f in files: - print(f.basename) - - ok = autoyes - if not ok: - print('Move %d files?' % len(files)) - ok = input('> ').lower() in ['y', 'yes']: - - if ok: - for f in files: - local = os.path.join('.', f.basename) - os.rename(f.absolute_path, local) - -def filepull_argparse(args): - filepull(pull_from=args.pull_from, autoyes=args.autoyes) - -def main(argv): - parser = argparse.ArgumentParser() - - parser.add_argument('pull_from', nargs='?', default='.') - parser.add_argument('-y', '--yes', dest='autoyes', action='store_true') - parser.set_defaults(func=filepull_argparse) - - args = parser.parse_args(argv) - args.func(args) - -if __name__ == '__main__': - main(sys.argv[1:]) diff --git a/Toolbox/forline.py b/Toolbox/forline.py deleted file mode 100644 index e21fe63..0000000 --- a/Toolbox/forline.py +++ /dev/null @@ -1,14 +0,0 @@ -import os -import sys - -from voussoirkit import clipext - -text = sys.argv[1] -command = sys.argv[2:] -command = ['"%s"' % x if (' ' in x or x == '%x') else x for x in command] -command = ' '.join(command) -text = clipext.resolve(text) - -for line in text.splitlines(): - thiscomm = command.replace('%x', line) - os.system(thiscomm) diff --git a/Toolbox/getcrx.py b/Toolbox/getcrx.py deleted file mode 100644 index 5289570..0000000 --- a/Toolbox/getcrx.py +++ /dev/null @@ -1,142 +0,0 @@ -import argparse -import io -import json -import os -import requests -import sys -import time -import traceback -import zipfile - -from voussoirkit import clipext - -FILENAME_BADCHARS = '\\/:*?<>|"' - -WEBSTORE_URL = 'https://chrome.google.com/webstore/detail/x/{extension_id}' -CRX_URL = 'https://clients2.google.com/service/update2/crx?response=redirect&prodversion=59.0&x=id%3D{extension_id}%26installsource%3Dondemand%26uc' - -def sanitize_filename(name): - for c in FILENAME_BADCHARS: - name = name.replace(c, '-') - return name - -def prompt_permission(prompt): - answer = input(prompt) - return answer.lower() in {'yes', 'y'} - -def get_webstore_name_version(extension_id): - url = WEBSTORE_URL.format(extension_id=extension_id) - response = requests.get(url) - try: - name = response.text - name = name.split('meta property="og:title" content="')[1] - name = name.split('"')[0] - except IndexError: - name = None - - try: - version = response.text - version = version.split('meta itemprop="version" content="')[1] - version = version.split('"')[0] - except IndexError: - version = None - - return (name, version) - -def get_crx_name_version(crx_bytes): - crx_handle = io.BytesIO(crx_bytes) - crx_archive = zipfile.ZipFile(crx_handle) - manifest = json.loads(crx_archive.read('manifest.json')) - name = manifest.get('name', None) - version = manifest.get('version', None) - return (name, version) - -def getcrx(extension_id, auto_overwrite=None): - url = CRX_URL.format(extension_id=extension_id) - response = requests.get(url) - response.raise_for_status() - - (name, version) = get_webstore_name_version(extension_id) - if name is None or version is None: - (crx_name, crx_ver) = get_crx_name_version(response.content) - name = name or crx_name - version = version or crx_version - - name = name or extension_id - version = version or time.strftime('%Y%m%d') - - version = version or response.url.split('/')[-1] - - crx_filename = '{name} ({id}) [{version}]' - crx_filename = crx_filename.format( - name=name, - id=extension_id, - version=version, - ) - - if not crx_filename.endswith('.crx'): - crx_filename += '.crx' - - crx_filename = sanitize_filename(crx_filename) - if os.path.isfile(crx_filename): - if auto_overwrite is None: - message = '"%s" already exists. Overwrite?' % crx_filename - permission = prompt_permission(message) - else: - permission = False - else: - permission = True - - if permission: - crx_handle = open(crx_filename, 'wb') - crx_handle.write(response.content) - print('Downloaded "%s".' % crx_filename) - -def getcrx_argparse(args): - extension_ids = [] - - if len(args.extension_ids) == 1: - extension_ids.extend(clipext.resolve(args.extension_ids[0], split_lines=True)) - - elif args.extension_ids: - extension_ids.extend(args.extension_ids) - - if args.file: - with open(args.file, 'r') as handle: - lines = handle.readlines() - extension_ids.extend(lines) - - extension_ids = [x.split('/')[-1].strip() for x in extension_ids] - - if args.overwrite and not args.dont_overwrite: - auto_overwrite = True - elif args.dont_overwrite and not args.overwrite: - auto_overwrite = False - else: - auto_overwrite = None - - for extension_id in extension_ids: - try: - getcrx(extension_id, auto_overwrite=auto_overwrite) - except Exception: - if args.fail_early: - raise - else: - traceback.print_exc() - print('Resuming...') - -def main(argv): - parser = argparse.ArgumentParser() - - parser.add_argument('extension_ids', nargs='*', default=None) - parser.add_argument('--file', dest='file', default=None) - parser.add_argument('--fail_early', dest='fail_early', action='store_true') - parser.add_argument('--overwrite', dest='overwrite', action='store_true') - parser.add_argument('--dont_overwrite', dest='dont_overwrite', action='store_true') - parser.set_defaults(func=getcrx_argparse) - - args = parser.parse_args(argv) - args.func(args) - -if __name__ == '__main__': - main(sys.argv[1:]) diff --git a/Toolbox/head.py b/Toolbox/head.py deleted file mode 100644 index cb747c7..0000000 --- a/Toolbox/head.py +++ /dev/null @@ -1,16 +0,0 @@ -''' -Perform a HEAD request and print the results. -''' -import sys -import json -import requests - -from voussoirkit import clipext - -urls = clipext.resolve(sys.argv[1], split_lines=True) -for url in urls: - page = requests.head(url) - headers = dict(page.headers) - headers = json.dumps(headers, indent=4, sort_keys=True) - print(page) - print(headers) diff --git a/Toolbox/hexpng.py b/Toolbox/hexpng.py deleted file mode 100644 index d901db3..0000000 --- a/Toolbox/hexpng.py +++ /dev/null @@ -1,42 +0,0 @@ -import argparse -import os -import PIL.Image -import sys - - -def full_hex(h): - h = h.replace('#', '') - if len(h) in [3, 4]: - h = ''.join([c * 2 for c in h]) - if len(h) == 6: - h += 'ff' - return h - -def hex_to_rgb(h): - rgb = [int(h[(2*i):(2*i)+2], 16) for i in range(len(h)//2)] - return tuple(rgb) - -def make_hexpng(h, width=1, height=1): - h = full_hex(h) - rgb = hex_to_rgb(h) - filename = '%s.png' % h - i = PIL.Image.new('RGBA', size=[width, height], color=rgb) - print(filename) - i.save(filename) - -def hexpng_argparse(args): - make_hexpng(args.hex_value, width=args.width, height=args.height) - -def main(argv): - parser = argparse.ArgumentParser(description=__doc__) - - parser.add_argument('hex_value') - parser.add_argument('--width', dest='width', type=int, default=1) - parser.add_argument('--height', dest='height', type=int, default=1) - parser.set_defaults(func=hexpng_argparse) - - args = parser.parse_args(argv) - args.func(args) - -if __name__ == '__main__': - raise SystemExit(main(sys.argv[1:])) diff --git a/Toolbox/kbps.py b/Toolbox/kbps.py deleted file mode 100644 index 2edbda1..0000000 --- a/Toolbox/kbps.py +++ /dev/null @@ -1,75 +0,0 @@ -''' -Find time, filesize, or bitrate, given two of the three. - -For example: - -kbps.py --time 1:00:00 --size 2g -kbps.py --time 1:00:00 --kbps 4660 -kbps.py --size 2g --kpbps 4660 -''' -import argparse -import sys - -from voussoirkit import bytestring - -def hms_s(hms): - 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 += int(hms[0]) - return seconds - -def s_hms(s): - (minutes, seconds) = divmod(s, 60) - (hours, minutes) = divmod(minutes, 60) - return '%02d:%02d:%02d' % (hours, minutes, seconds) - -def kbps(time=None, size=None, kbps=None): - if [time, size, kbps].count(None) != 1: - raise ValueError('Incorrect number of unknowns') - - if size is None: - seconds = hms_s(time) - kibs = int(kbps) / 8 - size = kibs * 1024 - size *= seconds - out = bytestring.bytestring(size) - return out - - if time is None: - size = bytestring.parsebytes(size) - kilobits = size / 128 - time = kilobits / int(kbps) - return s_hms(time) - - if kbps is None: - seconds = hms_s(time) - size = bytestring.parsebytes(size) - kibs = size / 1024 - kilobits = kibs * 8 - kbps = kilobits / seconds - kbps = '%d kbps' % int(round(kbps)) - return kbps - -def example_argparse(args): - print(kbps(time=args.time, size=args.size, kbps=args.kbps)) - -def main(argv): - parser = argparse.ArgumentParser() - - parser.add_argument('-t', '--time', dest='time', default=None) - parser.add_argument('-s', '--size', dest='size', default=None) - parser.add_argument('-k', '--kbps', dest='kbps', 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/linenumbers.py b/Toolbox/linenumbers.py deleted file mode 100644 index 5715723..0000000 --- a/Toolbox/linenumbers.py +++ /dev/null @@ -1,12 +0,0 @@ -import sys - -from voussoirkit import clipext - -if len(sys.argv) == 1: - sys.argv.append('!i') -text = clipext.resolve(sys.argv[1]) -lines = text.splitlines() -digits = len(str(len(lines))) -form = '{no:>0%d} | {line}' % digits -for (index, line) in enumerate(lines): - print(form.format(no=index+1, line=line)) diff --git a/Toolbox/mp3slice.py b/Toolbox/mp3slice.py deleted file mode 100644 index bcf7a11..0000000 --- a/Toolbox/mp3slice.py +++ /dev/null @@ -1,171 +0,0 @@ -''' -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/randomfile.py b/Toolbox/randomfile.py deleted file mode 100644 index c9f473b..0000000 --- a/Toolbox/randomfile.py +++ /dev/null @@ -1,46 +0,0 @@ -import math -import random -import sys -from voussoirkit import bytestring -CHUNK_SIZE = 512 * (2 ** 10) -def listget(li, index, fallback=None): - try: - return li[index] - except IndexError: - return fallback - -def rid(length=8): - import random - bits = length * 4 - bits = random.getrandbits(bits) - identifier = '{:02x}'.format(bits).rjust(length, '0') - return identifier - -def make_randomfile(length, filename=None): - if filename is None: - filename = rid(8) + '.txt' - chunks = math.ceil(length / CHUNK_SIZE) - written = 0 - f = open(filename, 'w') - for x in range(chunks): - b = min(CHUNK_SIZE, length-written) - f.write(rid(b)) - written += b - f.close() - print('Created %s' % filename) - - -bytes = listget(sys.argv, 1, None) -if bytes is None: - bytes = 2 ** 10 -else: - bytes = bytestring.parsebytes(bytes) - -filecount = 1 -filename = listget(sys.argv, 2, None) -if filename is not None and filename.isdigit(): - filecount = int(filename) - filename = None - -for x in range(filecount): - make_randomfile(bytes, filename) diff --git a/Toolbox/rejpg.py b/Toolbox/rejpg.py deleted file mode 100644 index f8a2f98..0000000 --- a/Toolbox/rejpg.py +++ /dev/null @@ -1,48 +0,0 @@ -''' -Recompress all jpg images in the current directory. -Add /r to do nested directories as well. -''' - -from voussoirkit import bytestring -import io -import os -import PIL.Image -import PIL.ImageFile -import string -import sys - -PIL.ImageFile.LOAD_TRUNCATED_IMAGES = True - -if '/r' in sys.argv: - from voussoirkit import spinal - walker = spinal.walk_generator() - files = list(walker) - files = [f.absolute_path for f in files] - -else: - files = os.listdir() - files = [f for f in files if os.path.isfile(f)] - -files = [f for f in files if any(ext in f.lower() for ext in ['.jpg', '.jpeg'])] - -bytes_saved = 0 -remaining_size = 0 -for filename in files: - print(''.join(c for c in filename if c in string.printable)) - bytesio = io.BytesIO() - i = PIL.Image.open(filename) - i.save(bytesio, format='jpeg', quality=80) - - bytesio.seek(0) - new_bytes = bytesio.read() - old_size = os.path.getsize(filename) - new_size = len(new_bytes) - remaining_size += new_size - if new_size < old_size: - bytes_saved += (old_size - new_size) - f = open(filename, 'wb') - f.write(new_bytes) - f.close() - -print('Saved', bytestring.bytestring(bytes_saved)) -print('Remaining are', bytestring.bytestring(remaining_size)) \ No newline at end of file diff --git a/Toolbox/repeat.py b/Toolbox/repeat.py deleted file mode 100644 index 341bc33..0000000 --- a/Toolbox/repeat.py +++ /dev/null @@ -1,16 +0,0 @@ -''' -Repeat the input as many times as you want - -> repeat "hello" 8 -> echo hi | repeat !i 4 -''' - -import sys - -from voussoirkit import clipext - -text = clipext.resolve(sys.argv[1]) -repeat_times = int(sys.argv[2]) - -for t in range(repeat_times): - print(text) diff --git a/Toolbox/resize.py b/Toolbox/resize.py deleted file mode 100644 index 5c28f84..0000000 --- a/Toolbox/resize.py +++ /dev/null @@ -1,52 +0,0 @@ -import glob -import os -from PIL import Image -import sys - -def fit_into_bounds(image_width, image_height, frame_width, frame_height): - ''' - Given the w+h of the image and the w+h of the frame, - return new w+h that fits the image into the frame - while maintaining the aspect ratio. - - (1920, 1080, 400, 400) -> (400, 225) - ''' - width_ratio = frame_width / image_width - height_ratio = frame_height / image_height - ratio = min(width_ratio, height_ratio) - - new_width = int(image_width * ratio) - new_height = int(image_height * ratio) - - return (new_width, new_height) - -filenames = sys.argv[1] - -filenames = glob.glob(filenames) -for filename in filenames: - i = Image.open(filename) - if all(x.isdigit() for x in sys.argv[2:3]): - new_x = int(sys.argv[2]) - new_y = int(sys.argv[3]) - else: - try: - ratio = float(sys.argv[2]) - new_x = int(i.size[0] * ratio) - new_y = int(i.size[1] * ratio) - except ValueError: - print('you did it wrong') - quit() - - (image_width, image_height) = i.size - - if new_x == 0: - (new_x, new_y) = fit_into_bounds(image_width, image_height, 10000000, new_y) - if new_y == 0: - (new_x, new_y) = fit_into_bounds(image_width, image_height, new_x, 10000000) - - print(i.size, new_x, new_y) - i = i.resize( (new_x, new_y), Image.ANTIALIAS) - suffix = '_{width}x{height}'.format(width=new_x, height=new_y) - (base, extension) = os.path.splitext(filename) - newname = base + suffix + extension - i.save(newname, quality=100) diff --git a/Toolbox/reverse.py b/Toolbox/reverse.py deleted file mode 100644 index bef9c56..0000000 --- a/Toolbox/reverse.py +++ /dev/null @@ -1,7 +0,0 @@ -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 deleted file mode 100644 index 1cce33f..0000000 --- a/Toolbox/search.py +++ /dev/null @@ -1,224 +0,0 @@ -import argparse -import fnmatch -import itertools -import os -import re -import stat -import sys -import traceback - -from voussoirkit import clipext -from voussoirkit import expressionmatch -from voussoirkit import pathclass -from voussoirkit import safeprint -from voussoirkit import spinal - -# Thanks georg -# http://stackoverflow.com/a/13443424 -STDIN_MODE = os.fstat(sys.stdin.fileno()).st_mode -if stat.S_ISFIFO(STDIN_MODE): - STDIN_MODE = 'pipe' -else: - STDIN_MODE = 'terminal' - -def all_terms_match(search_text, terms, match_function): - matches = ( - (not terms['yes_all'] or all(match_function(search_text, term) for term in terms['yes_all'])) and - (not terms['yes_any'] or any(match_function(search_text, term) for term in terms['yes_any'])) and - (not terms['not_all'] or not all(match_function(search_text, term) for term in terms['not_all'])) and - (not terms['not_any'] or not any(match_function(search_text, term) for term in terms['not_any'])) - ) - return matches - -def search( - *, - yes_all=None, - yes_any=None, - not_all=None, - not_any=None, - case_sensitive=False, - content_args=None, - do_expression=False, - do_glob=False, - do_regex=False, - line_numbers=False, - local_only=False, - text=None, - ): - terms = { - 'yes_all': yes_all, - 'yes_any': yes_any, - 'not_all': not_all, - 'not_any': not_any - } - 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: - raise ValueError('No terms supplied') - - def term_matches(line, term): - if not case_sensitive: - line = line.lower() - - if do_expression: - return term.evaluate(line) - - return ( - (term in line) or - (do_regex and re.search(term, line)) or - (do_glob and fnmatch.fnmatch(line, term)) - ) - - if do_expression: - # The value still needs to be a list so the upcoming any() / all() - # receives an iterable as it expects. It just happens to be 1 tree. - trees = {} - for (key, value) in terms.items(): - if value == []: - trees[key] = [] - continue - tree = ' '.join(value) - tree = expressionmatch.ExpressionTree.parse(tree) - if not case_sensitive: - tree.map(str.lower) - trees[key] = [tree] - terms = trees - - elif not case_sensitive: - terms = {k: [x.lower() for x in v] for (k, v) in terms.items()} - - if text is None: - search_objects = spinal.walk_generator( - depth_first=False, - recurse=not local_only, - yield_directories=True, - ) - else: - search_objects = text.splitlines() - - for (index, search_object) in enumerate(search_objects): - if index % 10 == 0: - #print(index, end='\r', flush=True) - pass - if isinstance(search_object, pathclass.Path): - search_text = search_object.basename - result_text = search_object.absolute_path - else: - search_text = search_object - result_text = search_object - if line_numbers: - result_text = '%4d | %s' % (index+1, result_text) - - if all_terms_match(search_text, terms, term_matches): - if not content_args: - yield result_text - else: - filepath = pathclass.Path(search_object) - if not filepath.is_file: - continue - try: - with open(filepath.absolute_path, 'r') as handle: - text = handle.read() - except UnicodeDecodeError: - try: - with open(filepath.absolute_path, 'r', encoding='utf-8') as handle: - text = handle.read() - except UnicodeDecodeError: - #safeprint.safeprint(filepath.absolute_path) - #traceback.print_exc() - continue - except Exception: - safeprint.safeprint(filepath.absolute_path) - traceback.print_exc() - continue - - content_args['text'] = text - content_args['line_numbers'] = True - results = search(**content_args) - results = list(results) - if not results: - continue - - yield filepath.absolute_path - yield from results - yield '' - -def argparse_to_dict(args): - text = args.text - if text is not None: - text = clipext.resolve(text) - elif STDIN_MODE == 'pipe': - text = clipext.resolve('!i') - - if hasattr(args, 'content_args') and args.content_args is not None: - content_args = argparse_to_dict(args.content_args) - else: - content_args = None - - return { - 'yes_all': args.yes_all, - 'yes_any': args.yes_any, - 'not_all': args.not_all, - 'not_any': args.not_any, - 'case_sensitive': args.case_sensitive, - 'content_args': content_args, - 'do_expression': args.do_expression, - 'do_glob': args.do_glob, - 'do_regex': args.do_regex, - 'local_only': args.local_only, - 'line_numbers': args.line_numbers, - 'text': text, - } - -def search_argparse(args): - generator = search(**argparse_to_dict(args)) - result_count = 0 - for result in generator: - safeprint.safeprint(result) - result_count += 1 - if args.show_count: - print('%d items.' % result_count) - -def main(argv): - parser = argparse.ArgumentParser() - - # The padding is inserted to guarantee that --content is not the first - # argument. Because if it were, we wouldn't know if we have - # [pre, '--content'] or ['--content', post], etc. and I don't want to - # actually check the values. - argv.insert(0, 'padding') - grouper = itertools.groupby(argv, lambda x: x == '--content') - halves = [list(group) for (key, group) in grouper] - # halves looks like [pre, '--content', post] - name_args = halves[0] - # Pop the padding - name_args.pop(0) - content_args = [item for chunk in halves[2:] for item in chunk] - - parser.add_argument('yes_all', nargs='*', default=None) - parser.add_argument('--all', dest='yes_all', nargs='+') - parser.add_argument('--any', dest='yes_any', nargs='+') - parser.add_argument('--not_all', dest='not_all', nargs='+') - parser.add_argument('--not_any', dest='not_any', nargs='+') - - parser.add_argument('--case', dest='case_sensitive', action='store_true') - parser.add_argument('--content', dest='do_content', action='store_true') - parser.add_argument('--count', dest='show_count', action='store_true') - parser.add_argument('--expression', dest='do_expression', action='store_true') - parser.add_argument('--glob', dest='do_glob', action='store_true') - parser.add_argument('--line_numbers', dest='line_numbers', action='store_true') - parser.add_argument('--local', dest='local_only', action='store_true') - parser.add_argument('--regex', dest='do_regex', action='store_true') - parser.add_argument('--text', dest='text', default=None) - parser.set_defaults(func=search_argparse) - - args = parser.parse_args(name_args) - if content_args: - args.content_args = parser.parse_args(content_args) - else: - args.content_args = None - args.func(args) - -if __name__ == '__main__': - raise SystemExit(main(sys.argv[1:])) diff --git a/Toolbox/sorted.py b/Toolbox/sorted.py deleted file mode 100644 index 02a91bf..0000000 --- a/Toolbox/sorted.py +++ /dev/null @@ -1,19 +0,0 @@ -''' -Sort the lines coming from stdin and print them. -''' -from voussoirkit import clipext -import sys - -if len(sys.argv) > 1: - text = clipext.resolve(sys.argv[1]) -else: - text = clipext.resolve('!input') - -text = text.split('\n') -if '-l' in sys.argv: - text.sort(key=lambda x: x.lower()) -else: - text.sort() - -new_text = '\n'.join(text) -print(new_text) diff --git a/Toolbox/subtitle_shift.py b/Toolbox/subtitle_shift.py deleted file mode 100644 index 703e415..0000000 --- a/Toolbox/subtitle_shift.py +++ /dev/null @@ -1,67 +0,0 @@ -''' -Usage: - -Shift all subtitles 10 seconds forward: -> subtitle_shift file.srt +10 - -Shift all subtitles 10 seconds backward: -> subtitle_shift file.srt -10 - -This will produce "file_correct.srt" with the new timestamps. -''' - -import os -import sys -filename = sys.argv[1] -offset = float(sys.argv[2]) -f = open(filename, 'r') - -lines = [l.strip() for l in f.readlines()] -for (lineindex, line) in enumerate(lines): - changed = False - - if '-->' not in line: - continue - - words = line.split(' ') - for (wordindex, word) in enumerate(words): - word = word.replace('.', ',') - if not (':' in word and ',' in word): - continue - - if not word.replace(':', '').replace(',', '').isdigit(): - continue - - # 1.) 01:23:45,678 --> 02:34:56,789 | our input - # 2.) 01:23:45:678 --> 02:34:56:789 | comma to colon - # 3.) 5025.678 --> 9296.789 | split by colon and sum - # 4.) 5035.678 --> 9306.789 | add offset - # 5.) 01:23:55.678 --> 02:35:06.789 | reformat - # 6.) 01:23:55,678 --> 02:35:06,789 | period to comma - word = word.replace(',', ':') - (hours, minutes, seconds, mili) = [int(x) for x in word.split(':')] - seconds = (3600 * hours) + (60 * minutes) + (seconds) + (mili / 1000) - - seconds += offset - (hours, seconds) = divmod(seconds, 3600) - (minutes, seconds) = divmod(seconds, 60) - - if hours < 0: - raise Exception('Negative time') - - word = '%02d:%02d:%06.3f' % (hours, minutes, seconds) - word = word.replace('.', ',') - changed = True - words[wordindex] = word - - if changed: - line = ' '.join(words) - print(line) - lines[lineindex] = line - -lines = '\n'.join(lines) -(name, extension) = os.path.splitext(filename) -newname = name + '_correct' + extension -x = open(newname, 'w') -x.write(lines) -x.close() \ No newline at end of file diff --git a/Toolbox/subtitle_shift_pointsync.py b/Toolbox/subtitle_shift_pointsync.py deleted file mode 100644 index 3a243ae..0000000 --- a/Toolbox/subtitle_shift_pointsync.py +++ /dev/null @@ -1,197 +0,0 @@ -import argparse -import sys - -inf = float('inf') - -class Subtitles: - def __init__(self, lines): - self.lines = sorted(lines) - - @classmethod - def from_text(cls, text): - text = text.strip() - while '\n\n\n' in text: - text = text.replace('\n\n\n', '\n\n') - lines = text.split('\n\n') - lines = [SubtitleLine.from_text(line) for line in lines] - return cls(lines) - - def __getitem__(self, index): - return self.lines[index] - - def __len__(self): - return len(self.lines) - - def __repr__(self): - return f'Subtitles with {len(self.lines)} lines.' - - def as_srt(self): - lines = sorted(self.lines) - lines = [f'{index+1}\n{line.as_srt()}' for (index, line) in enumerate(lines)] - return '\n\n'.join(lines) - -class SubtitleLine: - def __init__(self, start, end, text): - self.start = start - self.end = end - self.text = text - - @classmethod - def from_text(cls, text): - (index, timestamps, text) = text.split('\n', 2) - timestamps = timestamps.replace(',', '.') - (start, end) = timestamps.split('-->') - start = hms_to_seconds(start) - end = hms_to_seconds(end) - return cls(start, end, text) - - def __lt__(self, other): - return (self.start, self.end) < (other.start, other.end) - - def __repr__(self): - return repr(self.as_srt()) - - def as_srt(self): - start = seconds_to_hms(self.start, force_hours=True, force_milliseconds=True) - end = seconds_to_hms(self.end, force_hours=True, force_milliseconds=True) - srt = f'{start} --> {end}'.replace('.', ',') - srt += '\n' + self.text - return srt - - -class Point: - def __init__(self, x, y=None): - self.x = x - self.y = y - - def __lt__(self, other): - return (self.x, self.y) < (other.x, other.y) - - def __repr__(self): - return f'Point({self.x}, {self.y})' - - -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.pop(0)) * 3600 - if len(hms) == 2: - seconds += int(hms.pop(0)) * 60 - if len(hms) == 1: - seconds += float(hms.pop(0).replace(',', '.')) - return seconds - -def seconds_to_hms(seconds, force_hours=False, force_milliseconds=False): - milliseconds = seconds % 1 - if milliseconds >= 0.999: - milliseconds = 0 - seconds += 1 - seconds = int(seconds) - (minutes, seconds) = divmod(seconds, 60) - (hours, minutes) = divmod(minutes, 60) - - parts = [] - if hours or force_hours: - parts.append(hours) - - if minutes or force_hours: - parts.append(minutes) - - parts.append(seconds) - - hms = ':'.join(f'{part:02d}' for part in parts) - if milliseconds or force_milliseconds: - milliseconds = f'.{milliseconds:.03f}'.split('.')[-1] - hms += '.' + milliseconds - return hms - -def linear(slope, intercept): - ''' - Given slope m and intercept b, return a function f such that f(x) = mx + b. - ''' - def f(x): - y = (slope * x) + intercept - print(x, y, f'{y:.03f}', seconds_to_hms(y)) - return y - return f - -def slope_intercept(p1, p2): - ''' - Given two Points, return the slope and intercept describing a line - between them. - ''' - slope = (p2.y - p1.y) / (p2.x - p1.x) - intercept = p1.y - (slope * p1.x) - return (slope, intercept) - -def pointsync(input_filename, output_filename, input_landmarks): - landmarks = [] - used_olds = set() - for landmark in input_landmarks: - (old, new) = landmark.split('=') - (old, new) = (hms_to_seconds(old), hms_to_seconds(new)) - if old < 0 or new < 0: - raise ValueError('No negative numbers!') - if old in used_olds: - raise ValueError(f'Cant use the same old value {seconds_to_hms(old, force_hours=True)} twice.') - used_olds.add(old) - landmarks.append(Point(old, new)) - landmarks.sort() - if landmarks[0].x != 0: - landmarks.insert(0, Point(0, 0)) - - # print(landmarks) - if len(landmarks) < 2: - raise ValueError('Not enough landmarks') - - landmark_functions = [] - for (land1, land2) in zip(landmarks, landmarks[1:]): - (slope, intercept) = slope_intercept(land1, land2) - if slope < 0: - raise ValueError(f'Negative slope between {land1} and {land2}.') - f = linear(slope, intercept) - landmark_functions.append((land1.x, f)) - landmark_functions.append((inf, None)) - old_srt = Subtitles.from_text(open(input_filename, encoding='utf-8').read()) - - pointer = 0 - new_srt = Subtitles([]) - for old_line in old_srt: - if old_line.start >= landmark_functions[pointer+1][0]: - pointer += 1 - new_start = landmark_functions[pointer][1](old_line.start) - - if old_line.end >= landmark_functions[pointer+1][0]: - pointer += 1 - new_end = landmark_functions[pointer][1](old_line.end) - new_line = SubtitleLine(new_start, new_end, old_line.text) - new_srt.lines.append(new_line) - new_file = open(output_filename, 'w', encoding='utf-8') - new_file.write(new_srt.as_srt()) - -def pointsync_argparse(args): - input_filename = args.input_filename - output_filename = args.output_filename - if '.srt' not in output_filename: - raise ValueError('Output filename', output_filename) - - input_landmarks = args.landmarks - return pointsync(input_filename, output_filename, input_landmarks) - -def main(argv): - parser = argparse.ArgumentParser(description=__doc__) - - parser.add_argument('input_filename') - parser.add_argument('output_filename') - parser.add_argument('landmarks', nargs='*', default=None) - parser.set_defaults(func=pointsync_argparse) - - args = parser.parse_args(argv) - args.func(args) - -if __name__ == '__main__': - raise SystemExit(main(sys.argv[1:])) diff --git a/Toolbox/timestampfilename.pyw b/Toolbox/timestampfilename.pyw deleted file mode 100644 index 0a3ca6e..0000000 --- a/Toolbox/timestampfilename.pyw +++ /dev/null @@ -1,25 +0,0 @@ -''' -Drag a file on top of this .py file, and it will -be renamed to the current timestamp. -''' - -import datetime -import os -import sys - -STRFTIME = '%Y%m%d %H%M%S' -UTC = True - -filename = sys.argv[1] -folder = os.path.dirname(filename) -if folder == '': - folder = os.getcwd() -basename = os.path.basename(filename) -extension = os.path.splitext(basename)[1] - -now = datetime.datetime.now(datetime.timezone.utc if UTC else None) -newname = now.strftime(STRFTIME) - -newname = '%s\\%s%s' % (folder, newname, extension) -print(filename, '-->', newname) -os.rename(filename, newname) \ No newline at end of file diff --git a/Toolbox/touch.py b/Toolbox/touch.py deleted file mode 100644 index bf94858..0000000 --- a/Toolbox/touch.py +++ /dev/null @@ -1,24 +0,0 @@ -''' -Create the file, or update the last modified timestamp. -''' -import glob -import os -import sys - -from voussoirkit import clipext -from voussoirkit import safeprint - -def touch(glob_pattern): - filenames = glob.glob(glob_pattern) - if len(filenames) == 0: - safeprint.safeprint(glob_pattern) - open(glob_pattern, 'a').close() - else: - for filename in filenames: - safeprint.safeprint(filename) - os.utime(filename) - -if __name__ == '__main__': - glob_patterns = [clipext.resolve(x).strip() for x in sys.argv[1:]] - for glob_pattern in glob_patterns: - touch(glob_pattern) diff --git a/Toolbox/unique.py b/Toolbox/unique.py deleted file mode 100644 index 35e6982..0000000 --- a/Toolbox/unique.py +++ /dev/null @@ -1,20 +0,0 @@ -''' -Keep the unique lines coming from stdin and print them. -''' -from voussoirkit import clipext -import sys - -if len(sys.argv) > 1: - source = sys.argv[1] -else: - source = '!input' -lines = clipext.resolve(source, split_lines=True) - -new_text = [] -seen = set() -for line in lines: - if line not in seen: - #new_text.append(line) - seen.add(line) - print(line) -