This commit is contained in:
Ethan Dalool 2018-03-13 01:45:23 -07:00
parent b0950c3df1
commit 9b46a7b927
7 changed files with 266 additions and 4 deletions

View file

@ -375,9 +375,17 @@ def safeprint(*texts, **kwargs):
print(*texts, **kwargs) print(*texts, **kwargs)
def sanitize_filename(text, exclusions=''): def sanitize_filename(text, exclusions=''):
bet = FILENAME_BADCHARS.replace(exclusions, '') to_remove = FILENAME_BADCHARS
for char in bet: for exclude in exclusions:
to_remove = to_remove.replace(exclude, '')
for char in to_remove:
text = text.replace(char, '') text = text.replace(char, '')
(drive, path) = os.path.splitdrive(text)
path = path.replace(':', '')
text = drive + path
return text return text
def sanitize_url(url): def sanitize_url(url):

View file

@ -84,6 +84,7 @@ def copy(source, file_args=None, file_kwargs=None, dir_args=None, dir_kwargs=Non
def copy_dir( def copy_dir(
source, source,
destination=None, destination=None,
*,
bytes_per_second=None, bytes_per_second=None,
callback_directory=None, callback_directory=None,
callback_exclusion=None, callback_exclusion=None,
@ -277,6 +278,7 @@ def copy_dir(
def copy_file( def copy_file(
source, source,
destination=None, destination=None,
*,
destination_new_root=None, destination_new_root=None,
bytes_per_second=None, bytes_per_second=None,
callback_progress=None, callback_progress=None,
@ -565,6 +567,7 @@ def verify_hash(path, known_size, known_hash, callback=None):
def walk_generator( def walk_generator(
path='.', path='.',
*,
callback_exclusion=None, callback_exclusion=None,
callback_permission_denied=None, callback_permission_denied=None,
depth_first=True, depth_first=True,

View file

@ -46,7 +46,7 @@ class ThreadQueue:
break break
lam = self._lambdas.pop(0) lam = self._lambdas.pop(0)
thread = threading.Thread(target=lam) thread = threading.Thread(target=lam)
#thread.daemon = True thread.daemon = True
thread.start() thread.start()
self._threads.append(thread) self._threads.append(thread)

View file

@ -0,0 +1,73 @@
'''
This script takes an image and splits it up into pieces as separate files.
drawn_quartered test.jpg --width 2 --height 2
drawn_quartered test.jpg outputname.jpg --width 3 --height 4
'''
import argparse
import math
import os
import PIL.Image
import sys
from voussoirkit import pathclass
def drawquarter(image, width=2, height=2):
pieces = []
(image_width, image_height) = image.size
step_x = image_width / width
step_y = image_height / height
if (step_x != int(step_x)):
print('Warning: Imperfect x', step_x)
if (step_y != int(step_y)):
print('Warning: Imperfect y', step_y)
step_x = math.ceil(step_x)
step_y = math.ceil(step_y)
for y in range(height):
end_y = y + 1
for x in range(width):
end_x = x + 1
coords = (step_x * x, step_y * y, step_x * end_x, step_y * end_y)
piece = image.crop(coords)
pieces.append(piece)
return pieces
def drawquarter_argparse(args):
image = PIL.Image.open(args.input_filename)
if args.output_filename is not None:
output_filename = args.output_filename
else:
output_filename = args.input_filename
output_path = pathclass.Path(output_filename)
output_directory = output_path.parent
os.makedirs(output_directory.absolute_path, exist_ok=True)
output_filename_format = output_path.basename
output_filename_format = output_filename_format.rsplit('.', 1)[0]
output_filename_format += '_%dx%d_{ycoord}-{xcoord}.' % (args.width, args.height)
output_filename_format += args.input_filename.rsplit('.', 1)[1]
pieces = drawquarter(image, width=args.width, height=args.height)
for (index, piece) in enumerate(pieces):
(ycoord, xcoord) = divmod(index, args.height)
output_filename = output_filename_format.format(xcoord=xcoord, ycoord=ycoord)
output_filename = output_directory.with_child(output_filename)
print(output_filename.relative_path)
piece.save(output_filename.absolute_path)
def main(argv):
parser = argparse.ArgumentParser()
parser.add_argument('input_filename')
parser.add_argument('output_filename', nargs='?', default=None)
parser.add_argument('--width', dest='width', type=int, default=2)
parser.add_argument('--height', dest='height', type=int, default=2)
parser.set_defaults(func=drawquarter_argparse)
args = parser.parse_args(argv)
args.func(args)
if __name__ == '__main__':
main(sys.argv[1:])

171
Toolbox/mp3slice.py Normal file
View file

@ -0,0 +1,171 @@
'''
This script cuts an audio file into multiple files when you provide the
timestamps and titles for each.
> mp3slice bigfile.mp3 00:00-01:00 part1.mp3 01:00-02:00 part2.mp3
'''
import argparse
import os
import sys
from voussoirkit import bytestring
def parse_rules(lines):
rules = []
for (times, title) in lines[::-1]:
rule = {'title': title}
(start, end) = hyphen_range(times)
if start is None:
raise ValueError('Null start')
rule['start'] = start
if end is None and len(rules) > 0:
end = rules[-1]['start']
rule['end'] = end
rules.append(rule)
rules.sort(key=lambda x: x.get('start'))
return rules
def read_rulefile(filename):
text = None
for encoding in [None, 'utf-8']:
try:
with open(filename, 'r', encoding=encoding) as handle:
text = handle.read()
break
except UnicodeError:
pass
else:
raise UnicodeError()
lines = [l.strip() for l in text.strip().splitlines()]
lines = [l for l in lines if l]
rules = [l.split(maxsplit=1) for l in lines]
return parse_rules(rules)
def chunk_sequence(sequence, chunk_length, allow_incomplete=True):
'''
Given a sequence, divide it into sequences of length `chunk_length`.
allow_incomplete:
If True, allow the final chunk to be shorter if the
given sequence is not an exact multiple of `chunk_length`.
If False, the incomplete chunk will be discarded.
'''
(complete, leftover) = divmod(len(sequence), chunk_length)
if not allow_incomplete:
leftover = 0
chunk_count = complete + min(leftover, 1)
chunks = []
for x in range(chunk_count):
left = chunk_length * x
right = left + chunk_length
chunks.append(sequence[left:right])
return chunks
def hyphen_range(s):
'''
Given a string like '1-3', return numbers (1, 3) representing lower
and upper bounds.
Supports bytestring.parsebytes and hh:mm:ss format, for example
'1k-2k', '10:00-20:00', '4gib-'
'''
s = s.strip()
s = s.replace(' ', '')
if not s:
return (None, None)
parts = s.split('-')
parts = [part.strip() or None for part in parts]
if len(parts) == 1:
low = parts[0]
high = None
elif len(parts) == 2:
(low, high) = parts
else:
raise ValueError('Too many hyphens')
low = _unitconvert(low)
high = _unitconvert(high)
if low is not None and high is not None and low > high:
raise exceptions.OutOfOrder(range=s, min=low, max=high)
return low, high
def hms_to_seconds(hms):
'''
Convert hh:mm:ss string to an integer seconds.
'''
hms = hms.split(':')
seconds = 0
if len(hms) == 3:
seconds += int(hms[0])*3600
hms.pop(0)
if len(hms) == 2:
seconds += int(hms[0])*60
hms.pop(0)
if len(hms) == 1:
seconds += float(hms[0])
return seconds
def _unitconvert(value):
'''
When parsing hyphenated ranges, this function is used to convert
strings like "1k" to 1024 and "1:00" to 60.
'''
if value is None:
return None
if ':' in value:
return hms_to_seconds(value)
elif all(c in '0123456789.' for c in value):
return float(value)
else:
return bytestring.parsebytes(value)
def example_argparse(args):
if len(args.rules) == 1 and os.path.isfile(args.rules[0]):
rules = read_rulefile(args.rules[0])
else:
rules = args.rules
rules = chunk_sequence(rules, 2)
if len(rules[-1]) != 2:
raise ValueError('Odd-number parameters')
rules = parse_rules(rules)
extension = os.path.splitext(args.input_filename)[1]
outputters = []
for rule in rules:
outputter = []
if not rule['title'].endswith(extension):
rule['title'] += extension
outputter.append('-ss')
outputter.append(str(rule['start']))
if rule['end'] is not None:
outputter.append('-to')
outputter.append(str(rule['end']))
outputter.append(' -c copy')
outputter.append('"%s"' % rule['title'])
print(outputter)
outputter = ' '.join(outputter)
outputters.append(outputter)
outputters = ' '.join(outputters)
command = 'ffmpeg -i "%s" %s' % (args.input_filename, outputters)
print(command)
os.system(command)
def main(argv):
parser = argparse.ArgumentParser()
parser.add_argument('input_filename')
parser.add_argument('rules', nargs='+', default=None)
parser.set_defaults(func=example_argparse)
args = parser.parse_args(argv)
args.func(args)
if __name__ == '__main__':
main(sys.argv[1:])

7
Toolbox/reverse.py Normal file
View file

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

View file

@ -54,7 +54,7 @@ def search(
'not_all': not_all, 'not_all': not_all,
'not_any': not_any 'not_any': not_any
} }
terms = {k: (v or []) for (k, v) in terms.items()} terms = {k: ([v] if isinstance(v, str) else v or []) for (k, v) in terms.items()}
#print(terms, content_args) #print(terms, content_args)
if all(v == [] for v in terms.values()) and not content_args: if all(v == [] for v in terms.values()) and not content_args: