Windows compatibility
This commit is contained in:
+109
@@ -0,0 +1,109 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
|
||||
from pypdf import PdfReader, PdfWriter
|
||||
|
||||
|
||||
def copy_pdfs(directory: Path) -> list[Path]:
|
||||
directory = directory.expanduser().resolve()
|
||||
if not directory.is_dir():
|
||||
raise NotADirectoryError(f"Dossier introuvable : {directory}")
|
||||
return sorted(
|
||||
(
|
||||
path
|
||||
for path in directory.glob("*.pdf")
|
||||
if path.name.casefold() not in {"enonce.pdf", "énoncé.pdf"}
|
||||
),
|
||||
key=lambda path: path.name.casefold(),
|
||||
)
|
||||
|
||||
|
||||
def rotate_pdf(path: Path) -> None:
|
||||
temporary = path.with_name(f".{path.stem}.rotate-{uuid.uuid4().hex}.pdf")
|
||||
try:
|
||||
with path.open("rb") as source, temporary.open("wb") as destination:
|
||||
reader = PdfReader(source)
|
||||
writer = PdfWriter()
|
||||
for page in reader.pages:
|
||||
writer.add_page(page.rotate(180))
|
||||
if reader.metadata:
|
||||
metadata = {
|
||||
str(key): str(value)
|
||||
for key, value in reader.metadata.items()
|
||||
if value is not None
|
||||
}
|
||||
writer.add_metadata(metadata)
|
||||
writer.write(destination)
|
||||
temporary.replace(path)
|
||||
finally:
|
||||
if temporary.exists():
|
||||
temporary.unlink()
|
||||
|
||||
|
||||
def rotate_all(directory: Path) -> int:
|
||||
files = copy_pdfs(directory)
|
||||
for path in files:
|
||||
rotate_pdf(path)
|
||||
print(f"Rotated: {path.name}")
|
||||
if not files:
|
||||
print("No PDF copies found.")
|
||||
return len(files)
|
||||
|
||||
|
||||
def rename_all(directory: Path) -> list[tuple[Path, Path]]:
|
||||
directory = directory.expanduser().resolve()
|
||||
files = copy_pdfs(directory)
|
||||
width = max(2, len(str(len(files))))
|
||||
plan = [
|
||||
(source, directory / f"Copie{index:0{width}d}.pdf")
|
||||
for index, source in enumerate(files, start=1)
|
||||
]
|
||||
staged: list[tuple[Path, Path, Path]] = []
|
||||
|
||||
try:
|
||||
for source, destination in plan:
|
||||
temporary = directory / f".copienator-rename-{uuid.uuid4().hex}.pdf"
|
||||
source.replace(temporary)
|
||||
staged.append((source, temporary, destination))
|
||||
|
||||
completed: list[tuple[Path, Path, Path]] = []
|
||||
try:
|
||||
for source, temporary, destination in staged:
|
||||
temporary.replace(destination)
|
||||
completed.append((source, temporary, destination))
|
||||
print(f"Renamed: {source.name} -> {destination.name}")
|
||||
except OSError:
|
||||
for _source, temporary, destination in completed:
|
||||
if destination.exists():
|
||||
destination.replace(temporary)
|
||||
raise
|
||||
except OSError:
|
||||
for source, temporary, _destination in staged:
|
||||
if temporary.exists():
|
||||
temporary.replace(source)
|
||||
raise
|
||||
|
||||
if not plan:
|
||||
print("No PDF copies found.")
|
||||
return [(source, destination) for source, _temporary, destination in staged]
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description="Prepare scanned PDF copies.")
|
||||
subparsers = parser.add_subparsers(dest="operation", required=True)
|
||||
for operation in ("rotate", "rename"):
|
||||
subparser = subparsers.add_parser(operation)
|
||||
subparser.add_argument("directory", type=Path)
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.operation == "rotate":
|
||||
rotate_all(args.directory)
|
||||
else:
|
||||
rename_all(args.directory)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user