mirror of
https://git.srhtnin.garden/sarah/SaraView.git
synced 2026-10-14 18:38:11 +02:00
Initial commit
This commit is contained in:
commit
8aa223bc7b
40 files changed
+4320
No files matched your search
@@ -0,0 +1,130 @@
|
||||
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 a coarse file-type label used to pick a preview renderer."""
|
||||
return FileTypeMap.get(EntryPath.suffix.lower(), "unknown")
|
||||
|
||||
|
||||
def ListRoots() -> list[dict[str, Any]]:
|
||||
"""Return the configured roots as a flat list for the frontend."""
|
||||
return [{"Name": Name} for Name in GetRoots()]
|
||||
|
||||
|
||||
def CreateEntry(RootName: str, RelativePath: str, IsDirectory: bool) -> None:
|
||||
"""Create a new empty file or directory at the given path."""
|
||||
EntryPath = ResolvePath(RootName, RelativePath)
|
||||
|
||||
if EntryPath.exists():
|
||||
raise FileExistsError(f"{EntryPath.name} already exists.")
|
||||
|
||||
if IsDirectory:
|
||||
EntryPath.mkdir(parents=False)
|
||||
else:
|
||||
EntryPath.parent.mkdir(parents=True, exist_ok=True)
|
||||
EntryPath.touch()
|
||||
|
||||
|
||||
def MoveEntry(RootName: str, SourceRelativePath: str, DestinationRelativePath: str) -> None:
|
||||
"""Rename or move a file/directory within the same root. Used for both
|
||||
plain renames (same parent, new name) and full moves.
|
||||
"""
|
||||
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.")
|
||||
|
||||
DestinationPath.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
try:
|
||||
SourcePath.rename(DestinationPath)
|
||||
except OSError:
|
||||
# Cross-device or other rename failures — fall back to a copy+delete.
|
||||
shutil.move(str(SourcePath), str(DestinationPath))
|
||||
|
||||
|
||||
def DeleteEntry(RootName: str, RelativePath: str) -> None:
|
||||
"""Delete a file, or an entire directory tree."""
|
||||
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) -> list[dict[str, Any]]:
|
||||
"""List the immediate contents of a directory.
|
||||
|
||||
SaraTag's internal files are always hidden, regardless of ShowHidden.
|
||||
Dotfiles/dotfolders are hidden unless ShowHidden is True.
|
||||
"""
|
||||
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 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,
|
||||
}
|
||||
)
|
||||
|
||||
return Entries
|
||||
Reference in new issue
Block a user