Compare commits

..

12 commits

10 changed files with 182 additions and 41 deletions

View file

@ -148,7 +148,8 @@ def main(argv):
pgui = PGUILauncher(root) pgui = PGUILauncher(root)
pgui.pack(fill=tkinter.BOTH, expand=True) pgui.pack(fill=tkinter.BOTH, expand=True)
pgui.filter_entry.focus() pgui.filter_entry.focus_set()
pgui.filter_entry.focus_force()
width = root.winfo_reqwidth() width = root.winfo_reqwidth()
height = root.winfo_reqheight() height = root.winfo_reqheight()
@ -157,6 +158,8 @@ def main(argv):
root.geometry('%dx%d+%d+%d' % (width, height, x_offset, y_offset-50)) root.geometry('%dx%d+%d+%d' % (width, height, x_offset, y_offset-50))
root.deiconify() root.deiconify()
root.focus()
root.after(100, lambda: pgui.filter_entry.focus_force())
root.mainloop() root.mainloop()
if __name__ == '__main__': if __name__ == '__main__':

View file

@ -12,7 +12,7 @@ log = vlogging.getLogger(__name__, 'adbinstall')
def adbinstall_argparse(args): def adbinstall_argparse(args):
patterns = pipeable.input_many(args.apks, skip_blank=True, strip=True) patterns = pipeable.input_many(args.apks, skip_blank=True, strip=True)
apks = pathclass.glob_many_files(patterns) apks = pathclass.glob_many(patterns)
installs = [] installs = []
for apk in apks: for apk in apks:
apk = pathclass.Path(apk) apk = pathclass.Path(apk)

View file

@ -18,7 +18,7 @@ log = vlogging.getLogger(__name__, 'getcrx')
FILENAME_BADCHARS = '\\/:*?<>|"' FILENAME_BADCHARS = '\\/:*?<>|"'
WEBSTORE_URL = 'https://chrome.google.com/webstore/detail/{extension_id}' WEBSTORE_URL = 'https://chrome.google.com/webstore/detail/{extension_id}'
CRX_URL = 'https://clients2.google.com/service/update2/crx?response=redirect&prodversion=121.0.6167.161&acceptformat=crx2,crx3&x=id%3D{extension_id}%26uc' CRX_URL = 'https://clients2.google.com/service/update2/crx?response=redirect&prodversion=131.0.6167.161&acceptformat=crx2,crx3&x=id%3D{extension_id}%26uc'
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36' USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36'
session = requests.Session() session = requests.Session()

View file

@ -3,17 +3,16 @@ import psutil
import sys import sys
from voussoirkit import betterhelp from voussoirkit import betterhelp
from voussoirkit import subproctools
from voussoirkit import vlogging from voussoirkit import vlogging
log = vlogging.getLogger(__name__, 'getpid') log = vlogging.getLogger(__name__, 'getpid')
def getpid_argparse(args): def getpid_argparse(args):
status = 1 pids = subproctools.getpid(args.process_name)
target = args.process_name.lower() status = 0 if len(pids) > 0 else 1
for process in psutil.process_iter(): for pid in pids:
if process.name().lower() == target: print(pid)
print(process.pid)
status = 0
return status return status
@vlogging.main_decorator @vlogging.main_decorator

View file

@ -360,7 +360,7 @@ def main(argv):
nargs='+', nargs='+',
type=str, type=str,
help=''' help='''
Run `git <command>` in each directory. You can use \- to escape - in your Run `git <command>` in each directory. You can use \\- to escape - in your
git arguments, since they would confuse this program's argparse. git arguments, since they would confuse this program's argparse.
If this is used, any --fetch, --pull, --push is ignored. If this is used, any --fetch, --pull, --push is ignored.
''', ''',

View file

