from __future__ import annotations import shutil from pathlib import Path from typing import Any from Core import GetRoots, IsInternalPath, ResolvePath FileTypeMap = { ".md": "markdown", ".markdown": "markdown", ".txt": "text", ".toml": "config", ".yaml": "config", ".yml": "config", ".json": "config", ".cfg": "config", ".ini": "config", ".html": "html", ".htm": "html", ".png": "image", ".jpg": "image", ".jpeg": "image", ".gif": "image", ".webp": "image", ".svg": "image", ".mp4": "video", ".webm": "video", ".mov": "video", ".mkv": "video", } def ClassifyFile(EntryPath: Path) -> str: return FileTypeMap.get(EntryPath.suffix.lower(), "unknown") NoteFilePrefix = ".sv_" DefaultNoteExtension = ".md" def IsNoteFile(EntryName: str) -> bool: return EntryName.startswith(NoteFilePrefix) def FindDirectoryNote(DirectoryPath: Path) -> Path | None: Prefix = f"{NoteFilePrefix}{DirectoryPath.name}." for Candidate in DirectoryPath.iterdir(): if Candidate.is_file() and Candidate.name.startswith(Prefix): return Candidate return None def EnsureDirectoryNote(DirectoryPath: Path) -> Path: Existing = FindDirectoryNote(DirectoryPath) if Existing is not None: return Existing NotePath = DirectoryPath / f"{NoteFilePrefix}{DirectoryPath.name}{DefaultNoteExtension}" NotePath.touch() return NotePath def GetDirectoryNoteInfo(DirectoryPath: Path) -> dict[str, Any] | None: NotePath = FindDirectoryNote(DirectoryPath) if NotePath is None: return None return {"Name": NotePath.name, "FileType": ClassifyFile(NotePath)} def RenameDirectoryNote(DirectoryPath: Path, OldDirectoryName: str) -> None: OldPrefix = f"{NoteFilePrefix}{OldDirectoryName}." for Candidate in DirectoryPath.iterdir(): if Candidate.is_file() and Candidate.name.startswith(OldPrefix): NewName = f"{NoteFilePrefix}{DirectoryPath.name}{Candidate.suffix}" Candidate.rename(DirectoryPath / NewName) return def ListRoots() -> list[dict[str, Any]]: return [{"Name": Name} for Name in GetRoots()] def CreateEntry(RootName: str, RelativePath: str, IsDirectory: bool) -> None: EntryPath = ResolvePath(RootName, RelativePath) if EntryPath.exists(): raise FileExistsError(f"{EntryPath.name} already exists.") if IsDirectory: EntryPath.mkdir(parents=False) EnsureDirectoryNote(EntryPath) else: EntryPath.parent.mkdir(parents=True, exist_ok=True) EntryPath.touch() def MoveEntry(RootName: str, SourceRelativePath: str, DestinationRelativePath: str) -> None: SourcePath = ResolvePath(RootName, SourceRelativePath) DestinationPath = ResolvePath(RootName, DestinationRelativePath) if not SourcePath.exists(): raise FileNotFoundError(f"{SourcePath.name} does not exist.") if DestinationPath.exists(): raise FileExistsError(f"{DestinationPath.name} already exists.") OldName = SourcePath.name DestinationPath.parent.mkdir(parents=True, exist_ok=True) try: SourcePath.rename(DestinationPath) except OSError: shutil.move(str(SourcePath), str(DestinationPath)) if DestinationPath.is_dir(): RenameDirectoryNote(DestinationPath, OldName) def DeleteEntry(RootName: str, RelativePath: str) -> None: EntryPath = ResolvePath(RootName, RelativePath) if not EntryPath.exists(): raise FileNotFoundError(f"{EntryPath.name} does not exist.") if EntryPath.is_dir(): shutil.rmtree(EntryPath) else: EntryPath.unlink() def ListDirectory( RootName: str, RelativePath: str, ShowHidden: bool = False, ShowSvFiles: bool = False ) -> list[dict[str, Any]]: DirectoryPath = ResolvePath(RootName, RelativePath) if not DirectoryPath.is_dir(): raise NotADirectoryError(f"{DirectoryPath} is not a directory.") Entries: list[dict[str, Any]] = [] SortedChildren = sorted( DirectoryPath.iterdir(), key=lambda EntryPath: (EntryPath.is_file(), EntryPath.name.casefold()), ) for EntryPath in SortedChildren: if IsInternalPath(EntryPath): continue if IsNoteFile(EntryPath.name): if not ShowSvFiles: continue elif not ShowHidden and EntryPath.name.startswith("."): continue IsDirectory = EntryPath.is_dir() Entries.append( { "Name": EntryPath.name, "IsDirectory": IsDirectory, "FileType": None if IsDirectory else ClassifyFile(EntryPath), "Size": None if IsDirectory else EntryPath.stat().st_size, "Note": GetDirectoryNoteInfo(EntryPath) if IsDirectory else None, } ) return Entries