platform agnosticity

This commit is contained in:
2026-07-19 11:45:08 +02:00
parent 4fe9e76cad
commit d375db47c3
4 changed files with 85 additions and 40 deletions

View File

@@ -1,7 +1,6 @@
input: input:
paths: paths:
- "~/Pictures" - "~/Pictures"
- "/mnt/storage3/aman/screenshots"
patterns: patterns:
- "SCR-*.png" - "SCR-*.png"
- "*.jpg" - "*.jpg"
@@ -11,8 +10,8 @@ input:
recursive: true recursive: true
ocr: ocr:
engine: "paddleocr" # "paddleocr" | "tesseract" | "auto" engine: "paddleocr"
languages: ["en"] # For tesseract: eng, lat, deu, fra, spa, ita, por, chi_sim, jpn, kor languages: ["en", "latin"]
use_gpu: false use_gpu: false
use_angle_cls: true use_angle_cls: true
det_db_thresh: 0.3 det_db_thresh: 0.3
@@ -20,13 +19,6 @@ ocr:
det_db_unclip_ratio: 1.5 det_db_unclip_ratio: 1.5
rec_batch_num: 6 rec_batch_num: 6
cpu_threads: 4 cpu_threads: 4
preprocess:
deskew: true
denoise: true
clahe: true
adaptive_threshold: true
remove_lines: true
max_dimension: 4096
processing: processing:
workers: 4 workers: 4
@@ -76,10 +68,11 @@ chunking:
keep_separator: true keep_separator: true
output: output:
base_directory: "./data/ocr_output" # Uses platformdirs default: ~/.local/share/ocr-pipeline (Linux), ~/Library/Application Support/ocr-pipeline (macOS), %LOCALAPPDATA%\ocr-pipeline (Windows)
format: "markdown" format: "markdown"
organize_by: "date_run" # "date_run" | "source_dir" | "flat" organize_by: "date_run"
frontmatter: frontmatter:
fields:
- "source_path" - "source_path"
- "source_hash" - "source_hash"
- "timestamp" - "timestamp"
@@ -92,14 +85,15 @@ output:
- "citations_found" - "citations_found"
- "chunk_index" - "chunk_index"
- "total_chunks" - "total_chunks"
include_raw_text: false
watch: watch:
enabled: true enabled: true
debounce_seconds: 5 debounce_seconds: 5
ignore_patterns: # Uses platform-aware defaults from config.py
- ".DS_Store" # ignore_patterns:
- "*.tmp" # - ".DS_Store"
- "*.partial" # - "*.tmp"
- "*.crdownload" # - "*.partial"
db_path: "./data/processed_files.db" # - "*.crdownload"
poll_interval: 1.0 poll_interval: 1.0

View File

@@ -44,6 +44,7 @@ dependencies = [
"python-slugify>=8.0.0", "python-slugify>=8.0.0",
"xxhash>=3.4.0", "xxhash>=3.4.0",
"python-magic>=0.4.27", "python-magic>=0.4.27",
"platformdirs>=4.2.0",
] ]
[project.optional-dependencies] [project.optional-dependencies]
@@ -75,6 +76,7 @@ dev-dependencies = [
"mypy>=1.8.0", "mypy>=1.8.0",
"pre-commit>=3.6.0", "pre-commit>=3.6.0",
] ]
override-dependencies = ["lxml>=5.3.0"]
[tool.ruff] [tool.ruff]
line-length = 100 line-length = 100

View File