@ -1,8 +1,10 @@
import argparse
import socket import socket
import sqlite3 import sqlite3
import sys import sys
import time import time
from voussoirkit import betterhelp
from voussoirkit import hms from voussoirkit import hms
from voussoirkit import threadpool from voussoirkit import threadpool
from voussoirkit import vlogging from voussoirkit import vlogging
@ -35,12 +37,11 @@ def percentage(items):
trues = sum(bool(i) for i in items) trues = sum(bool(i) for i in items)
return trues / len(items) return trues / len(items)
def ping_lan(): def ping_lan(gateway_ip):
ip = '192.168.1.1' log.debug('Checking LAN at %s.', gateway_ip)
log.debug('Checking LAN at %s.', ip)
try: try:
socket.setdefaulttimeout(1) socket.setdefaulttimeout(1)
socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect((ip, 80)) socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect((gateway_ip, 80))
return True return True
except socket.error: except socket.error:
return False return False
@ -76,21 +77,12 @@ def check_ip():
log.debug('Checking IP %s.', ip) log.debug('Checking IP %s.', ip)
try: try:
socket.setdefaulttimeout(2) socket.setdefaulttimeout(2)
socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect(('104.43.253.214', 80)) socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect((ip, 80))
return True return True
except socket.error: except socket.error:
return False return False
ips = [ ips = [
'104.43.253.214', '198.100.155.125', # voussoir.net
'13.107.21.200',
'13.77.161.179',
'142.136.81.136',
'151.101.1.140',
'172.217.11.174',
'172.217.11.78',
'176.32.103.205',
'35.165.194.49',
'69.252.80.75',
] ]
thread_pool.pause() thread_pool.pause()
jobs = [{'function': check, 'args': [ip]} for ip in ips] jobs = [{'function': check, 'args': [ip]} for ip in ips]
@ -127,10 +119,10 @@ def end_down():
down = False down = False
outage_started = None outage_started = None
def check_forever(): def check_forever(lan_gateway_ip):
while True: while True:
now = time.strftime('%Y-%m-%d %H:%M:%S') now = time.strftime('%Y-%m-%d %H:%M:%S')
lan_ok = int(ping_lan()) lan_ok = int(ping_lan(lan_gateway_ip))
dns_stat = check_dns() dns_stat = check_dns()
ip_stat = check_ip() ip_stat = check_ip()
message = f'{now}, LAN={lan_ok}, DNS={dns_stat:0.2f}, IP={ip_stat:0.2f}' message = f'{now}, LAN={lan_ok}, DNS={dns_stat:0.2f}, IP={ip_stat:0.2f}'
@ -148,13 +140,31 @@ def check_forever():
else: else:
time.sleep(20) time.sleep(20)
@vlogging.main_decorator def internetcheck_argparse(args):
def main(argv):
try: try:
check_forever() check_forever(lan_gateway_ip=args.lan_gateway_ip)
except KeyboardInterrupt: except KeyboardInterrupt:
pass pass
return 0 return 0
@vlogging.main_decorator
def main(argv):
parser = argparse.ArgumentParser(
description='''
This program will periodically ping both LAN and external IP addresses
to help you troubleshoot the time and duration of network outages.
''',
)
parser.add_argument(
'--lan_gateway_ip',
'--lan-gateway-ip',
default='192.168.1.1',
help='''
''',
)
parser.set_defaults(func=internetcheck_argparse)
return betterhelp.go(parser, argv)
if __name__ == '__main__': if __name__ == '__main__':
raise SystemExit(main(sys.argv[1:])) raise SystemExit(main(sys.argv[1:]))

View file

