Delete toolbox folder in preference of new repository.
This commit is contained in:
parent
ba54238021
commit
8a0fcb09ca
34 changed files with 0 additions and 1864 deletions
|
@ -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.
|
|
|
@ -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))
|
|
|
@ -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))
|
|
|
@ -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:]))
|
|
|
@ -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:]))
|
|
|
@ -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)
|
|
|
@ -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:]))
|
|
|
@ -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)
|
|
|
@ -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:])
|
|
|
@ -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)
|
|
|
@ -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))
|
|
|
@ -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))
|
|
|
@ -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))
|
|
|
@ -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:])
|
|
|
@ -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:])
|
|
|
@ -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)
|
|
|
@ -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:])
|
|
|
@ -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)
|
|
|
@ -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:]))
|
|
|
@ -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:])
|
|
|
@ -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))
|
|
|
@ -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:])
|
|
|
@ -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)
|
|
|
@ -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))
|
|
|
@ -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)
|
|
|
@ -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)
|
|
|
@ -1,7 +0,0 @@
|
||||||
import sys
|
|
||||||
|
|
||||||
from voussoirkit import clipext
|
|
||||||
|
|
||||||
arg = clipext.resolve(sys.argv[1])
|
|
||||||
arg = ''.join(reversed(arg))
|
|
||||||
print(arg)
|
|
|
@ -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:]))
|
|
|
@ -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)
|
|
|
@ -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()
|
|
|
@ -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:]))
|
|
|
@ -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)
|
|
|
@ -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)
|
|
|
@ -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)
|
|
||||||
|
|
Loading…
Reference in a new issue