feat(multi-rename): add safer move and trash flow
This commit is contained in:
Regular → Executable
+20
-11
@@ -1,4 +1,6 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
import argparse
|
import argparse
|
||||||
|
from functools import cache
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
@@ -34,20 +36,22 @@ def edit_text(text: str) -> str | None:
|
|||||||
return temp_file_path.read_text()
|
return temp_file_path.read_text()
|
||||||
|
|
||||||
|
|
||||||
def move_to_trash(file_path: Path):
|
def move_to_trash(file_path: Path) -> None:
|
||||||
|
"""Move file to Trash using trash CLI."""
|
||||||
if not file_path.is_file():
|
if not file_path.is_file():
|
||||||
raise FileNotFoundError
|
raise FileNotFoundError(f"File not found: {file_path}")
|
||||||
|
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
[
|
["trash", str(file_path.resolve())],
|
||||||
"osascript",
|
|
||||||
"-e",
|
|
||||||
f'tell app "Finder" to move POSIX file "{file_path}" to trash',
|
|
||||||
],
|
|
||||||
check=True,
|
check=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@cache
|
||||||
|
def make_dir(path: Path) -> None:
|
||||||
|
path.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
def rename_files(files: list[Path], cwd: Path | None = None):
|
def rename_files(files: list[Path], cwd: Path | None = None):
|
||||||
if not cwd:
|
if not cwd:
|
||||||
cwd = files[0].parent
|
cwd = files[0].parent
|
||||||
@@ -58,25 +62,30 @@ def rename_files(files: list[Path], cwd: Path | None = None):
|
|||||||
return
|
return
|
||||||
edited = edited_raw.splitlines(keepends=False)
|
edited = edited_raw.splitlines(keepends=False)
|
||||||
|
|
||||||
|
if len(edited) != len(originals):
|
||||||
|
logging.error("number of edited lines does not match number of original lines")
|
||||||
|
return
|
||||||
|
|
||||||
deletables = []
|
deletables = []
|
||||||
moveables = []
|
moveables: list[tuple[Path, Path]] = []
|
||||||
for source, target in zip(originals, edited):
|
for source, target in zip(originals, edited):
|
||||||
if target.startswith("#") or target == "":
|
if target.startswith("#") or target == "":
|
||||||
deletables.append(Path(source))
|
deletables.append((cwd / source).resolve())
|
||||||
continue
|
continue
|
||||||
if source != target:
|
if source != target:
|
||||||
moveables.append((cwd / source, cwd / target))
|
moveables.append((cwd / source, cwd / target))
|
||||||
|
|
||||||
for source, target in moveables:
|
for source, target in moveables:
|
||||||
logging.info(f"moving {source} -> {target}")
|
logging.info(f"moving {source} -> {target}")
|
||||||
|
make_dir(target.parent)
|
||||||
source.rename(target)
|
source.rename(target)
|
||||||
|
|
||||||
for f in deletables:
|
for f in deletables:
|
||||||
try:
|
try:
|
||||||
logging.info(f"trashing {f}")
|
logging.info(f"trashing {f}")
|
||||||
move_to_trash(f)
|
move_to_trash(f)
|
||||||
except:
|
except Exception as e:
|
||||||
logging.error(f"failed to trash {f}")
|
logging.error(f"failed to trash {f}: {e}")
|
||||||
|
|
||||||
|
|
||||||
def parse_args():
|
def parse_args():
|
||||||
|
|||||||
Reference in New Issue
Block a user