Delete toolbox folder in preference of new repository.

master
Ethan Dalool 2019-06-11 22:32:32 -07:00
parent ba54238021
commit 8a0fcb09ca
34 changed files with 0 additions and 1864 deletions

View File

@ -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.

View File

@ -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))

View File

@ -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))

View File

@ -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:]))

View File

@ -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:]))

View File

@ -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)

View File

@ -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:]))

View File

@ -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)

View File

@ -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:])

View File

@ -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)

View File

@ -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))

View File

@ -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))

View File

@ -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))

View File

@ -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:])

View File

@ -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:])

View File

@ -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)

View File

@ -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:])

View File

@ -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)

View File

@ -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:]))

View File

@ -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:])

View File

@ -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))

View File

@ -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:])

View File

@ -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)

View File

@ -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))

View File

@ -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)

View File

@ -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)

View File

@ -1,7 +0,0 @@
import sys
from voussoirkit import clipext
arg = clipext.resolve(sys.argv[1])
arg = ''.join(reversed(arg))
print(arg)

View File

@ -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:]))

View File

@ -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)

View File

@ -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()

View File

@ -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:]))

View File

@ -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)

View File

@ -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)

View File

@ -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)