@ -4,6 +4,7 @@ import os
import re import re
import sys import sys
from voussoirkit import betterhelp
from voussoirkit import imagetools from voussoirkit import imagetools
from voussoirkit import interactive from voussoirkit import interactive
from voussoirkit import pathclass from voussoirkit import pathclass
@ -13,13 +14,14 @@ from voussoirkit import vlogging
log = vlogging.getLogger(__name__) log = vlogging.getLogger(__name__)
def makename(file, read_exif=False): def makename(file, read_exif=False, read_mtime=False, minus_duration=False):
old = file.replace_extension('').basename old = file.replace_extension('').basename
new = old new = old
final_pattern = r'^(\d\d\d\d)-(\d\d)-(\d\d)_(\d\d)-(\d\d)-(\d\d)(?:x\d+)?$'
# Already optimized filenames need not apply # Already optimized filenames need not apply
# This is also important when the filename and the exif disagree # This is also important when the filename and the exif disagree
if re.match(r'^(\d\d\d\d)-(\d\d)-(\d\d)_(\d\d)-(\d\d)-(\d\d)(?:x\d+)?$', old) and not read_exif: if re.match(final_pattern, old) and not read_exif:
return file return file
# Microsoft ICE # Microsoft ICE
@ -106,6 +108,13 @@ def makename(file, read_exif=False):
new, new,
) )
# Sony videos
new = re.sub(
r'^VideoPro_(\d\d\d\d)(\d\d)(\d\d)_(\d\d)(\d\d)(\d\d)+$',
r'\1-\2-\3_\4-\5-\6',
new,
)
new = re.sub( new = re.sub(
r'^(\d\d\d\d)-(\d\d)-(\d\d)_(\d\d)-(\d\d)-(\d\d)[_-](\d+)$', r'^(\d\d\d\d)-(\d\d)-(\d\d)_(\d\d)-(\d\d)-(\d\d)[_-](\d+)$',
r'\1-\2-\3_\4-\5-\6x\7', r'\1-\2-\3_\4-\5-\6x\7',
@ -147,6 +156,16 @@ def makename(file, read_exif=False):
if new == old and read_exif and file.extension in {'jpg', 'jpeg'}: if new == old and read_exif and file.extension in {'jpg', 'jpeg'}:
new = makename_exif(file, old) new = makename_exif(file, old)
if new == old and re.match(final_pattern, new):
return file
if new == old and read_mtime:
mtime = file.stat.st_mtime
if minus_duration:
mtime -= get_file_duration(file)
date = datetime.datetime.fromtimestamp(mtime)
new = date.strftime('%Y-%m-%d_%H-%M-%S')
new = file.parent.with_child(new).add_extension(file.extension) new = file.parent.with_child(new).add_extension(file.extension)
return new return new
@ -156,11 +175,23 @@ def makename_exif(file, fallback):
return fallback return fallback
return dt.strftime('%Y-%m-%d_%H-%M-%S') return dt.strftime('%Y-%m-%d_%H-%M-%S')
def makenames(files, read_exif=False): def get_file_duration(file):
import kkroening_ffmpeg
probe = kkroening_ffmpeg.probe(file.absolute_path)
duration = float(probe['format']['duration'])
return duration
def makename_ffmpeg(file, fallback):
import kkroening_ffmpeg
probe = kkroening_ffmpeg.probe(file.absolute_path)
duration = float(probe['format']['duration'])
zulu = probe['streams'][0]['tags']['creation_time']
def makenames(files, read_exif=False, read_mtime=False, minus_duration=False):
pairs = {} pairs = {}
new_duplicates = {} new_duplicates = {}
for file in files: for file in files:
newname = makename(file, read_exif=read_exif) newname = makename(file, read_exif=read_exif, read_mtime=read_mtime, minus_duration=minus_duration)
new_duplicates.setdefault(newname, []).append(file) new_duplicates.setdefault(newname, []).append(file)
if file.basename == newname.basename: if file.basename == newname.basename:
continue continue
@ -193,7 +224,7 @@ def photo_rename_argparse(args):
else: else:
files = pathclass.glob_many_files(patterns) files = pathclass.glob_many_files(patterns)
pairs = makenames(files, read_exif=args.read_exif) pairs = makenames(files, read_exif=args.read_exif, read_mtime=args.read_mtime, minus_duration=args.minus_duration)
if not pairs: if not pairs:
return 0 return 0
@ -212,12 +243,37 @@ def main(argv):
parser.add_argument('patterns', nargs='+') parser.add_argument('patterns', nargs='+')
parser.add_argument('--recurse', action='store_true') parser.add_argument('--recurse', action='store_true')
parser.add_argument('--exif', dest='read_exif', action='store_true') parser.add_argument(
'--exif',
dest='read_exif',
action='store_true',
help='''
Program will look for EXIF metadata and use ImageDateTime, if available.
''',
)
parser.add_argument(
'--mtime',
dest='read_mtime',
action='store_true',
help='''
Program will use the file's mtime as a last resort.
''',
)
parser.add_argument(
'--minus_duration',
'--minus-duration',
dest='minus_duration',
action='store_true',
help='''
When used with --mtime, the file's duration will be subtracted.
Use for cameras that set the mtime to the end of the video and you want
the name to be the start.
''',
)
parser.add_argument('--yes', dest='autoyes', action='store_true') parser.add_argument('--yes', dest='autoyes', action='store_true')
parser.set_defaults(func=photo_rename_argparse) parser.set_defaults(func=photo_rename_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:]))

View file

