else
This commit is contained in:
parent
b0950c3df1
commit
9b46a7b927
7 changed files with 266 additions and 4 deletions
|
@ -375,9 +375,17 @@ def safeprint(*texts, **kwargs):
|
|||
print(*texts, **kwargs)
|
||||
|
||||
def sanitize_filename(text, exclusions=''):
|
||||
bet = FILENAME_BADCHARS.replace(exclusions, '')
|
||||
for char in bet:
|
||||
to_remove = FILENAME_BADCHARS
|
||||
for exclude in exclusions:
|
||||
to_remove = to_remove.replace(exclude, '')
|
||||
|
||||
for char in to_remove:
|
||||
text = text.replace(char, '')
|
||||
|
||||
(drive, path) = os.path.splitdrive(text)
|
||||
path = path.replace(':', '')
|
||||
text = drive + path
|
||||
|
||||
return text
|
||||
|
||||
def sanitize_url(url):
|
||||
|
|
|
@ -84,6 +84,7 @@ def copy(source, file_args=None, file_kwargs=None, dir_args=None, dir_kwargs=Non
|
|||
def copy_dir(
|
||||
source,
|
||||
destination=None,
|
||||
*,
|
||||
bytes_per_second=None,
|
||||
callback_directory=None,
|
||||
callback_exclusion=None,
|
||||
|
@ -277,6 +278,7 @@ def copy_dir(
|
|||
def copy_file(
|
||||
source,
|
||||
destination=None,
|
||||
*,
|
||||
destination_new_root=None,
|
||||
bytes_per_second=None,
|
||||
callback_progress=None,
|
||||
|
@ -565,6 +567,7 @@ def verify_hash(path, known_size, known_hash, callback=None):
|
|||
|
||||
def walk_generator(
|
||||
path='.',
|
||||
*,
|
||||
callback_exclusion=None,
|
||||
callback_permission_denied=None,
|
||||
depth_first=True,
|
||||
|
|
|
@ -46,7 +46,7 @@ class ThreadQueue:
|
|||
break
|
||||
lam = self._lambdas.pop(0)
|
||||
thread = threading.Thread(target=lam)
|
||||
#thread.daemon = True
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
self._threads.append(thread)
|
||||
|
||||
|
|
73
Toolbox/drawn_quartered.py
Normal file
73
Toolbox/drawn_quartered.py
Normal 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
171
Toolbox/mp3slice.py
Normal 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
7
Toolbox/reverse.py
Normal file
|
@ -0,0 +1,7 @@
|
|||
import sys
|
||||
|
||||
from voussoirkit import clipext
|
||||
|
||||
arg = clipext.resolve(sys.argv[1])
|
||||
arg = ''.join(reversed(arg))
|
||||
print(arg)
|
|
@ -54,7 +54,7 @@ def search(
|
|||
'not_all': not_all,
|
||||
'not_any': not_any
|
||||
}
|
||||
terms = {k: (v or []) for (k, v) in terms.items()}
|
||||
terms = {k: ([v] if isinstance(v, str) else v or []) for (k, v) in terms.items()}
|
||||
#print(terms, content_args)
|
||||
|
||||
if all(v == [] for v in terms.values()) and not content_args:
|
||||
|
|
Loading…
Reference in a new issue