Files
SaraView/Backend/Explorer.py
T

176 lines
4.8 KiB
Python

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