Compare commits

...
2 Commits
Author SHA1 Message Date
luxick a8b1d41398 Update nn
Exclude hidden files per default. Use `-a` to include them
2026-08-24 10:54:58 +02:00
luxick 2dc08013b7 Update nn
Direct input into Y/N questions
2026-08-24 10:52:48 +02:00
+51 -10
View File
@@ -6,6 +6,8 @@ import re
import shutil
import subprocess
import sys
import termios
import tty
from datetime import datetime
from pathlib import Path
@@ -24,6 +26,9 @@ TELEGRAM_PATTERN = re.compile(
NORMALIZED = re.compile(r'^\d{4}-\d{2}-\d{2} .+')
# Windows hidden-file attribute, mirrored from the stat module.
FILE_ATTRIBUTE_HIDDEN = 0x2
STATUS_RENAME = 'rename'
STATUS_ALREADY = 'skip (already normalized)'
STATUS_NO_MATCH = 'skip (no rule matched)'
@@ -146,7 +151,19 @@ def get_date_for_file(filepath, parse_mode):
return None
def collect_files(paths):
def is_hidden(filepath):
"""Check whether a file is hidden (dotfile, or hidden attribute on Windows)."""
if filepath.name.startswith('.'):
return True
try:
attrs = filepath.stat().st_file_attributes
except (AttributeError, OSError):
return False
return bool(attrs & FILE_ATTRIBUTE_HIDDEN)
def collect_files(paths, include_hidden=False):
"""Resolve the list of files to process."""
if not paths:
paths = ['.']
@@ -155,8 +172,12 @@ def collect_files(paths):
for path in paths:
candidate = Path(path)
if candidate.is_dir():
files.extend(sorted(file for file in candidate.iterdir() if file.is_file()))
files.extend(sorted(
file for file in candidate.iterdir()
if file.is_file() and (include_hidden or not is_hidden(file))
))
elif candidate.is_file():
# Explicitly named files are always processed.
files.append(candidate)
return files
@@ -273,13 +294,23 @@ def apply_renames(plan):
def confirm(prompt='Apply renames? [y/n] '):
"""Ask the user for confirmation."""
while True:
answer = input(prompt).strip().lower()
if answer == 'y':
return True
if answer == 'n':
return False
"""Ask the user for confirmation, reacting to a single keypress."""
print(prompt, end='', flush=True)
if not sys.stdin.isatty():
return sys.stdin.read(1).strip().lower() == 'y'
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try:
# cbreak (not raw) keeps Ctrl-C as SIGINT and newlines well behaved.
tty.setcbreak(fd)
while (key := sys.stdin.read(1).lower()) not in ('y', 'n', ''):
pass
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
print(key)
return key == 'y'
def main():
@@ -306,6 +337,12 @@ def main():
action='store_true',
help='Force numbering even for already-normalized files (used with -n).',
)
parser.add_argument(
'-a',
'--all',
action='store_true',
help='Include hidden files when scanning directories.',
)
parser.add_argument(
'files',
nargs='*',
@@ -314,7 +351,7 @@ def main():
args = parser.parse_args()
files = collect_files(args.files)
files = collect_files(args.files, args.all)
if not files:
print('No files found.')
sys.exit(0)
@@ -337,4 +374,8 @@ def main():
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
print('\nAborted.')
sys.exit(1)