@ -67,7 +67,7 @@ body.noscrollbar::-webkit-scrollbar
} }
body.noscrollbar body.noscrollbar
{ {
scrollbar-width: none; scrollbar-width: 0;
} }
header header
@ -286,6 +286,10 @@ pre,
<p>Click each photo to view its full resolution. Click the number to download it.</p> <p>Click each photo to view its full resolution. Click the number to download it.</p>
{% for zip in zipz %}
<p>Click here to download <a target="_blank" href="{{urlroot}}{{zip.relative_to('.', simple=True)}}">{{zip.basename}}</a></p>
{% endfor %}
{% for file in files %} {% for file in files %}
{% if file.extension == 'jpg' %} {% if file.extension == 'jpg' %}
<article class="photograph"> <article class="photograph">
@ -296,7 +300,7 @@ pre,
</article> </article>
{% elif file.extension in ['mp4', 'mov'] %} {% elif file.extension in ['mp4', 'mov'] %}
<article class="photograph"> <article class="photograph">
<p>{{file.replace_extension('').basename}}</p> <p>{{file.replace_extension('').basename}} (<a download="{{file.basename}}" href="{{urlroot}}{{file.relative_to('.', simple=True)}}">#{{loop.index}}/{{files|length}}</a>)</p>
<video controls preload="none" src="{{urlroot}}{{file.relative_to('.', simple=True)}}" poster="{{urlroot}}thumbs/small_{{file.replace_extension('jpg').relative_to('.', simple=True)}}"></video> <video controls preload="none" src="{{urlroot}}{{file.relative_to('.', simple=True)}}" poster="{{urlroot}}thumbs/small_{{file.replace_extension('jpg').relative_to('.', simple=True)}}"></video>
</article> </article>
{% endif %} {% endif %}
@ -438,7 +442,10 @@ document.addEventListener("DOMContentLoaded", on_pageload);
''') ''')
def imagegallery(files, title, urlroot, with_download_links): def imagegallery(files, title, urlroot, with_download_links):
zipz = [f for f in files if f.extension == 'zip']
files = [f for f in files if f.extension != 'zip']
html = TEMPLATE.render( html = TEMPLATE.render(
zipz=zipz,
files=files, files=files,
title=title, title=title,
urlroot=urlroot, urlroot=urlroot,

66
sdingest.py Normal file
View file

@ -0,0 +1,66 @@
import shutil
import argparse
import sys
from voussoirkit import betterhelp
from voussoirkit import pathclass
from voussoirkit import spinal
from voussoirkit import vlogging
from voussoirkit import windrives
from voussoirkit import progressbars
log = vlogging.getLogger(__name__, 'sdingest')
def sdingest_one(folder):
cwd = pathclass.cwd()
for file in folder.walk_files():
# print(file.absolute_path)
spinal.copy_file(
file,
cwd.with_child(file.basename),
progressbar=progressbars.Bar1_bytestring,
)
def sdingest_all():
dcim = None
drives = windrives.get_drive_map()
for (mount, info) in drives.items():
mount = pathclass.Path(mount)
# Panasonic HC-X1500/HC-X2000
panasonic = mount.with_child('PRIVATE').with_child('PANA_GRP').with_child('001YAQAM')
if panasonic.is_folder:
sdingest_one(panasonic)
continue
# Most cameras
dcim = mount.with_child('DCIM')
if dcim.is_folder:
sdingest_one(dcim)
continue
# Sony ICD UX570
if info.get('name').upper() == 'MEMORY CARD':
folder = mount.join('PRIVATE\\SONY\\REC_FILE')
if folder.exists:
sdingest_one(folder)
continue
if dcim is None:
return 1
def sdingest_argparse(args):
return sdingest_all()
@vlogging.main_decorator
def main(argv):
parser = argparse.ArgumentParser(
description='''
''',
)
parser.set_defaults(func=sdingest_argparse)
return betterhelp.go(parser, argv)
if __name__ == '__main__':
raise SystemExit(main(sys.argv[1:]))

View file

@ -16,7 +16,7 @@ alias move=mv
alias copy=cp alias copy=cp
alias md=mkdir alias md=mkdir
alias cls=clear alias cls=clear
alias gfa=git fetch --all alias gfa='git fetch --all'
# This generates a warning in some non-interactive situations, like cron. # This generates a warning in some non-interactive situations, like cron.
bind TAB:menu-complete > /dev/null 2>&1 bind TAB:menu-complete > /dev/null 2>&1