Use new betterhelp.
This commit is contained in:
parent
389f22faff
commit
8616cdc5dd
29 changed files with 1133 additions and 706 deletions
|
@ -1,11 +1,3 @@
|
||||||
'''
|
|
||||||
bitwise_or
|
|
||||||
==========
|
|
||||||
|
|
||||||
Merge two or more files by performing bitwise or on their bits.
|
|
||||||
|
|
||||||
> bitwise_or file1 file2 --output file3
|
|
||||||
'''
|
|
||||||
import argparse
|
import argparse
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
@ -40,6 +32,8 @@ def bitwise_or_argparse(args):
|
||||||
pass
|
pass
|
||||||
elif args.overwrite:
|
elif args.overwrite:
|
||||||
pass
|
pass
|
||||||
|
elif not pipeable.in_tty():
|
||||||
|
return 1
|
||||||
elif not interactive.getpermission(f'Overwrite "{output.absolute_path}"?'):
|
elif not interactive.getpermission(f'Overwrite "{output.absolute_path}"?'):
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
@ -61,14 +55,27 @@ def bitwise_or_argparse(args):
|
||||||
|
|
||||||
@vlogging.main_decorator
|
@vlogging.main_decorator
|
||||||
def main(argv):
|
def main(argv):
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
parser = argparse.ArgumentParser(
|
||||||
|
description='''
|
||||||
|
Merge two or more files by performing bitwise or on their bits.
|
||||||
|
That is, every byte of the output file will be the bitwise or of the
|
||||||
|
corresponding byte from all of the input files.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
|
||||||
parser.add_argument('files', nargs='+')
|
parser.add_argument('files', nargs='+')
|
||||||
parser.add_argument('--output', required=True)
|
parser.add_argument('--output', required=True, type=pathclass.Path)
|
||||||
parser.add_argument('--overwrite', action='store_true')
|
parser.add_argument(
|
||||||
|
'--overwrite',
|
||||||
|
action='store_true',
|
||||||
|
help='''
|
||||||
|
Provide this flag if the output file already exists and you'd like to
|
||||||
|
overwrite it.
|
||||||
|
''',
|
||||||
|
)
|
||||||
parser.set_defaults(func=bitwise_or_argparse)
|
parser.set_defaults(func=bitwise_or_argparse)
|
||||||
|
|
||||||
return betterhelp.single_main(argv, parser, __doc__)
|
return betterhelp.go(parser, argv)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
raise SystemExit(main(sys.argv[1:]))
|
raise SystemExit(main(sys.argv[1:]))
|
||||||
|
|
68
blankimage.py
Normal file
68
blankimage.py
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
import argparse
|
||||||
|
import PIL.Image
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from voussoirkit import betterhelp
|
||||||
|
from voussoirkit import vlogging
|
||||||
|
|
||||||
|
log = vlogging.getLogger(__name__, 'blankimage')
|
||||||
|
|
||||||
|
def blankimage_argparse(args):
|
||||||
|
if args.width and args.height:
|
||||||
|
size = (args.width, args.height)
|
||||||
|
else:
|
||||||
|
size = (512, 512)
|
||||||
|
|
||||||
|
if args.color:
|
||||||
|
color = args.color
|
||||||
|
else:
|
||||||
|
color = (255, 255, 255, 255)
|
||||||
|
|
||||||
|
image = PIL.Image.new('RGBA', size, color=color)
|
||||||
|
for filename in args.names:
|
||||||
|
image.save(filename)
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
@vlogging.main_decorator
|
||||||
|
def main(argv):
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description='''
|
||||||
|
Create a blank image file.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'names',
|
||||||
|
nargs='+',
|
||||||
|
help='''
|
||||||
|
One or more filenames. The same image will be saved to each.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--width',
|
||||||
|
default=None,
|
||||||
|
type=int,
|
||||||
|
help='''
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--height',
|
||||||
|
default=None,
|
||||||
|
type=int,
|
||||||
|
help='''
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--color',
|
||||||
|
default=None,
|
||||||
|
type=str,
|
||||||
|
help='''
|
||||||
|
A hex color like #fff or #0a0a0a or #ff0000ff.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.set_defaults(func=blankimage_argparse)
|
||||||
|
|
||||||
|
return betterhelp.go(parser, argv)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
raise SystemExit(main(sys.argv[1:]))
|
79
brename.py
79
brename.py
|
@ -1,19 +1,3 @@
|
||||||
'''
|
|
||||||
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.
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
Prefix all the files:
|
|
||||||
brename.py "f'Test_{x}'"
|
|
||||||
|
|
||||||
Rename files to their index with 0 padding:
|
|
||||||
brename.py "f'{index1:>03}{dot_ext}'"
|
|
||||||
|
|
||||||
Keep the first word and extension:
|
|
||||||
brename.py "(x.split(' ')[0] + dot_ext) if ' ' in x else x"
|
|
||||||
'''
|
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
|
@ -125,13 +109,22 @@ def brename_argparse(args):
|
||||||
recurse=args.recurse,
|
recurse=args.recurse,
|
||||||
)
|
)
|
||||||
|
|
||||||
DOCSTRING = '''
|
def main(argv):
|
||||||
brename - batch file renaming
|
parser = argparse.ArgumentParser(
|
||||||
=============================
|
description='''
|
||||||
|
Batch rename files by providing a string to be `eval`ed, using variable `x` as
|
||||||
> brename.py eval_string <flags>
|
the current filename. Yes I know this is weird, but for certain tasks it's just
|
||||||
|
too quick and easy to pass up.
|
||||||
eval_string:
|
''',
|
||||||
|
)
|
||||||
|
parser.examples = [
|
||||||
|
{'args': ['f\'Test_{x}\''], 'comment': 'Prefix all the files'},
|
||||||
|
{'args': ['f\'{index1:>03}{dot_ext}\''], 'comment': 'Rename files to their index with 0 padding'},
|
||||||
|
{'args': ['(x.split(space)[0] + dot_ext) if space in x else x'], 'comment': 'Keep the first word and extension'},
|
||||||
|
]
|
||||||
|
parser.add_argument(
|
||||||
|
'transformation',
|
||||||
|
help='''
|
||||||
A string which will be evaluated by Python's eval. The name of the file or
|
A string which will be evaluated by Python's eval. The name of the file or
|
||||||
folder will be in the variable `x`. In addition, many other variables are
|
folder will be in the variable `x`. In addition, many other variables are
|
||||||
provided for your convenience:
|
provided for your convenience:
|
||||||
|
@ -146,32 +139,40 @@ eval_string:
|
||||||
`cwd` a pathclass.Path object for the cwd of this program session.
|
`cwd` a pathclass.Path object for the cwd of this program session.
|
||||||
`noext` the name of the file, but without its extension.
|
`noext` the name of the file, but without its extension.
|
||||||
`ext` the file's extension, with no dot.
|
`ext` the file's extension, with no dot.
|
||||||
|
`dot_ext` the file's extension, with dot.
|
||||||
-y | --yes:
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'-y',
|
||||||
|
'--yes',
|
||||||
|
dest='autoyes',
|
||||||
|
action='store_true',
|
||||||
|
help='''
|
||||||
Accept the results without confirming.
|
Accept the results without confirming.
|
||||||
|
''',
|
||||||
--recurse:
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--recurse',
|
||||||
|
action='store_true',
|
||||||
|
help='''
|
||||||
Recurse into subfolders and rename those files too.
|
Recurse into subfolders and rename those files too.
|
||||||
|
''',
|
||||||
--naturalsort:
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--naturalsort',
|
||||||
|
action='store_true',
|
||||||
|
help='''
|
||||||
Before renaming, the files will be sorted using natural sort instead of the
|
Before renaming, the files will be sorted using natural sort instead of the
|
||||||
default lexicographic sort. Natural sort means that "My file 20" will come
|
default lexicographic sort. Natural sort means that "My file 20" will come
|
||||||
before "My file 100" because 20<100. Lexicographic sort means 100 will come
|
before "My file 100" because 20<100. Lexicographic sort means 100 will come
|
||||||
first because 1 is before 2.
|
first because 1 is before 2.
|
||||||
The purpose of this flag is so your index and index1 variables are applied
|
The purpose of this flag is so your index and index1 variables are applied
|
||||||
in the order you desire.
|
in the order you desire.
|
||||||
'''
|
''',
|
||||||
|
)
|
||||||
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')
|
|
||||||
parser.add_argument('--recurse', action='store_true')
|
|
||||||
parser.add_argument('--naturalsort', action='store_true')
|
|
||||||
parser.set_defaults(func=brename_argparse)
|
parser.set_defaults(func=brename_argparse)
|
||||||
|
|
||||||
return betterhelp.single_main(argv, parser, DOCSTRING)
|
return betterhelp.go(parser, argv)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
raise SystemExit(main(sys.argv[1:]))
|
raise SystemExit(main(sys.argv[1:]))
|
||||||
|
|
|
@ -1,36 +1,3 @@
|
||||||
'''
|
|
||||||
contentreplace - find-and-replace en masse
|
|
||||||
==========================================
|
|
||||||
|
|
||||||
> contentreplace filename_glob replace_from replace_to <flags>
|
|
||||||
|
|
||||||
filename_glob:
|
|
||||||
A glob pattern that targets the files of interest.
|
|
||||||
|
|
||||||
replace_from:
|
|
||||||
String to be replaced.
|
|
||||||
|
|
||||||
replace_to:
|
|
||||||
String with which to replace.
|
|
||||||
|
|
||||||
flags:
|
|
||||||
--recurse:
|
|
||||||
If provided, we will recurse into subdirectories and look for glob matches
|
|
||||||
there too. If not provided, only files in the cwd are affected.
|
|
||||||
|
|
||||||
--regex:
|
|
||||||
If provided, the given replace_from, replace_to will be treated as regex
|
|
||||||
strings. If not provided, we use regular str.replace
|
|
||||||
|
|
||||||
--clip_prompt:
|
|
||||||
If you want to do contentreplace with unicode that is difficult to enter
|
|
||||||
into your terminal, or multi-line strings that don't work as command line
|
|
||||||
arguments, this option might help you. The program will wait for you to put
|
|
||||||
the text of interest into your clipboard and press Enter.
|
|
||||||
|
|
||||||
--yes:
|
|
||||||
If provided, replacements will occur automatically without prompting.
|
|
||||||
'''
|
|
||||||
import argparse
|
import argparse
|
||||||
import codecs
|
import codecs
|
||||||
import pyperclip
|
import pyperclip
|
||||||
|
@ -106,18 +73,69 @@ def contentreplace_argparse(args):
|
||||||
|
|
||||||
@vlogging.main_decorator
|
@vlogging.main_decorator
|
||||||
def main(argv):
|
def main(argv):
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
parser = argparse.ArgumentParser(
|
||||||
|
description='''
|
||||||
parser.add_argument('filename_glob')
|
find-and-replace en masse
|
||||||
parser.add_argument('replace_from')
|
''',
|
||||||
parser.add_argument('replace_to')
|
)
|
||||||
parser.add_argument('--yes', dest='autoyes', action='store_true')
|
parser.add_argument(
|
||||||
parser.add_argument('--recurse', action='store_true')
|
'filename_glob',
|
||||||
parser.add_argument('--regex', dest='do_regex', action='store_true')
|
help='''
|
||||||
parser.add_argument('--clip_prompt', '--clip-prompt', action='store_true')
|
A glob pattern that targets the files of interest.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'replace_from',
|
||||||
|
help='''
|
||||||
|
String to be replaced. You can use backslash-escaped symbols like
|
||||||
|
\\n for newline.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'replace_to',
|
||||||
|
help='''
|
||||||
|
String with which to replace. Can use backslash-escaped symbols.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--yes',
|
||||||
|
dest='autoyes',
|
||||||
|
action='store_true',
|
||||||
|
help='''
|
||||||
|
If provided, replacements will occur automatically without prompting.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--recurse',
|
||||||
|
action='store_true',
|
||||||
|
help='''
|
||||||
|
If provided, we will recurse into subdirectories and look for glob matches
|
||||||
|
there too. If not provided, only files in the cwd are affected.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--regex',
|
||||||
|
dest='do_regex',
|
||||||
|
action='store_true',
|
||||||
|
help='''
|
||||||
|
If provided, the given replace_from, replace_to will be treated as regex
|
||||||
|
strings. If not provided, we use regular str.replace.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--clip_prompt',
|
||||||
|
'--clip-prompt',
|
||||||
|
action='store_true',
|
||||||
|
help='''
|
||||||
|
If you want to do contentreplace with unicode that is difficult to enter
|
||||||
|
into your terminal, or multi-line strings that don't work as command line
|
||||||
|
arguments, this option might help you. The program will wait for you to put
|
||||||
|
the text of interest into your clipboard and press Enter.
|
||||||
|
''',
|
||||||
|
)
|
||||||
parser.set_defaults(func=contentreplace_argparse)
|
parser.set_defaults(func=contentreplace_argparse)
|
||||||
|
|
||||||
return betterhelp.single_main(argv, parser, __doc__)
|
return betterhelp.go(parser, argv)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
raise SystemExit(main(sys.argv[1:]))
|
raise SystemExit(main(sys.argv[1:]))
|
||||||
|
|
|
@ -1,16 +1,3 @@
|
||||||
'''
|
|
||||||
directory_discrepancy
|
|
||||||
=====================
|
|
||||||
|
|
||||||
This program compares two directory and shows which files exist in each
|
|
||||||
directory that do not exist in the other.
|
|
||||||
|
|
||||||
> directory_discrepancy dir1 dir2
|
|
||||||
|
|
||||||
flags:
|
|
||||||
--recurse:
|
|
||||||
Also check subdirectories.
|
|
||||||
'''
|
|
||||||
import argparse
|
import argparse
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
@ -48,14 +35,24 @@ def directory_discrepancy_argparse(args):
|
||||||
|
|
||||||
@vlogging.main_decorator
|
@vlogging.main_decorator
|
||||||
def main(argv):
|
def main(argv):
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
parser = argparse.ArgumentParser(
|
||||||
|
description='''
|
||||||
|
This program compares two directory and shows which files exist in each
|
||||||
|
directory that do not exist in the other.
|
||||||
|
''',
|
||||||
|
)
|
||||||
parser.add_argument('dir1')
|
parser.add_argument('dir1')
|
||||||
parser.add_argument('dir2')
|
parser.add_argument('dir2')
|
||||||
parser.add_argument('--recurse', action='store_true')
|
parser.add_argument(
|
||||||
|
'--recurse',
|
||||||
|
action='store_true',
|
||||||
|
help='''
|
||||||
|
Also check subdirectories.
|
||||||
|
''',
|
||||||
|
)
|
||||||
parser.set_defaults(func=directory_discrepancy_argparse)
|
parser.set_defaults(func=directory_discrepancy_argparse)
|
||||||
|
|
||||||
return betterhelp.single_main(argv, parser, __doc__)
|
return betterhelp.go(parser, argv)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
raise SystemExit(main(sys.argv[1:]))
|
raise SystemExit(main(sys.argv[1:]))
|
||||||
|
|
64
fdroidapk.py
64
fdroidapk.py
|
@ -1,22 +1,3 @@
|
||||||
'''
|
|
||||||
fdroidapk - F-Droid APK downloader
|
|
||||||
==================================
|
|
||||||
|
|
||||||
> fdroidapk package_names <flags>
|
|
||||||
|
|
||||||
package_names:
|
|
||||||
One or more package names to download, separated by spaces. You can find
|
|
||||||
the package name in the URL on f-droid.org.
|
|
||||||
For example, com.nutomic.syncthingandroid from the URL
|
|
||||||
https://f-droid.org/en/packages/com.nutomic.syncthingandroid/
|
|
||||||
|
|
||||||
--destination path:
|
|
||||||
Alternative path to download the apk files to. Default is cwd.
|
|
||||||
|
|
||||||
--folders:
|
|
||||||
If provided, each apk will be downloaded into a separate folder named after
|
|
||||||
the package.
|
|
||||||
'''
|
|
||||||
import argparse
|
import argparse
|
||||||
import bs4
|
import bs4
|
||||||
import io
|
import io
|
||||||
|
@ -33,6 +14,7 @@ from voussoirkit import httperrors
|
||||||
from voussoirkit import operatornotify
|
from voussoirkit import operatornotify
|
||||||
from voussoirkit import pathclass
|
from voussoirkit import pathclass
|
||||||
from voussoirkit import pipeable
|
from voussoirkit import pipeable
|
||||||
|
from voussoirkit import progressbars
|
||||||
from voussoirkit import vlogging
|
from voussoirkit import vlogging
|
||||||
|
|
||||||
log = vlogging.getLogger(__name__, 'fdroidapk')
|
log = vlogging.getLogger(__name__, 'fdroidapk')
|
||||||
|
@ -52,7 +34,7 @@ def download_file(url, path):
|
||||||
return downloady.download_file(
|
return downloady.download_file(
|
||||||
url,
|
url,
|
||||||
path,
|
path,
|
||||||
callback_progress=downloady.Progress2,
|
progressbar=progressbars.bar1_bytestring,
|
||||||
timeout=30,
|
timeout=30,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -88,8 +70,7 @@ def normalize_package_name(package_name):
|
||||||
|
|
||||||
@pipeable.ctrlc_return1
|
@pipeable.ctrlc_return1
|
||||||
def fpk_argparse(args):
|
def fpk_argparse(args):
|
||||||
destination = pathclass.Path(args.destination)
|
args.destination.assert_is_directory()
|
||||||
destination.assert_is_directory()
|
|
||||||
|
|
||||||
return_status = 0
|
return_status = 0
|
||||||
|
|
||||||
|
@ -117,10 +98,10 @@ def fpk_argparse(args):
|
||||||
apk_url = f'https://f-droid.org/repo/{apk_basename}'
|
apk_url = f'https://f-droid.org/repo/{apk_basename}'
|
||||||
|
|
||||||
if args.folders:
|
if args.folders:
|
||||||
this_dest = destination.with_child(package)
|
this_dest = args.destination.with_child(package)
|
||||||
this_dest.makedirs(exist_ok=True)
|
this_dest.makedirs(exist_ok=True)
|
||||||
else:
|
else:
|
||||||
this_dest = destination
|
this_dest = args.destination
|
||||||
this_dest = this_dest.with_child(apk_basename)
|
this_dest = this_dest.with_child(apk_basename)
|
||||||
|
|
||||||
if this_dest.exists:
|
if this_dest.exists:
|
||||||
|
@ -145,14 +126,39 @@ def fpk_argparse(args):
|
||||||
@operatornotify.main_decorator(subject='fdroidapk.py')
|
@operatornotify.main_decorator(subject='fdroidapk.py')
|
||||||
@vlogging.main_decorator
|
@vlogging.main_decorator
|
||||||
def main(argv):
|
def main(argv):
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
parser = argparse.ArgumentParser(description='F-Droid APK downloader.')
|
||||||
|
|
||||||
parser.add_argument('packages', nargs='+')
|
parser.add_argument(
|
||||||
parser.add_argument('--folders', action='store_true')
|
'packages',
|
||||||
parser.add_argument('--destination', default='.')
|
nargs='+',
|
||||||
|
type=str,
|
||||||
|
help='''
|
||||||
|
One or more package names to download, separated by spaces. You can find
|
||||||
|
the package name in the URL on f-droid.org.
|
||||||
|
For example, com.nutomic.syncthingandroid from the URL
|
||||||
|
https://f-droid.org/en/packages/com.nutomic.syncthingandroid/
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--folders',
|
||||||
|
action='store_true',
|
||||||
|
help='''
|
||||||
|
If provided, each apk will be downloaded into a separate folder named after
|
||||||
|
the package.
|
||||||
|
If omitted, the apks are downloaded into the destination folder directly.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--destination',
|
||||||
|
default=pathclass.cwd(),
|
||||||
|
type=pathclass.Path,
|
||||||
|
help='''
|
||||||
|
Alternative path to download the apk files to. Default is cwd.
|
||||||
|
''',
|
||||||
|
)
|
||||||
parser.set_defaults(func=fpk_argparse)
|
parser.set_defaults(func=fpk_argparse)
|
||||||
|
|
||||||
return betterhelp.single_main(argv, parser, __doc__)
|
return betterhelp.go(parser, argv)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
raise SystemExit(main(sys.argv[1:]))
|
raise SystemExit(main(sys.argv[1:]))
|
||||||
|
|
27
filepull.py
27
filepull.py
|
@ -1,6 +1,3 @@
|
||||||
'''
|
|
||||||
Pull all of the files in nested directories into the current directory.
|
|
||||||
'''
|
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
@ -9,10 +6,15 @@ from voussoirkit import interactive
|
||||||
from voussoirkit import pathclass
|
from voussoirkit import pathclass
|
||||||
from voussoirkit import pipeable
|
from voussoirkit import pipeable
|
||||||
from voussoirkit import spinal
|
from voussoirkit import spinal
|
||||||
|
from voussoirkit import winglob
|
||||||
|
|
||||||
def filepull(pull_from='.', autoyes=False):
|
def filepull(pull_from='.', globs=None, autoyes=False):
|
||||||
start = pathclass.Path(pull_from)
|
start = pathclass.Path(pull_from)
|
||||||
files = [file for d in start.listdir_directories() for file in d.walk_files()]
|
files = [
|
||||||
|
file
|
||||||
|
for d in start.listdir_directories()
|
||||||
|
for file in spinal.walk(d, glob_filenames=globs)
|
||||||
|
]
|
||||||
|
|
||||||
if len(files) == 0:
|
if len(files) == 0:
|
||||||
pipeable.stderr('No files to move')
|
pipeable.stderr('No files to move')
|
||||||
|
@ -45,12 +47,23 @@ def filepull(pull_from='.', autoyes=False):
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
def filepull_argparse(args):
|
def filepull_argparse(args):
|
||||||
return filepull(pull_from=args.pull_from, autoyes=args.autoyes)
|
return filepull(pull_from=args.pull_from, globs=args.glob, autoyes=args.autoyes)
|
||||||
|
|
||||||
def main(argv):
|
def main(argv):
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser(
|
||||||
|
description='''
|
||||||
|
Pull all of the files in nested directories into the current directory.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
|
||||||
parser.add_argument('pull_from', nargs='?', default='.')
|
parser.add_argument('pull_from', nargs='?', default='.')
|
||||||
|
parser.add_argument(
|
||||||
|
'--glob',
|
||||||
|
nargs='+',
|
||||||
|
help='''
|
||||||
|
Only pull files whose basename matches any of these glob patterns.
|
||||||
|
''',
|
||||||
|
)
|
||||||
parser.add_argument('-y', '--yes', dest='autoyes', action='store_true')
|
parser.add_argument('-y', '--yes', dest='autoyes', action='store_true')
|
||||||
parser.set_defaults(func=filepull_argparse)
|
parser.set_defaults(func=filepull_argparse)
|
||||||
|
|
||||||
|
|
64
fuchsiatransparent.py
Normal file
64
fuchsiatransparent.py
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
import argparse
|
||||||
|
import PIL.Image
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from voussoirkit import betterhelp
|
||||||
|
from voussoirkit import imagetools
|
||||||
|
from voussoirkit import pathclass
|
||||||
|
from voussoirkit import pipeable
|
||||||
|
from voussoirkit import vlogging
|
||||||
|
|
||||||
|
log = vlogging.getLogger(__name__, 'fuchsiatransparent')
|
||||||
|
|
||||||
|
FUCHSIA = (255, 0, 255, 255)
|
||||||
|
TRANSPARENT = (0, 0, 0, 0)
|
||||||
|
|
||||||
|
def fuchsiatransparent_argparse(args):
|
||||||
|
patterns = pipeable.input_many(args.patterns)
|
||||||
|
files = pathclass.glob_many_files(patterns)
|
||||||
|
for file in files:
|
||||||
|
image = PIL.Image.open(file.absolute_path)
|
||||||
|
if image.mode == 'RGB':
|
||||||
|
image = image.convert('RGBA')
|
||||||
|
if image.mode == 'RGBA':
|
||||||
|
image = imagetools.replace_color(image, FUCHSIA, TRANSPARENT)
|
||||||
|
else:
|
||||||
|
log.info('Can\'t process %s', file.absolute_path)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if args.inplace:
|
||||||
|
outpath = file
|
||||||
|
else:
|
||||||
|
outname = file.replace_extension('').basename + '_transparent'
|
||||||
|
outpath = file.parent.with_child(outname).add_extension(file.extension)
|
||||||
|
|
||||||
|
pipeable.stderr(outpath.absolute_path)
|
||||||
|
image.save(outpath.absolute_path)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
@vlogging.main_decorator
|
||||||
|
def main(argv):
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description='''
|
||||||
|
Replace #FF00FF colored pixels with transparent.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'patterns',
|
||||||
|
help='''
|
||||||
|
One or more glob patterns for input files.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--inplace',
|
||||||
|
action='store_true',
|
||||||
|
help='''
|
||||||
|
Overwrite the input file instead of saving it as _transparent.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.set_defaults(func=fuchsiatransparent_argparse)
|
||||||
|
|
||||||
|
return betterhelp.go(parser, argv)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
raise SystemExit(main(sys.argv[1:]))
|
|
@ -127,6 +127,7 @@ def getcrx_argparse(args):
|
||||||
else:
|
else:
|
||||||
auto_overwrite = None
|
auto_overwrite = None
|
||||||
|
|
||||||
|
return_status = 0
|
||||||
for extension_id in extension_ids:
|
for extension_id in extension_ids:
|
||||||
try:
|
try:
|
||||||
download_crx(extension_id, auto_overwrite=auto_overwrite)
|
download_crx(extension_id, auto_overwrite=auto_overwrite)
|
||||||
|
@ -136,7 +137,8 @@ def getcrx_argparse(args):
|
||||||
else:
|
else:
|
||||||
log.error(traceback.format_exc())
|
log.error(traceback.format_exc())
|
||||||
pipeable.stderr('Resuming...')
|
pipeable.stderr('Resuming...')
|
||||||
return 0
|
return_status = 1
|
||||||
|
return return_status
|
||||||
|
|
||||||
@operatornotify.main_decorator(subject='getcrx')
|
@operatornotify.main_decorator(subject='getcrx')
|
||||||
@vlogging.main_decorator
|
@vlogging.main_decorator
|
||||||
|
|
32
getpid.py
32
getpid.py
|
@ -1,17 +1,3 @@
|
||||||
'''
|
|
||||||
getpid
|
|
||||||
======
|
|
||||||
|
|
||||||
Get PIDs for running processes that match the given process name.
|
|
||||||
|
|
||||||
Error level will be 0 if any processes are found, 1 if none are found.
|
|
||||||
|
|
||||||
> getpid process_name
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
> getpid python.exe
|
|
||||||
> getpid chrome.exe
|
|
||||||
'''
|
|
||||||
import argparse
|
import argparse
|
||||||
import psutil
|
import psutil
|
||||||
import sys
|
import sys
|
||||||
|
@ -32,12 +18,24 @@ def getpid_argparse(args):
|
||||||
|
|
||||||
@vlogging.main_decorator
|
@vlogging.main_decorator
|
||||||
def main(argv):
|
def main(argv):
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
parser = argparse.ArgumentParser(
|
||||||
|
description='''
|
||||||
|
Get PIDs for running processes that match the given process name.
|
||||||
|
|
||||||
parser.add_argument('process_name')
|
Error level will be 0 if any processes are found, 1 if none are found.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'process_name',
|
||||||
|
type=str,
|
||||||
|
help='''
|
||||||
|
Name like "python.exe" or "chrome" as it appears in your task manager / ps.
|
||||||
|
''',
|
||||||
|
)
|
||||||
parser.set_defaults(func=getpid_argparse)
|
parser.set_defaults(func=getpid_argparse)
|
||||||
|
|
||||||
return betterhelp.single_main(argv, parser, __doc__)
|
return betterhelp.go(parser, argv)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
raise SystemExit(main(sys.argv[1:]))
|
raise SystemExit(main(sys.argv[1:]))
|
||||||
|
|
137
gitcheckup.py
137
gitcheckup.py
|
@ -1,50 +1,3 @@
|
||||||
'''
|
|
||||||
gitcheckup
|
|
||||||
==========
|
|
||||||
|
|
||||||
This program helps you check the commit and push status of your favorite git
|
|
||||||
repositories. The output looks like this:
|
|
||||||
|
|
||||||
[ ][P] D:\\Git\\cmd (~1)
|
|
||||||
[C][P] D:\\Git\\Etiquette
|
|
||||||
[ ][P] D:\\Git\\voussoirkit (+1)
|
|
||||||
[C][ ] D:\\Git\\YCDL (↑3)
|
|
||||||
|
|
||||||
To specify the list of git directories, you may either:
|
|
||||||
- Create a gitcheckup.txt file in the same directory as this file, where every
|
|
||||||
line contains an absolute path to the directory, or
|
|
||||||
- Pass directories as a series of positional arguments to this program.
|
|
||||||
|
|
||||||
> gitcheckup.py <flags>
|
|
||||||
> gitcheckup.py dir1 dir2 <flags>
|
|
||||||
|
|
||||||
flags:
|
|
||||||
--fetch:
|
|
||||||
Run `git fetch --all` in each directory.
|
|
||||||
|
|
||||||
--pull:
|
|
||||||
Run `git pull --all` in each directory.
|
|
||||||
|
|
||||||
--push:
|
|
||||||
Run `git push` in each directory.
|
|
||||||
|
|
||||||
--run <command>:
|
|
||||||
Run `git <command>` in each directory. You can use \- to escape - in your
|
|
||||||
git arguments, since they would confuse this program's argparse.
|
|
||||||
If this is used, any --fetch, --pull, --push is ignored.
|
|
||||||
|
|
||||||
--add path:
|
|
||||||
Add path to the gitcheckup.txt file.
|
|
||||||
|
|
||||||
--remove path:
|
|
||||||
Remove path from the gitcheckup.txt file.
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
> gitcheckup
|
|
||||||
> gitcheckup --fetch
|
|
||||||
> gitcheckup D:\\Git\\cmd D:\\Git\\YCDL --pull
|
|
||||||
> gitcheckup --run add README.md
|
|
||||||
'''
|
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
@ -349,19 +302,91 @@ def gitcheckup_argparse(args):
|
||||||
|
|
||||||
@vlogging.main_decorator
|
@vlogging.main_decorator
|
||||||
def main(argv):
|
def main(argv):
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
parser = argparse.ArgumentParser(
|
||||||
|
description='''
|
||||||
|
This program helps you check the commit and push status of your favorite git
|
||||||
|
repositories. The output looks like this:
|
||||||
|
|
||||||
parser.add_argument('directories', nargs='*')
|
[ ][P] D:\\Git\\cmd (~1)
|
||||||
parser.add_argument('--fetch', dest='do_fetch', action='store_true')
|
[C][P] D:\\Git\\Etiquette
|
||||||
parser.add_argument('--pull', dest='do_pull', action='store_true')
|
[ ][P] D:\\Git\\voussoirkit (+1)
|
||||||
parser.add_argument('--push', dest='do_push', action='store_true')
|
[C][ ] D:\\Git\\YCDL (↑3)
|
||||||
parser.add_argument('--add', dest='add_directory')
|
''',
|
||||||
parser.add_argument('--run', dest='run_command', nargs='+')
|
)
|
||||||
parser.add_argument('--remove', dest='remove_directory')
|
parser.examples = [
|
||||||
|
'',
|
||||||
|
'--fetch',
|
||||||
|
'D:\\Git\\cmd D:\\Git\\YCDL --pull',
|
||||||
|
'--run add README.md',
|
||||||
|
]
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'directories',
|
||||||
|
nargs='*',
|
||||||
|
help='''
|
||||||
|
One or more directories to check up.
|
||||||
|
If omitted, you should have a file called gitcheckup.txt in the same
|
||||||
|
directory as this file, where every line contains an absolute path to
|
||||||
|
a directory.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--fetch',
|
||||||
|
dest='do_fetch',
|
||||||
|
action='store_true',
|
||||||
|
help='''
|
||||||
|
Run `git fetch --all` in each directory.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--pull',
|
||||||
|
dest='do_pull',
|
||||||
|
action='store_true',
|
||||||
|
help='''
|
||||||
|
Run `git pull --all` in each directory.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--push',
|
||||||
|
dest='do_push',
|
||||||
|
action='store_true',
|
||||||
|
help='''
|
||||||
|
Run `git push` in each directory.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--run',
|
||||||
|
dest='run_command',
|
||||||
|
nargs='+',
|
||||||
|
type=str,
|
||||||
|
help='''
|
||||||
|
Run `git <command>` in each directory. You can use \- to escape - in your
|
||||||
|
git arguments, since they would confuse this program's argparse.
|
||||||
|
If this is used, any --fetch, --pull, --push is ignored.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--add',
|
||||||
|
dest='add_directory',
|
||||||
|
metavar='path',
|
||||||
|
type=str,
|
||||||
|
help='''
|
||||||
|
Add path to the gitcheckup.txt file.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--remove',
|
||||||
|
dest='remove_directory',
|
||||||
|
metavar='path',
|
||||||
|
type=str,
|
||||||
|
help='''
|
||||||
|
Remove path from the gitcheckup.txt file.
|
||||||
|
''',
|
||||||
|
)
|
||||||
parser.set_defaults(func=gitcheckup_argparse)
|
parser.set_defaults(func=gitcheckup_argparse)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return betterhelp.single_main(argv, parser, docstring=__doc__)
|
return betterhelp.go(parser, argv)
|
||||||
except GitCheckupException as exc:
|
except GitCheckupException as exc:
|
||||||
print(exc)
|
print(exc)
|
||||||
return 1
|
return 1
|
||||||
|
|
|
@ -85,11 +85,17 @@
|
||||||
# | 40 | n | Pixel bytes, r, g, b, a. |
|
# | 40 | n | Pixel bytes, r, g, b, a. |
|
||||||
# |________|______________|_______________________________________________________|
|
# |________|______________|_______________________________________________________|
|
||||||
|
|
||||||
|
import argparse
|
||||||
import os
|
import os
|
||||||
import PIL.Image
|
import PIL.Image
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from voussoirkit import betterhelp
|
||||||
from voussoirkit import imagetools
|
from voussoirkit import imagetools
|
||||||
|
from voussoirkit import pipeable
|
||||||
|
from voussoirkit import vlogging
|
||||||
|
|
||||||
|
log = vlogging.get_logger(__name__, 'icoconvert')
|
||||||
|
|
||||||
ICO_HEADER_LENGTH = 6
|
ICO_HEADER_LENGTH = 6
|
||||||
ICON_DIRECTORY_ENTRY_LENGTH = 16
|
ICON_DIRECTORY_ENTRY_LENGTH = 16
|
||||||
|
@ -230,17 +236,36 @@ def images_to_ico(images):
|
||||||
final_data = b''.join(datablobs)
|
final_data = b''.join(datablobs)
|
||||||
return final_data
|
return final_data
|
||||||
|
|
||||||
if __name__ == '__main__':
|
def icoconvert_argparse(args):
|
||||||
try:
|
log.info('Iconifying %s', args.files)
|
||||||
inputfiles = sys.argv[1:]
|
images = [load_image(filename) for filename in args.files]
|
||||||
except Exception:
|
|
||||||
print('Please provide an image file')
|
|
||||||
raise SystemExit
|
|
||||||
print('Iconifying', inputfiles)
|
|
||||||
images = [load_image(filename) for filename in inputfiles]
|
|
||||||
final_data = images_to_ico(images)
|
final_data = images_to_ico(images)
|
||||||
name = os.path.splitext(inputfiles[0])[0] + '.ico'
|
|
||||||
output_file = open(name, 'wb')
|
iconame = os.path.splitext(args.files[0])[0] + '.ico'
|
||||||
|
output_file = open(iconame, 'wb')
|
||||||
output_file.write(final_data)
|
output_file.write(final_data)
|
||||||
output_file.close()
|
output_file.close()
|
||||||
print('Finished %s.' % name)
|
pipeable.stderr(iconame)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
@vlogging.main_decorator
|
||||||
|
def main(argv):
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description='''
|
||||||
|
Create a Windows .ico icon file from one or more images.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'files',
|
||||||
|
nargs='+',
|
||||||
|
help='''
|
||||||
|
One or more image files to put into the ico.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.set_defaults(func=icoconvert_argparse)
|
||||||
|
|
||||||
|
return betterhelp.go(parser, argv)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
raise SystemExit(main(sys.argv[1:]))
|
||||||
|
|
18
inodes.py
18
inodes.py
|
@ -13,12 +13,22 @@ def inodes_argparse(args):
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def main(argv):
|
def main(argv):
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
parser = argparse.ArgumentParser(
|
||||||
|
description='''
|
||||||
parser.add_argument('patterns', nargs='+')
|
Show the st_dev, st_ino of files.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'patterns',
|
||||||
|
nargs='+',
|
||||||
|
help='''
|
||||||
|
One or more glob patterns. Supports pipeable !c clipboard, !i stdin
|
||||||
|
lines of patterns.
|
||||||
|
''',
|
||||||
|
)
|
||||||
parser.set_defaults(func=inodes_argparse)
|
parser.set_defaults(func=inodes_argparse)
|
||||||
|
|
||||||
return betterhelp.single_main(argv, parser, __doc__)
|
return betterhelp.go(parser, argv)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
raise SystemExit(main(sys.argv[1:]))
|
raise SystemExit(main(sys.argv[1:]))
|
||||||
|
|
|
@ -1,22 +1,3 @@
|
||||||
'''
|
|
||||||
named_python
|
|
||||||
============
|
|
||||||
|
|
||||||
Because Python is interpreted, when you look at the task manager / process list
|
|
||||||
you'll see that every running python instance has the same name, python.exe.
|
|
||||||
This script helps you name the executables so they stand out.
|
|
||||||
|
|
||||||
For the time being this script doesn't automatically call your new exe, you
|
|
||||||
have to write a second command to actually run it. I tried using
|
|
||||||
subprocess.Popen to spawn the new python with the rest of argv but the behavior
|
|
||||||
was different on Linux and Windows and neither was really clean.
|
|
||||||
|
|
||||||
> named_python name
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
> named_python myserver && python-myserver server.py --port 8080
|
|
||||||
> named_python hnarchive && python-hnarchive hnarchive.py livestream
|
|
||||||
'''
|
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
@ -40,12 +21,29 @@ def namedpython_argparse(args):
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def main(argv):
|
def main(argv):
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
parser = argparse.ArgumentParser(
|
||||||
|
description='''
|
||||||
|
Because Python is interpreted, when you look at the task manager / process list
|
||||||
|
you'll see that every running python instance has the same name, python.exe.
|
||||||
|
This script helps you name the executables so they stand out.
|
||||||
|
|
||||||
parser.add_argument('name')
|
For the time being this script doesn't automatically call your new exe, you
|
||||||
|
have to write a second command to actually run it. I tried using
|
||||||
|
subprocess.Popen to spawn the new python with the rest of argv but the behavior
|
||||||
|
was different on Linux and Windows and neither was really clean.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'name',
|
||||||
|
type=str,
|
||||||
|
help='''
|
||||||
|
If you invoke this script with python.exe, a hardlink python-{name}.exe
|
||||||
|
will be created. Also works with pythonw.
|
||||||
|
''',
|
||||||
|
)
|
||||||
parser.set_defaults(func=namedpython_argparse)
|
parser.set_defaults(func=namedpython_argparse)
|
||||||
|
|
||||||
return betterhelp.single_main(argv, parser, __doc__)
|
return betterhelp.go(parser, argv)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
raise SystemExit(main(sys.argv[1:]))
|
raise SystemExit(main(sys.argv[1:]))
|
||||||
|
|
|
@ -1,17 +1,10 @@
|
||||||
'''
|
|
||||||
no smart quotes
|
|
||||||
===============
|
|
||||||
|
|
||||||
Replace smart quotes and smart apostrophes with regular ASCII values.
|
|
||||||
|
|
||||||
Just say no to smart quotes!
|
|
||||||
'''
|
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from voussoirkit import betterhelp
|
from voussoirkit import betterhelp
|
||||||
from voussoirkit import pathclass
|
from voussoirkit import pathclass
|
||||||
|
from voussoirkit import pipeable
|
||||||
from voussoirkit import spinal
|
from voussoirkit import spinal
|
||||||
from voussoirkit import vlogging
|
from voussoirkit import vlogging
|
||||||
|
|
||||||
|
@ -27,8 +20,9 @@ def replace_smartquotes(text):
|
||||||
return text
|
return text
|
||||||
|
|
||||||
def nosmartquotes_argparse(args):
|
def nosmartquotes_argparse(args):
|
||||||
|
globs = list(pipeable.input_many(args.patterns))
|
||||||
files = spinal.walk(
|
files = spinal.walk(
|
||||||
glob_filenames=args.filename_glob,
|
glob_filenames=globs,
|
||||||
exclude_filenames={THIS_FILE},
|
exclude_filenames={THIS_FILE},
|
||||||
recurse=args.recurse,
|
recurse=args.recurse,
|
||||||
)
|
)
|
||||||
|
@ -43,19 +37,39 @@ def nosmartquotes_argparse(args):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
file.write('w', text, encoding='utf-8')
|
file.write('w', text, encoding='utf-8')
|
||||||
print(file.absolute_path)
|
pipeable.stdout(file.absolute_path)
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@vlogging.main_decorator
|
@vlogging.main_decorator
|
||||||
def main(argv):
|
def main(argv):
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
parser = argparse.ArgumentParser(
|
||||||
|
description='''
|
||||||
|
Replace smart quotes and smart apostrophes with regular ASCII values.
|
||||||
|
|
||||||
parser.add_argument('filename_glob')
|
Just say no to smart quotes!
|
||||||
parser.add_argument('--recurse', action='store_true')
|
''',
|
||||||
|
)
|
||||||
|
parser.examples = [
|
||||||
|
'*.md --recurse',
|
||||||
|
]
|
||||||
|
parser.add_argument(
|
||||||
|
'patterns',
|
||||||
|
nargs='+',
|
||||||
|
help='''
|
||||||
|
One or more glob patterns for input files.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--recurse',
|
||||||
|
action='store_true',
|
||||||
|
help='''
|
||||||
|
If provided, recurse into subdirectories and process those files too.
|
||||||
|
''',
|
||||||
|
)
|
||||||
parser.set_defaults(func=nosmartquotes_argparse)
|
parser.set_defaults(func=nosmartquotes_argparse)
|
||||||
|
|
||||||
return betterhelp.single_main(argv, parser, __doc__)
|
return betterhelp.go(parser, argv)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
raise SystemExit(main(sys.argv[1:]))
|
raise SystemExit(main(sys.argv[1:]))
|
||||||
|
|
|
@ -1,11 +1,3 @@
|
||||||
'''
|
|
||||||
This program deletes all empty directories which are children of the given
|
|
||||||
starting directory. The starting directory itself will not be deleted even
|
|
||||||
if it is empty.
|
|
||||||
|
|
||||||
> prune_dirs .
|
|
||||||
> prune_dirs C:\\somepath
|
|
||||||
'''
|
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
@ -46,12 +38,17 @@ def prune_dirs_argparse(args):
|
||||||
|
|
||||||
@vlogging.main_decorator
|
@vlogging.main_decorator
|
||||||
def main(argv):
|
def main(argv):
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
parser = argparse.ArgumentParser(
|
||||||
|
description='''
|
||||||
|
This program deletes all empty directories which are children of the given
|
||||||
|
starting directory. The starting directory itself will not be deleted even
|
||||||
|
if it is empty.
|
||||||
|
''',
|
||||||
|
)
|
||||||
parser.add_argument('starting')
|
parser.add_argument('starting')
|
||||||
parser.set_defaults(func=prune_dirs_argparse)
|
parser.set_defaults(func=prune_dirs_argparse)
|
||||||
|
|
||||||
return betterhelp.single_main(argv, parser, docstring=__doc__)
|
return betterhelp.go(parser, argv)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
raise SystemExit(main(sys.argv[1:]))
|
raise SystemExit(main(sys.argv[1:]))
|
||||||
|
|
193
rarpar.py
193
rarpar.py
|
@ -460,64 +460,6 @@ def rarpar(
|
||||||
|
|
||||||
# COMMAND LINE #####################################################################################
|
# COMMAND LINE #####################################################################################
|
||||||
|
|
||||||
DOCSTRING = '''
|
|
||||||
rarpar
|
|
||||||
======
|
|
||||||
|
|
||||||
> rarpar path <flags>
|
|
||||||
|
|
||||||
path:
|
|
||||||
The input file or directory to rarpar.
|
|
||||||
|
|
||||||
--volume X | X% | min(A, B) | max(A, B):
|
|
||||||
Split rars into volumes of this many megabytes. Should be
|
|
||||||
An integer number of megabytes, or;
|
|
||||||
A percentage "X%" to calculate volumes as X% of the file size, down to
|
|
||||||
a 1 MB minimum, or;
|
|
||||||
A string "min(A, B)" or "max(A, B)" where A and B follow the above rules.
|
|
||||||
|
|
||||||
--rec X:
|
|
||||||
An integer to generate X% recovery record in the rars.
|
|
||||||
See winrar documentation for information about recovery records.
|
|
||||||
|
|
||||||
--rev X:
|
|
||||||
An integer to generate X% recovery volumes.
|
|
||||||
Note that winrar's behavior is the number of revs will always be less than
|
|
||||||
the number of rars. If you don't split volumes, you will have 1 rar and
|
|
||||||
thus 0 revs even if you ask for 100% rev.
|
|
||||||
See winrar documentation for information about recovery volumes.
|
|
||||||
|
|
||||||
--par X:
|
|
||||||
A number to generate X% recovery with par2.
|
|
||||||
|
|
||||||
--basename X:
|
|
||||||
A basename for the rar and par files. You will end up with
|
|
||||||
basename.partXX.rar and basename.par2.
|
|
||||||
Without this argument, the default basename is "{basename} ({timestamp})".
|
|
||||||
Your string may include {basename}, {timestamp} and/or {date} including the
|
|
||||||
braces to insert that value there.
|
|
||||||
|
|
||||||
--compression X:
|
|
||||||
Level of compression. Can be "store" or "max" or integer 0-5.
|
|
||||||
|
|
||||||
--password X:
|
|
||||||
A password with which to encrypt the rar files.
|
|
||||||
|
|
||||||
--workdir X:
|
|
||||||
The directory in which the rars and pars will be generated while the
|
|
||||||
program is working.
|
|
||||||
|
|
||||||
--moveto X:
|
|
||||||
The directory to which the rars and pars will be moved after the program
|
|
||||||
has finished working.
|
|
||||||
|
|
||||||
--recycle:
|
|
||||||
The input file or directory will be recycled at the end.
|
|
||||||
|
|
||||||
--dry:
|
|
||||||
Print the commands that will be run, but don't actually run them.
|
|
||||||
'''
|
|
||||||
|
|
||||||
def rarpar_argparse(args):
|
def rarpar_argparse(args):
|
||||||
status = 0
|
status = 0
|
||||||
try:
|
try:
|
||||||
|
@ -549,24 +491,127 @@ def rarpar_argparse(args):
|
||||||
def main(argv):
|
def main(argv):
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
|
|
||||||
parser.add_argument('path')
|
parser.add_argument(
|
||||||
parser.add_argument('--volume')
|
'path',
|
||||||
parser.add_argument('--rec')
|
type=pathclass.Path,
|
||||||
parser.add_argument('--rev')
|
help='''
|
||||||
parser.add_argument('--par')
|
The input file or directory to rarpar.
|
||||||
parser.add_argument('--basename')
|
''',
|
||||||
parser.add_argument('--compression')
|
)
|
||||||
parser.add_argument('--password')
|
parser.add_argument(
|
||||||
parser.add_argument('--profile', dest='rar_profile')
|
'--volume',
|
||||||
parser.add_argument('--workdir', default='.')
|
help='''
|
||||||
parser.add_argument('--moveto')
|
Split rars into volumes of this many megabytes. Should be:
|
||||||
parser.add_argument('--recycle', dest='recycle_original', action='store_true')
|
- An integer number of megabytes, or
|
||||||
parser.add_argument('--dictionary', dest='dictionary_size')
|
- A percentage "X%" to calculate volumes as X% of the file size, down to
|
||||||
parser.add_argument('--solid', action='store_true')
|
a 1 MB minimum, or
|
||||||
parser.add_argument('--dry', action='store_true')
|
- A string "min(A, B)" or "max(A, B)" where A and B follow the above rules.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--rec',
|
||||||
|
type=int,
|
||||||
|
help='''
|
||||||
|
An integer to generate X% recovery record in the rars.
|
||||||
|
See winrar documentation for information about recovery records.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--rev',
|
||||||
|
type=int,
|
||||||
|
help='''
|
||||||
|
An integer to generate X% recovery volumes.
|
||||||
|
Note that winrar's behavior is the number of revs will always be less than
|
||||||
|
the number of rars. If you don't split volumes, you will have 1 rar and
|
||||||
|
thus 0 revs even if you ask for 100% rev.
|
||||||
|
See winrar documentation for information about recovery volumes.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--par',
|
||||||
|
type=int,
|
||||||
|
help='''
|
||||||
|
A number to generate X% recovery with par2.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--basename',
|
||||||
|
type=str,
|
||||||
|
help='''
|
||||||
|
A basename for the rar and par files. You will end up with
|
||||||
|
basename.partXX.rar and basename.par2.
|
||||||
|
Without this argument, the default basename is "{basename} ({timestamp})".
|
||||||
|
Your string may include {basename}, {timestamp} and/or {date} including the
|
||||||
|
braces to insert that value there.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--compression',
|
||||||
|
help='''
|
||||||
|
Level of compression. Can be "store" or "max" or integer 0-5.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--password',
|
||||||
|
type=str,
|
||||||
|
help='''
|
||||||
|
A password with which to encrypt the rar files.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--profile', dest='rar_profile',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--workdir',
|
||||||
|
type=pathclass.Path,
|
||||||
|
default='.',
|
||||||
|
help='''
|
||||||
|
The directory in which the rars and pars will be generated while the
|
||||||
|
program is working.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--moveto',
|
||||||
|
type=pathclass.Path,
|
||||||
|
help='''
|
||||||
|
The directory to which the rars and pars will be moved after the program
|
||||||
|
has finished working.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--recycle',
|
||||||
|
dest='recycle_original',
|
||||||
|
action='store_true',
|
||||||
|
help='''
|
||||||
|
The input file or directory will be recycled at the end.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--dictionary',
|
||||||
|
dest='dictionary_size',
|
||||||
|
help='''
|
||||||
|
Larger dictionary sizes can improve compression in exchange for higher
|
||||||
|
memory usage. Accepted values are 128k, 256k, 512k, 1m, 2m, 4m, 8m, 16m,
|
||||||
|
32m, 64m, 128m, 256m, 512m, 1g.
|
||||||
|
'''
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--solid',
|
||||||
|
action='store_true',
|
||||||
|
help='''
|
||||||
|
Generate a 'solid' rar archive. See winrar's documentation for details.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--dry',
|
||||||
|
action='store_true',
|
||||||
|
help='''
|
||||||
|
Print the commands that will be run, but don't actually run them.
|
||||||
|
''',
|
||||||
|
)
|
||||||
parser.set_defaults(func=rarpar_argparse)
|
parser.set_defaults(func=rarpar_argparse)
|
||||||
|
|
||||||
return betterhelp.single_main(argv, parser, DOCSTRING)
|
return betterhelp.go(parser, argv)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
raise SystemExit(main(sys.argv[1:]))
|
raise SystemExit(main(sys.argv[1:]))
|
||||||
|
|
|
@ -1,32 +1,3 @@
|
||||||
'''
|
|
||||||
reg_extension_icon
|
|
||||||
==================
|
|
||||||
|
|
||||||
This script edits the windows registry HKEY_CLASSES_ROOT to assign a file
|
|
||||||
extension icon and optionally a human-friendly name string.
|
|
||||||
|
|
||||||
Must run as administrator.
|
|
||||||
|
|
||||||
WARNING, if the extension is already associated with a program, or is otherwise
|
|
||||||
connected to a progid, this will break it.
|
|
||||||
|
|
||||||
> reg_extension_icon ico_file <flags>
|
|
||||||
|
|
||||||
ico_file:
|
|
||||||
Filepath of the icon file.
|
|
||||||
|
|
||||||
--extension:
|
|
||||||
If you omit this option, your file should be named "png.ico" or "py.ico" to
|
|
||||||
set the icon for png and py types. If the name of your ico file is not the
|
|
||||||
name of the extension you want to control, specify the extension here.
|
|
||||||
|
|
||||||
--name:
|
|
||||||
A human-friendly name string which will show on Explorer under the "Type"
|
|
||||||
column and in the properties dialog.
|
|
||||||
|
|
||||||
--shellopen:
|
|
||||||
A command-line string to use as the shell\open\command
|
|
||||||
'''
|
|
||||||
import argparse
|
import argparse
|
||||||
import sys
|
import sys
|
||||||
import winreg
|
import winreg
|
||||||
|
@ -97,16 +68,56 @@ def extension_registry_argparse(args):
|
||||||
)
|
)
|
||||||
|
|
||||||
def main(argv):
|
def main(argv):
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
parser = argparse.ArgumentParser(
|
||||||
|
description='''
|
||||||
|
This script edits the windows registry HKEY_CLASSES_ROOT to assign a file
|
||||||
|
extension icon and optionally a human-friendly name string.
|
||||||
|
|
||||||
parser.add_argument('ico_file')
|
Must run as administrator.
|
||||||
parser.add_argument('--extension', default=None)
|
|
||||||
parser.add_argument('--name', default=None)
|
WARNING, if the extension is already associated with a program, or is otherwise
|
||||||
parser.add_argument('--shellopen', default=None)
|
connected to a progid, this will break it.
|
||||||
parser.add_argument('--yes', dest='autoyes', action='store_true')
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'ico_file',
|
||||||
|
help='''
|
||||||
|
Filepath of the icon file.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--extension',
|
||||||
|
default=None,
|
||||||
|
help='''
|
||||||
|
If you omit this option, your file should be named "png.ico" or "py.ico" to
|
||||||
|
set the icon for png and py types. If the name of your ico file is not the
|
||||||
|
name of the extension you want to control, specify the extension here.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--name',
|
||||||
|
type=str,
|
||||||
|
default=None,
|
||||||
|
help='''
|
||||||
|
A human-friendly name string which will show on Explorer under the "Type"
|
||||||
|
column and in the properties dialog.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--shellopen',
|
||||||
|
default=None,
|
||||||
|
help='''
|
||||||
|
A command-line string to use as the shell\\open\\command
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--yes',
|
||||||
|
dest='autoyes',
|
||||||
|
action='store_true',
|
||||||
|
)
|
||||||
parser.set_defaults(func=extension_registry_argparse)
|
parser.set_defaults(func=extension_registry_argparse)
|
||||||
|
|
||||||
return betterhelp.single_main(argv, parser, __doc__)
|
return betterhelp.go(parser, argv)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
raise SystemExit(main(sys.argv[1:]))
|
raise SystemExit(main(sys.argv[1:]))
|
||||||
|
|
|
@ -1,17 +1,3 @@
|
||||||
'''
|
|
||||||
reserve_disk_space
|
|
||||||
==================
|
|
||||||
|
|
||||||
Exits with status of 0 if the disk has the requested amount of space, 1 if not.
|
|
||||||
|
|
||||||
> reserve_disk_space reserve [drive]
|
|
||||||
|
|
||||||
reserve:
|
|
||||||
A string like "50g" or "100 gb"
|
|
||||||
|
|
||||||
drive:
|
|
||||||
Filepath to the drive you want to check. Defaults to cwd drive.
|
|
||||||
'''
|
|
||||||
import argparse
|
import argparse
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
@ -39,13 +25,29 @@ def reserve_disk_space_argparse(args):
|
||||||
|
|
||||||
@vlogging.main_decorator
|
@vlogging.main_decorator
|
||||||
def main(argv):
|
def main(argv):
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
parser = argparse.ArgumentParser(
|
||||||
|
description='''
|
||||||
parser.add_argument('reserve')
|
Exits with status of 0 if the disk has the requested amount of space, 1 if not.
|
||||||
parser.add_argument('drive', nargs='?', default='.')
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'reserve',
|
||||||
|
type=str,
|
||||||
|
help='''
|
||||||
|
A string like "50g" or "100 gb"
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'drive',
|
||||||
|
nargs='?',
|
||||||
|
default='.',
|
||||||
|
help='''
|
||||||
|
Filepath to the drive you want to check.
|
||||||
|
''',
|
||||||
|
)
|
||||||
parser.set_defaults(func=reserve_disk_space_argparse)
|
parser.set_defaults(func=reserve_disk_space_argparse)
|
||||||
|
|
||||||
return betterhelp.single_main(argv, parser, __doc__)
|
return betterhelp.go(parser, argv)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
raise SystemExit(main(sys.argv[1:]))
|
raise SystemExit(main(sys.argv[1:]))
|
||||||
|
|
195
resize.py
195
resize.py
|
@ -1,65 +1,3 @@
|
||||||
'''
|
|
||||||
resize
|
|
||||||
======
|
|
||||||
|
|
||||||
Resize image files.
|
|
||||||
|
|
||||||
> resize patterns <flags>
|
|
||||||
|
|
||||||
patterns:
|
|
||||||
One or more glob patterns for input files.
|
|
||||||
Uses pipeable to support !c clipboard, !i stdin lines of glob patterns.
|
|
||||||
|
|
||||||
flags:
|
|
||||||
--width X:
|
|
||||||
--height X:
|
|
||||||
New dimensions for the image. If either of these is omitted, then that
|
|
||||||
dimension will be calculated automatically based on the aspect ratio.
|
|
||||||
|
|
||||||
--break_aspect_ratio:
|
|
||||||
If provided, the given --width and --height will be used exactly. You will
|
|
||||||
need to provide both --width and --height.
|
|
||||||
|
|
||||||
If omitted, the image will be resized to fit within the bounds provided by
|
|
||||||
--width and --height while preserving its aspect ratio.
|
|
||||||
|
|
||||||
--output X:
|
|
||||||
A string that controls the output filename format. Suppose the input file
|
|
||||||
was myphoto.jpg. You can use these variables in your format string:
|
|
||||||
{base} = myphoto
|
|
||||||
{filename} = myphoto.jpg
|
|
||||||
{width} = an integer
|
|
||||||
{height} = an integer
|
|
||||||
{extension} = .jpg
|
|
||||||
|
|
||||||
You may omit {extension} from your format string and it will automatically
|
|
||||||
be added to the end, unless you already provided a different extension.
|
|
||||||
|
|
||||||
If your format string only designates a basename, output files will go to
|
|
||||||
the same directory as the corresponding input file. If your string contains
|
|
||||||
path separators, all output files will go to that directory.
|
|
||||||
The directory
|
|
||||||
part is not formatted with the variables.
|
|
||||||
|
|
||||||
--inplace:
|
|
||||||
Overwrite the input files. Cannot be used along with --output.
|
|
||||||
Be careful!
|
|
||||||
|
|
||||||
--nearest:
|
|
||||||
If provided, use nearest-neighbor scaling to preserve pixelated images.
|
|
||||||
If omitted, use antialiased scaling.
|
|
||||||
|
|
||||||
--only_shrink:
|
|
||||||
If the input image is smaller than the requested dimensions, do nothing.
|
|
||||||
Useful when globbing in a directory with many differently sized images.
|
|
||||||
|
|
||||||
--quality X:
|
|
||||||
JPEG compression quality.
|
|
||||||
|
|
||||||
--scale X:
|
|
||||||
Scale the image by factor X.
|
|
||||||
Use this option instead of --width, --height.
|
|
||||||
'''
|
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
import PIL.Image
|
import PIL.Image
|
||||||
|
@ -77,15 +15,6 @@ log = vlogging.getLogger(__name__, 'resize')
|
||||||
OUTPUT_INPLACE = sentinel.Sentinel('output inplace')
|
OUTPUT_INPLACE = sentinel.Sentinel('output inplace')
|
||||||
DEFAULT_OUTPUT_FORMAT = '{base}_{width}x{height}{extension}'
|
DEFAULT_OUTPUT_FORMAT = '{base}_{width}x{height}{extension}'
|
||||||
|
|
||||||
def resize_core(
|
|
||||||
image,
|
|
||||||
height=None,
|
|
||||||
only_shrink=False,
|
|
||||||
scale=None,
|
|
||||||
width=None,
|
|
||||||
):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def resize(
|
def resize(
|
||||||
filename,
|
filename,
|
||||||
*,
|
*,
|
||||||
|
@ -215,21 +144,121 @@ def resize_argparse(args):
|
||||||
|
|
||||||
@vlogging.main_decorator
|
@vlogging.main_decorator
|
||||||
def main(argv):
|
def main(argv):
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
parser = argparse.ArgumentParser(description='Resize image files.')
|
||||||
|
parser.examples = [
|
||||||
|
'myphoto.jpg --scale 0.5 --inplace',
|
||||||
|
'*.jpg --only_shrink --width 500 --height 500 --output thumbs\\{base}.jpg',
|
||||||
|
'sprite*.png sprite*.bmp --width 1024 --nearest --output {base}_big{extension}',
|
||||||
|
]
|
||||||
|
|
||||||
parser.add_argument('patterns', nargs='+')
|
parser.add_argument(
|
||||||
parser.add_argument('--width', type=int, default=None)
|
'patterns',
|
||||||
parser.add_argument('--height', type=int, default=None)
|
nargs='+',
|
||||||
parser.add_argument('--inplace', action='store_true')
|
type=str,
|
||||||
parser.add_argument('--nearest', dest='nearest_neighbor', action='store_true')
|
help='''
|
||||||
parser.add_argument('--only_shrink', '--only-shrink', action='store_true')
|
One or more glob patterns for input files.
|
||||||
parser.add_argument('--break_aspect_ratio', '--break-aspect-ratio', action='store_true')
|
Uses pipeable to support !c clipboard, !i stdin lines of glob patterns.
|
||||||
parser.add_argument('--output', default=None)
|
''',
|
||||||
parser.add_argument('--scale', type=float, default=None)
|
)
|
||||||
parser.add_argument('--quality', type=int, default=100)
|
parser.add_argument(
|
||||||
|
'--width',
|
||||||
|
type=int,
|
||||||
|
default=None,
|
||||||
|
help='''
|
||||||
|
New width of the image. If --width is omitted and --height is given, then
|
||||||
|
width will be calculated automatically based on the aspect ratio.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--height',
|
||||||
|
type=int,
|
||||||
|
default=None,
|
||||||
|
help='''
|
||||||
|
New width of the image. If --height is omitted and --width is given, then
|
||||||
|
height will be calculated automatically based on the aspect ratio.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--break_aspect_ratio',
|
||||||
|
'--break-aspect-ratio',
|
||||||
|
action='store_true',
|
||||||
|
help='''
|
||||||
|
If provided, the given --width and --height will be used exactly. You will
|
||||||
|
need to provide both --width and --height.
|
||||||
|
|
||||||
|
If omitted, the image will be resized to fit within the bounds provided by
|
||||||
|
--width and --height while preserving its aspect ratio.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--inplace',
|
||||||
|
action='store_true',
|
||||||
|
help='''
|
||||||
|
Overwrite the input files. Cannot be used along with --output.
|
||||||
|
Be careful!
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--nearest',
|
||||||
|
'--nearest_neighbor',
|
||||||
|
'--nearest-neighbor',
|
||||||
|
dest='nearest_neighbor',
|
||||||
|
action='store_true',
|
||||||
|
help='''
|
||||||
|
If provided, use nearest-neighbor scaling to preserve pixelated images.
|
||||||
|
If omitted, use antialiased scaling.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--only_shrink',
|
||||||
|
'--only-shrink',
|
||||||
|
action='store_true',
|
||||||
|
help='''
|
||||||
|
If the input image is smaller than the requested dimensions, do nothing.
|
||||||
|
Useful when globbing in a directory with many differently sized images.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--output',
|
||||||
|
default=None,
|
||||||
|
help='''
|
||||||
|
A string that controls the output filename format. Suppose the input file
|
||||||
|
was myphoto.jpg. You can use these variables in your format string:
|
||||||
|
{base} = myphoto
|
||||||
|
{filename} = myphoto.jpg
|
||||||
|
{width} = an integer
|
||||||
|
{height} = an integer
|
||||||
|
{extension} = .jpg
|
||||||
|
|
||||||
|
You may omit {extension} from your format string and it will automatically
|
||||||
|
be added to the end, unless you already provided a different extension.
|
||||||
|
|
||||||
|
If your format string only designates a basename, output files will go to
|
||||||
|
the same directory as the corresponding input file. If your string contains
|
||||||
|
path separators, all output files will go to that directory.
|
||||||
|
The directory part is not formatted with the variables.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--scale',
|
||||||
|
type=float,
|
||||||
|
default=None,
|
||||||
|
help='''
|
||||||
|
Scale the image by this factor, where 1.00 is regular size.
|
||||||
|
Use this option instead of --width, --height.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--quality',
|
||||||
|
type=int,
|
||||||
|
default=100,
|
||||||
|
help='''
|
||||||
|
JPEG compression quality.
|
||||||
|
'''
|
||||||
|
)
|
||||||
parser.set_defaults(func=resize_argparse)
|
parser.set_defaults(func=resize_argparse)
|
||||||
|
|
||||||
return betterhelp.single_main(argv, parser, __doc__)
|
return betterhelp.go(parser, argv)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
raise SystemExit(main(sys.argv[1:]))
|
raise SystemExit(main(sys.argv[1:]))
|
||||||
|
|
37
retry.py
37
retry.py
|
@ -2,6 +2,7 @@ import argparse
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
from voussoirkit import betterhelp
|
||||||
|
|
||||||
class NoMoreRetries(Exception):
|
class NoMoreRetries(Exception):
|
||||||
pass
|
pass
|
||||||
|
@ -43,15 +44,39 @@ def retry_argparse(args):
|
||||||
)
|
)
|
||||||
|
|
||||||
def main(argv):
|
def main(argv):
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
parser = argparse.ArgumentParser(
|
||||||
|
description='''
|
||||||
|
Run a command line command multiple times until it returns 0.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
|
||||||
parser.add_argument('command', nargs='+')
|
parser.add_argument(
|
||||||
parser.add_argument('--limit', type=int, default=None)
|
'command',
|
||||||
parser.add_argument('--sleep', type=float, default=None)
|
nargs='+',
|
||||||
|
help='''
|
||||||
|
A command line command. You may need to put this after -- to avoid
|
||||||
|
confusion with arguments to this program.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--limit',
|
||||||
|
type=int,
|
||||||
|
default=None,
|
||||||
|
help='''
|
||||||
|
Maximum number of retries before giving up.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--sleep',
|
||||||
|
type=float,
|
||||||
|
default=None,
|
||||||
|
help='''
|
||||||
|
Number of seconds of sleep between each retry.
|
||||||
|
''',
|
||||||
|
)
|
||||||
parser.set_defaults(func=retry_argparse)
|
parser.set_defaults(func=retry_argparse)
|
||||||
|
|
||||||
args = parser.parse_args(argv)
|
return betterhelp.go(parser, argv)
|
||||||
return args.func(args)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
raise SystemExit(main(sys.argv[1:]))
|
raise SystemExit(main(sys.argv[1:]))
|
||||||
|
|
67
shortcut.py
67
shortcut.py
|
@ -38,29 +38,6 @@ def shortcut(lnk_name, target, start_in=None, icon=None):
|
||||||
shortcut.write()
|
shortcut.write()
|
||||||
return lnk
|
return lnk
|
||||||
|
|
||||||
DOCSTRING = '''
|
|
||||||
shortcut
|
|
||||||
========
|
|
||||||
|
|
||||||
> shortcut lnk_path target <flags>
|
|
||||||
|
|
||||||
lnk_path:
|
|
||||||
The filepath of the lnk file you want to create.
|
|
||||||
|
|
||||||
target:
|
|
||||||
The filepath of the target file and any additional arguments separated
|
|
||||||
by spaces. If you want to include an argument that starts with hyphens,
|
|
||||||
consider putting this last and use `--` to indicate the end of named
|
|
||||||
arguments. For example:
|
|
||||||
> shortcut game.lnk --icon game.ico -- javaw.exe -jar game.jar
|
|
||||||
|
|
||||||
--start-in:
|
|
||||||
Directory to use as CWD for the program.
|
|
||||||
|
|
||||||
--icon:
|
|
||||||
Path to an .ico file.
|
|
||||||
'''
|
|
||||||
|
|
||||||
def shortcut_argparse(args):
|
def shortcut_argparse(args):
|
||||||
try:
|
try:
|
||||||
lnk = shortcut(
|
lnk = shortcut(
|
||||||
|
@ -77,14 +54,48 @@ def shortcut_argparse(args):
|
||||||
|
|
||||||
def main(argv):
|
def main(argv):
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
|
parser.examples = [
|
||||||
|
'game.lnk --icon game.ico -- javaw.exe -jar game.jar',
|
||||||
|
'game.lnk --icon game.ico -- 4 8 6',
|
||||||
|
]
|
||||||
|
|
||||||
parser.add_argument('lnk_name')
|
parser.add_argument(
|
||||||
parser.add_argument('target', nargs='+')
|
'lnk_name',
|
||||||
parser.add_argument('--start_in', '--start-in', '--startin', default=None)
|
help='''
|
||||||
parser.add_argument('--icon', default=None)
|
The filepath of the lnk file you want to create.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'target',
|
||||||
|
nargs='+',
|
||||||
|
type=int,
|
||||||
|
help='''
|
||||||
|
The filepath of the target file and any additional arguments separated
|
||||||
|
by spaces. If you want to include an argument that starts with hyphens,
|
||||||
|
consider putting this last and use `--` to indicate the end of named
|
||||||
|
arguments, since they might otherwise be mistaken for arguments to this
|
||||||
|
program.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--start_in',
|
||||||
|
'--start-in',
|
||||||
|
'--startin',
|
||||||
|
default=None,
|
||||||
|
help='''
|
||||||
|
Directory to use as CWD for the program.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--icon',
|
||||||
|
default=None,
|
||||||
|
help='''
|
||||||
|
Path to an .ico file.
|
||||||
|
''',
|
||||||
|
)
|
||||||
parser.set_defaults(func=shortcut_argparse)
|
parser.set_defaults(func=shortcut_argparse)
|
||||||
|
|
||||||
return betterhelp.single_main(argv, parser, DOCSTRING)
|
return betterhelp.go(parser, argv)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
raise SystemExit(main(sys.argv[1:]))
|
raise SystemExit(main(sys.argv[1:]))
|
||||||
|
|
34
stitch.py
34
stitch.py
|
@ -2,6 +2,7 @@ import PIL.Image
|
||||||
import argparse
|
import argparse
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from voussoirkit import betterhelp
|
||||||
from voussoirkit import pathclass
|
from voussoirkit import pathclass
|
||||||
from voussoirkit import pipeable
|
from voussoirkit import pipeable
|
||||||
from voussoirkit import sentinel
|
from voussoirkit import sentinel
|
||||||
|
@ -49,14 +50,35 @@ def main(argv):
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
|
|
||||||
parser.add_argument('image_files', nargs='+')
|
parser.add_argument('image_files', nargs='+')
|
||||||
parser.add_argument('--output', required=True)
|
parser.add_argument(
|
||||||
parser.add_argument('--horizontal', action='store_true')
|
'--output',
|
||||||
parser.add_argument('--vertical', action='store_true')
|
required=True,
|
||||||
parser.add_argument('--gap', type=int, default=0)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--horizontal',
|
||||||
|
action='store_true',
|
||||||
|
help='''
|
||||||
|
Stitch the images together horizontally.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--vertical',
|
||||||
|
action='store_true',
|
||||||
|
help='''
|
||||||
|
Stitch the images together vertically.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--gap',
|
||||||
|
type=int,
|
||||||
|
default=0,
|
||||||
|
help='''
|
||||||
|
This many pixels of transparent gap between each row / column.
|
||||||
|
''',
|
||||||
|
)
|
||||||
parser.set_defaults(func=stitch_argparse)
|
parser.set_defaults(func=stitch_argparse)
|
||||||
|
|
||||||
args = parser.parse_args(argv)
|
return betterhelp.go(parser, argv)
|
||||||
return args.func(args)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
raise SystemExit(main(sys.argv[1:]))
|
raise SystemExit(main(sys.argv[1:]))
|
||||||
|
|
86
svgrender.py
86
svgrender.py
|
@ -1,29 +1,3 @@
|
||||||
'''
|
|
||||||
svgrender
|
|
||||||
=========
|
|
||||||
|
|
||||||
Calls the Inkscape command line to render svg files to png. A link to inkscape
|
|
||||||
should be on your PATH.
|
|
||||||
|
|
||||||
> svgrender svg_file scales <flags>
|
|
||||||
|
|
||||||
scales:
|
|
||||||
One or more integers. Each integer will be the size of one output file.
|
|
||||||
|
|
||||||
flags:
|
|
||||||
--destination:
|
|
||||||
A path to a directory where the png files should be saved. By default,
|
|
||||||
they go to the same folder as the svg file.
|
|
||||||
|
|
||||||
--y:
|
|
||||||
By default, the scales control the width of the output image.
|
|
||||||
Pass this if you want the scales to control the height.
|
|
||||||
|
|
||||||
--basename-only:
|
|
||||||
By default, the png filenames will have suffixes like _{scale}.
|
|
||||||
Pass this if you want the png to have the same name as the svg file.
|
|
||||||
Naturally, this only works if you're only using a single scale.
|
|
||||||
'''
|
|
||||||
import argparse
|
import argparse
|
||||||
import glob
|
import glob
|
||||||
import os
|
import os
|
||||||
|
@ -79,11 +53,10 @@ def svgrender(filepath, scales, destination, scale_suffix=True, axis='x'):
|
||||||
|
|
||||||
def svgrender_argparse(args):
|
def svgrender_argparse(args):
|
||||||
svg_paths = glob.glob(args.svg_filepath)
|
svg_paths = glob.glob(args.svg_filepath)
|
||||||
scales = [int(x) for x in args.scales]
|
|
||||||
for svg_path in svg_paths:
|
for svg_path in svg_paths:
|
||||||
svgrender(
|
svgrender(
|
||||||
svg_path,
|
svg_path,
|
||||||
scales,
|
scales=args.scales,
|
||||||
destination=args.destination,
|
destination=args.destination,
|
||||||
scale_suffix=args.scale_suffix,
|
scale_suffix=args.scale_suffix,
|
||||||
axis='y' if args.y else 'x',
|
axis='y' if args.y else 'x',
|
||||||
|
@ -93,16 +66,57 @@ def svgrender_argparse(args):
|
||||||
|
|
||||||
@vlogging.main_decorator
|
@vlogging.main_decorator
|
||||||
def main(argv):
|
def main(argv):
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
parser = argparse.ArgumentParser(
|
||||||
|
description='''
|
||||||
parser.add_argument('svg_filepath')
|
Calls the Inkscape command line to render svg files to png. A link to Inkscape
|
||||||
parser.add_argument('scales', nargs='+')
|
should be on your PATH.
|
||||||
parser.add_argument('--destination', default=None)
|
''',
|
||||||
parser.add_argument('--y', dest='y', action='store_true')
|
)
|
||||||
parser.add_argument('--basename_only', '--basename-only', dest='scale_suffix', action='store_false')
|
parser.add_argument(
|
||||||
|
'svg_filepath',
|
||||||
|
help='''
|
||||||
|
Input svg file to be rendered.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'scales',
|
||||||
|
type=int,
|
||||||
|
nargs='+',
|
||||||
|
help='''
|
||||||
|
One or more integers. Each integer will be the size of one output file.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--destination',
|
||||||
|
default=None,
|
||||||
|
help='''
|
||||||
|
A path to a directory where the png files should be saved. By default,
|
||||||
|
they go to the same folder as the svg file.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--y',
|
||||||
|
dest='y',
|
||||||
|
action='store_true',
|
||||||
|
help='''
|
||||||
|
By default, the scales control the width of the output image.
|
||||||
|
Pass this if you want the scales to control the height.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--basename_only',
|
||||||
|
'--basename-only',
|
||||||
|
dest='scale_suffix',
|
||||||
|
action='store_false',
|
||||||
|
help='''
|
||||||
|
By default, the png filenames will have suffixes like _{scale}.
|
||||||
|
Pass this if you want the png to have the same name as the svg file.
|
||||||
|
Naturally, this only works if you're only using a single scale.
|
||||||
|
''',
|
||||||
|
)
|
||||||
parser.set_defaults(func=svgrender_argparse)
|
parser.set_defaults(func=svgrender_argparse)
|
||||||
|
|
||||||
return betterhelp.single_main(argv, parser, __doc__)
|
return betterhelp.go(parser, argv)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main(sys.argv[1:])
|
main(sys.argv[1:])
|
||||||
|
|
|
@ -1,21 +1,3 @@
|
||||||
'''
|
|
||||||
tempeditor
|
|
||||||
==========
|
|
||||||
|
|
||||||
This program allows you to use your preferred text editor as an intermediate
|
|
||||||
step in a processing pipeline. The user will use the text editor to edit a temp
|
|
||||||
file, and when they close the editor the contents of the temp file will be sent
|
|
||||||
to stdout.
|
|
||||||
|
|
||||||
Command line usage:
|
|
||||||
|
|
||||||
> tempeditor [--text X]
|
|
||||||
|
|
||||||
--text X:
|
|
||||||
The initial text in the document.
|
|
||||||
Uses pipeable to support !c clipboard, !i stdin.
|
|
||||||
If not provided, the user starts with a blank document.
|
|
||||||
'''
|
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
import shlex
|
import shlex
|
||||||
|
@ -90,12 +72,29 @@ def tempeditor_argparse(args):
|
||||||
|
|
||||||
@vlogging.main_decorator
|
@vlogging.main_decorator
|
||||||
def main(argv):
|
def main(argv):
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
parser = argparse.ArgumentParser(
|
||||||
|
description='''
|
||||||
|
This program allows you to use your preferred text editor as an
|
||||||
|
intermediate step in a processing pipeline. The user will use the text
|
||||||
|
editor to edit a temp file, and when they close the editor the contents
|
||||||
|
of the temp file will be sent to stdout.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
|
||||||
parser.add_argument('--text', dest='initial_text', default=None)
|
parser.add_argument(
|
||||||
|
'--text',
|
||||||
|
dest='initial_text',
|
||||||
|
default=None,
|
||||||
|
type=str,
|
||||||
|
help='''
|
||||||
|
The initial text in the document.
|
||||||
|
Uses pipeable to support !c clipboard, !i stdin.
|
||||||
|
If not provided, the user starts with a blank document.
|
||||||
|
''',
|
||||||
|
)
|
||||||
parser.set_defaults(func=tempeditor_argparse)
|
parser.set_defaults(func=tempeditor_argparse)
|
||||||
|
|
||||||
return betterhelp.single_main(argv, parser, __doc__)
|
return betterhelp.go(parser, argv)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
raise SystemExit(main(sys.argv[1:]))
|
raise SystemExit(main(sys.argv[1:]))
|
||||||
|
|
102
threaded_dl.py
102
threaded_dl.py
|
@ -1,40 +1,3 @@
|
||||||
'''
|
|
||||||
threaded_dl
|
|
||||||
===========
|
|
||||||
|
|
||||||
> threaded_dl links thread_count filename_format <flags>
|
|
||||||
|
|
||||||
links:
|
|
||||||
The name of a file containing links to download, one per line.
|
|
||||||
Uses pipeable to support !c clipboard, !i stdin lines of urls.
|
|
||||||
|
|
||||||
thread_count:
|
|
||||||
Integer number of threads to use for downloading.
|
|
||||||
|
|
||||||
filename_format:
|
|
||||||
A string that controls the names of the downloaded files. Uses Python's
|
|
||||||
brace-style formatting. Available formatters are:
|
|
||||||
- {basename}: The name of the file as indicated by the URL.
|
|
||||||
E.g. example.com/image.jpg -> image.jpg
|
|
||||||
- {extension}: The extension of the file as indicated by the URL, including
|
|
||||||
the dot. E.g. example.com/image.jpg -> .jpg
|
|
||||||
- {index}: The index of this URL within the sequence of all downloaded URLs.
|
|
||||||
Starts from 0.
|
|
||||||
- {now}: The unix timestamp at which this download job was started. It might
|
|
||||||
be ugly but at least it's unambiguous when doing multiple download batches
|
|
||||||
with similar filenames.
|
|
||||||
|
|
||||||
flags:
|
|
||||||
--bytespersecond X:
|
|
||||||
Limit the overall download speed to X bytes per second. Uses
|
|
||||||
bytestring.parsebytes to support strings like "1m", "500k", "2 mb", etc.
|
|
||||||
|
|
||||||
--headers X:
|
|
||||||
;
|
|
||||||
|
|
||||||
--timeout X:
|
|
||||||
Integer number of seconds to use as HTTP request timeout for each download.
|
|
||||||
'''
|
|
||||||
import argparse
|
import argparse
|
||||||
import ast
|
import ast
|
||||||
import os
|
import os
|
||||||
|
@ -194,7 +157,7 @@ def threaded_dl(
|
||||||
ui_thread.join()
|
ui_thread.join()
|
||||||
|
|
||||||
def ui_thread_func(meter, pool, stop_event):
|
def ui_thread_func(meter, pool, stop_event):
|
||||||
if pipeable.OUT_PIPE:
|
if pipeable.stdout_pipe():
|
||||||
return
|
return
|
||||||
|
|
||||||
while not stop_event.is_set():
|
while not stop_event.is_set():
|
||||||
|
@ -236,17 +199,62 @@ def threaded_dl_argparse(args):
|
||||||
|
|
||||||
@vlogging.main_decorator
|
@vlogging.main_decorator
|
||||||
def main(argv):
|
def main(argv):
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument(
|
||||||
parser.add_argument('url_file')
|
'url_file',
|
||||||
parser.add_argument('thread_count', type=int)
|
metavar='links',
|
||||||
parser.add_argument('filename_format', nargs='?', default='{now}_{index}_{basename}')
|
help='''
|
||||||
parser.add_argument('--bytespersecond', default=None)
|
The name of a file containing links to download, one per line.
|
||||||
parser.add_argument('--timeout', default=15)
|
Uses pipeable to support !c clipboard, !i stdin lines of urls.
|
||||||
parser.add_argument('--headers', nargs='+', default=None)
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'thread_count',
|
||||||
|
type=int,
|
||||||
|
help='''
|
||||||
|
Integer number of threads to use for downloading.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'filename_format',
|
||||||
|
nargs='?',
|
||||||
|
type=str,
|
||||||
|
default='{now}_{index}_{basename}',
|
||||||
|
help='''
|
||||||
|
A string that controls the names of the downloaded files. Uses Python's
|
||||||
|
brace-style formatting. Available formatters are:
|
||||||
|
- {basename}: The name of the file as indicated by the URL.
|
||||||
|
E.g. example.com/image.jpg -> image.jpg
|
||||||
|
- {extension}: The extension of the file as indicated by the URL, including
|
||||||
|
the dot. E.g. example.com/image.jpg -> .jpg
|
||||||
|
- {index}: The index of this URL within the sequence of all downloaded URLs.
|
||||||
|
Starts from 0.
|
||||||
|
- {now}: The unix timestamp at which this download job was started. It might
|
||||||
|
be ugly but at least it's unambiguous when doing multiple download batches
|
||||||
|
with similar filenames.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--bytespersecond',
|
||||||
|
default=None,
|
||||||
|
help='''
|
||||||
|
Limit the overall download speed to X bytes per second. Uses
|
||||||
|
bytestring.parsebytes to support strings like "1m", "500k", "2 mb", etc.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--timeout',
|
||||||
|
default=15,
|
||||||
|
help='''
|
||||||
|
Integer number of seconds to use as HTTP request timeout for each download.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--headers', nargs='+', default=None,
|
||||||
|
)
|
||||||
parser.set_defaults(func=threaded_dl_argparse)
|
parser.set_defaults(func=threaded_dl_argparse)
|
||||||
|
|
||||||
return betterhelp.single_main(argv, parser, __doc__)
|
return betterhelp.go(parser, argv)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
raise SystemExit(main(sys.argv[1:]))
|
raise SystemExit(main(sys.argv[1:]))
|
||||||
|
|
30
touch.py
30
touch.py
|
@ -1,10 +1,8 @@
|
||||||
'''
|
|
||||||
Create the file, or update the last modified timestamp.
|
|
||||||
'''
|
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from voussoirkit import betterhelp
|
||||||
from voussoirkit import pipeable
|
from voussoirkit import pipeable
|
||||||
from voussoirkit import winglob
|
from voussoirkit import winglob
|
||||||
|
|
||||||
|
@ -24,13 +22,31 @@ def touch_argparse(args):
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def main(argv):
|
def main(argv):
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
parser = argparse.ArgumentParser(
|
||||||
|
description='''
|
||||||
|
Create blank files, or update mtimes of existing files.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
|
||||||
parser.add_argument('patterns', nargs='+')
|
parser.add_argument(
|
||||||
|
'patterns',
|
||||||
|
type=str,
|
||||||
|
nargs='+',
|
||||||
|
help='''
|
||||||
|
One or more filenames or glob patterns.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--sleep',
|
||||||
|
type=float,
|
||||||
|
default=None,
|
||||||
|
help='''
|
||||||
|
Sleep for this many seconds between touching each file.
|
||||||
|
''',
|
||||||
|
)
|
||||||
parser.set_defaults(func=touch_argparse)
|
parser.set_defaults(func=touch_argparse)
|
||||||
|
|
||||||
args = parser.parse_args(argv)
|
return betterhelp.go(parser, argv)
|
||||||
return args.func(args)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
raise SystemExit(main(sys.argv[1:]))
|
raise SystemExit(main(sys.argv[1:]))
|
||||||
|
|
|
@ -1,16 +1,3 @@
|
||||||
'''
|
|
||||||
wait_for_internet
|
|
||||||
=================
|
|
||||||
|
|
||||||
This program will block until internet access is available. It can be useful to
|
|
||||||
run this program before running another program that expects an internet
|
|
||||||
connection.
|
|
||||||
|
|
||||||
> wait_for_internet timeout
|
|
||||||
|
|
||||||
timeout:
|
|
||||||
An integer number of seconds, after which to give up and return 1.
|
|
||||||
'''
|
|
||||||
import argparse
|
import argparse
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
@ -27,12 +14,23 @@ def wait_for_internet_argparse(args):
|
||||||
|
|
||||||
@vlogging.main_decorator
|
@vlogging.main_decorator
|
||||||
def main(argv):
|
def main(argv):
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
parser = argparse.ArgumentParser(
|
||||||
|
description='''
|
||||||
parser.add_argument('timeout', type=int)
|
This program will block until internet access is available. It can be useful to
|
||||||
|
run this program before running another program that expects an internet
|
||||||
|
connection.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'timeout',
|
||||||
|
type=int,
|
||||||
|
help='''
|
||||||
|
An integer number of seconds, after which to give up and return 1.
|
||||||
|
''',
|
||||||
|
)
|
||||||
parser.set_defaults(func=wait_for_internet_argparse)
|
parser.set_defaults(func=wait_for_internet_argparse)
|
||||||
|
|
||||||
return betterhelp.single_main(argv, parser, __doc__)
|
return betterhelp.go(parser, argv)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
raise SystemExit(main(sys.argv[1:]))
|
raise SystemExit(main(sys.argv[1:]))
|
||||||
|
|
|
@ -1,21 +1,3 @@
|
||||||
'''
|
|
||||||
watchforlinks
|
|
||||||
=============
|
|
||||||
|
|
||||||
This program will continuously watch your clipboard for URLs and save them to
|
|
||||||
individual files. The files will have randomly generated names. The current
|
|
||||||
contents of your clipboard will be erased.
|
|
||||||
|
|
||||||
> watchforlinks [extension] <flags>
|
|
||||||
|
|
||||||
extension:
|
|
||||||
The saved files will have this extension.
|
|
||||||
If not provided, the default is "generic".
|
|
||||||
|
|
||||||
flags:
|
|
||||||
--regex X:
|
|
||||||
A regex pattern. Only URLs that match this pattern will be saved.
|
|
||||||
'''
|
|
||||||
import argparse
|
import argparse
|
||||||
import pyperclip
|
import pyperclip
|
||||||
import re
|
import re
|
||||||
|
@ -64,13 +46,35 @@ def watchforlinks_argparse(args):
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def main(argv):
|
def main(argv):
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
parser = argparse.ArgumentParser(
|
||||||
|
description='''
|
||||||
|
This program will continuously watch your clipboard for http:// and
|
||||||
|
https:// URLs and save them to individual files. The files will have
|
||||||
|
randomly generated names. The current contents of your clipboard will
|
||||||
|
be erased.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
|
||||||
parser.add_argument('extension', nargs='?', default='generic')
|
parser.add_argument(
|
||||||
parser.add_argument('--regex', default=None)
|
'extension',
|
||||||
|
nargs='?',
|
||||||
|
type=str,
|
||||||
|
default='generic',
|
||||||
|
help='''
|
||||||
|
The saved files will have this extension.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--regex',
|
||||||
|
type=str,
|
||||||
|
default=None,
|
||||||
|
help='''
|
||||||
|
A regex pattern. Only URLs that match this pattern will be saved.
|
||||||
|
''',
|
||||||
|
)
|
||||||
parser.set_defaults(func=watchforlinks_argparse)
|
parser.set_defaults(func=watchforlinks_argparse)
|
||||||
|
|
||||||
return betterhelp.single_main(argv, parser, __doc__)
|
return betterhelp.go(parser, argv)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
raise SystemExit(main(sys.argv[1:]))
|
raise SystemExit(main(sys.argv[1:]))
|
||||||
|
|
Loading…
Reference in a new issue