@@ -1,14 +1,42 @@
from __future__ import annotations from __future__ import annotations
import os import os
import platform
from pathlib import Path from pathlib import Path
from typing import Any, ClassVar from typing import Any, ClassVar
import platformdirs
import yaml import yaml
from pydantic import Field, field_validator from pydantic import Field, field_validator
from pydantic_settings import BaseSettings, SettingsConfigDict from pydantic_settings import BaseSettings, SettingsConfigDict
APP_NAME = "ocr-pipeline"
APP_AUTHOR = "aman"
def _get_platform_ignore_patterns() -> list[str]:
"""Get platform-specific ignore patterns for file watching."""
patterns = [
"*.tmp",
"*.partial",
"*.crdownload",
"*.part",
"~$*", # MS Office temp files
"*.swp",
"*.swo", # Vim swap files
]
system = platform.system().lower()
if system == "darwin":
patterns.extend([".DS_Store", "._*", ".Spotlight-V100", ".Trashes", ".fseventsd"])
elif system == "windows":
patterns.extend(["Thumbs.db", "desktop.ini", "*.lnk", "$RECYCLE.BIN"])
else:
# Linux/Unix
patterns.extend([".directory", "*.swp", "*.swo", ".git"])
return patterns
class InputConfig(BaseSettings): class InputConfig(BaseSettings):
paths: list[str] = Field(default_factory=lambda: ["~/Pictures"]) paths: list[str] = Field(default_factory=lambda: ["~/Pictures"])
patterns: list[str] = Field( patterns: list[str] = Field(
@@ -132,7 +160,9 @@ class FrontmatterConfig(BaseSettings):
class OutputConfig(BaseSettings): class OutputConfig(BaseSettings):
base_directory: str = "./data/ocr_output" base_directory: str = Field(
default_factory=lambda: str(platformdirs.user_data_dir(APP_NAME, APP_AUTHOR))
)
format: str = "markdown" format: str = "markdown"
organize_by: str = "date_run" # date_run | source_dir | flat organize_by: str = "date_run" # date_run | source_dir | flat
frontmatter: FrontmatterConfig = Field(default_factory=FrontmatterConfig) frontmatter: FrontmatterConfig = Field(default_factory=FrontmatterConfig)
@@ -141,15 +171,10 @@ class OutputConfig(BaseSettings):
class WatchConfig(BaseSettings): class WatchConfig(BaseSettings):
enabled: bool = True enabled: bool = True
debounce_seconds: int = 5 debounce_seconds: int = 5
ignore_patterns: list[str] = Field( ignore_patterns: list[str] = Field(default_factory=_get_platform_ignore_patterns)
default_factory=lambda: [ db_path: str = Field(
".DS_Store", default_factory=lambda: str(Path(platformdirs.user_data_dir(APP_NAME, APP_AUTHOR)) / "processed_files.db")
"*.tmp",
"*.partial",
"*.crdownload",
]
) )
db_path: str = "./data/processed_files.db"
poll_interval: float = 1.0 poll_interval: float = 1.0

View File

@@ -1,16 +1,40 @@
from __future__ import annotations from __future__ import annotations
import hashlib import hashlib
import os
import platform
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
import nbformat import nbformat
import platformdirs
from ocr_pipeline.config import settings
from ocr_pipeline.utils.logging import get_logger from ocr_pipeline.utils.logging import get_logger
logger = get_logger(__name__) logger = get_logger(__name__)
def _get_pictures_dir() -> Path:
"""Get platform-appropriate Pictures directory."""
system = platform.system().lower()
if system == "darwin":
return Path.home() / "Pictures"
elif system == "windows":
# Windows: Pictures folder in user profile
return Path.home() / "Pictures"
else:
# Linux/Unix: XDG Pictures dir or ~/Pictures
pictures = Path.home() / "Pictures"
if pictures.exists():
return pictures
# Fallback to XDG_PICTURES_DIR
xdg = os.environ.get("XDG_PICTURES_DIR")
if xdg:
return Path(xdg)
return pictures
def parse_notebook_for_images(notebook_path: Path) -> list[Path]: def parse_notebook_for_images(notebook_path: Path) -> list[Path]:
image_paths = [] image_paths = []
try: try:
@@ -38,7 +62,7 @@ def parse_notebook_for_images(notebook_path: Path) -> list[Path]:
if part.startswith("SCR-") and any( if part.startswith("SCR-") and any(
part.endswith(ext) for ext in (".png", ".jpg", ".jpeg") part.endswith(ext) for ext in (".png", ".jpg", ".jpeg")
): ):
path = Path("/Users/Aman/Pictures") / part path = _get_pictures_dir() / part
if path.exists(): if path.exists():
image_paths.append(path) image_paths.append(path)