mirror of
https://git.srhtnin.garden/sarah/SaraSuite.git
synced 2026-10-14 18:28:11 +02:00
Initial commit
This commit is contained in:
commit
bea0f43947
25 files changed
+2327
No files matched your search
@@ -0,0 +1,67 @@
|
||||
Name = "SaraFem"
|
||||
Description = "Desktop file manager that understands SaraTag Books, and can stand in as the system file picker."
|
||||
|
||||
# The portal reads its descriptors and preferences once at startup.
|
||||
RestartUnits = ["xdg-desktop-portal.service"]
|
||||
|
||||
[Source]
|
||||
Git = "{GitHost}/SaraFem"
|
||||
Include = ["*.py", "requirements.txt", "assets/**"]
|
||||
Exclude = ["Packaging"]
|
||||
|
||||
[Python]
|
||||
RequirementsFile = "requirements.txt"
|
||||
|
||||
[[Options]]
|
||||
Id = "FilePicker"
|
||||
Label = "Use SaraFem as the system file picker (asks for your password: the portal file lives in /usr/share)"
|
||||
Default = true
|
||||
|
||||
[[Options]]
|
||||
Id = "DefaultFileManager"
|
||||
Label = "Make SaraFem the default file manager"
|
||||
Default = false
|
||||
|
||||
[[Templates]]
|
||||
Source = "Launch.sh"
|
||||
Target = "{AppDir}/Packaging/Launch.sh" # where PortalService.py looks for it
|
||||
Executable = true
|
||||
|
||||
[[Templates]]
|
||||
Source = "SaraFem.desktop"
|
||||
Target = "{ApplicationsDir}/SaraFem.desktop"
|
||||
|
||||
[[Templates]]
|
||||
Source = "PortalService.service"
|
||||
Target = "{DBusServicesDir}/org.freedesktop.impl.portal.desktop.SaraFem.service"
|
||||
Option = "FilePicker"
|
||||
|
||||
[[Templates]]
|
||||
Source = "SaraFem.portal"
|
||||
Target = "/usr/share/xdg-desktop-portal/portals/SaraFem.portal"
|
||||
Option = "FilePicker"
|
||||
Root = true
|
||||
|
||||
[[IniEdits]]
|
||||
File = "{ConfigHome}/xdg-desktop-portal/{Desktop}-portals.conf"
|
||||
Section = "preferred"
|
||||
Key = "org.freedesktop.impl.portal.FileChooser"
|
||||
Value = "SaraFem;{Desktop};gtk;"
|
||||
Option = "FilePicker"
|
||||
|
||||
[[IniEdits]]
|
||||
File = "{ConfigHome}/mimeapps.list"
|
||||
Section = "Default Applications"
|
||||
Key = "inode/directory"
|
||||
Value = "SaraFem.desktop"
|
||||
Option = "DefaultFileManager"
|
||||
|
||||
[[Leftovers]]
|
||||
Path = "{CacheHome}/SaraFem"
|
||||
Kind = "Runtime"
|
||||
Note = "thumbnail cache"
|
||||
|
||||
[[Leftovers]]
|
||||
Path = "{ConfigHome}/SaraFem"
|
||||
Kind = "Config"
|
||||
Note = "old pre-SaraSuite config location"
|
||||
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
# Written by SaraSuite. Runs from the app folder so Flet finds assets/.
|
||||
cd "{AppDir}" || exit 1
|
||||
exec "{Python}" Main.py "$@"
|
||||
@@ -0,0 +1,3 @@
|
||||
[D-BUS Service]
|
||||
Name=org.freedesktop.impl.portal.desktop.SaraFem
|
||||
Exec={AppDir}/Packaging/Launch.sh --portal-service
|
||||
@@ -0,0 +1,10 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=SaraFem
|
||||
Comment=A file manager for the SaraSuite
|
||||
Exec={AppDir}/Packaging/Launch.sh %f
|
||||
Icon={AppDir}/assets/Icon.png
|
||||
Terminal=false
|
||||
Categories=System;FileManager;
|
||||
MimeType=inode/directory;
|
||||
StartupNotify=true
|
||||
@@ -0,0 +1,4 @@
|
||||
[portal]
|
||||
DBusName=org.freedesktop.impl.portal.desktop.SaraFem
|
||||
Interfaces=org.freedesktop.impl.portal.FileChooser;
|
||||
UseIn={DesktopName}
|
||||
@@ -0,0 +1,91 @@
|
||||
"""Works out what SaraSortd made on its own while running, by reading
|
||||
GlobalConf.toml the same way SaraSortd.py does (see its Parse/GetConf/Init).
|
||||
|
||||
It never removes the user's files: the input/output/failed folders are only
|
||||
removed if they're empty once SaraSortd's own files are gone.
|
||||
"""
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
import tomlkit
|
||||
|
||||
from Manifests import Leftover
|
||||
|
||||
|
||||
def _Find(Conf: dict, Key: str):
|
||||
"""SaraSortd's GetConf: a top-level key, or one inside a table/array of tables."""
|
||||
if Key in Conf:
|
||||
return Conf[Key]
|
||||
for Value in Conf.values():
|
||||
if isinstance(Value, dict) and Key in Value:
|
||||
return Value[Key]
|
||||
if isinstance(Value, list):
|
||||
for Item in Value:
|
||||
if isinstance(Item, dict) and Key in Item:
|
||||
return Item[Key]
|
||||
return None
|
||||
|
||||
|
||||
def _Resolve(Text: str, Conf: dict) -> str:
|
||||
"""SaraSortd's %Var% substitution (single pass, like its Parse)."""
|
||||
VarCall = Conf.get("Variables", {}).get("VarCall", "%%")
|
||||
Pattern = f"{re.escape(VarCall[0])}(.*?){re.escape(VarCall[1])}"
|
||||
|
||||
def Replace(Match: re.Match) -> str:
|
||||
Value = _Find(Conf, Match[1]) if Match[1] else None
|
||||
return str(Value) if Value is not None else Match[0]
|
||||
|
||||
return re.sub(Pattern, Replace, Text)
|
||||
|
||||
|
||||
def _DirFileName(Conf: dict, NameKey: str, DirPath: Path) -> str:
|
||||
"""e.g. ".@Config" for a folder named Input -> ".InputConfig.toml"."""
|
||||
Parent = Conf.get("Variables", {}).get("Parent", "@")
|
||||
Name = Conf.get("Names", {}).get(NameKey, "")
|
||||
return f"{Name.replace(Parent, DirPath.name)}.toml" if Name else ""
|
||||
|
||||
|
||||
def Leftovers(Context: dict) -> list[Leftover]:
|
||||
ConfPath = Path(Context["ConfigDir"]) / "GlobalConf.toml"
|
||||
if not ConfPath.exists():
|
||||
return []
|
||||
try:
|
||||
Conf = tomlkit.parse(ConfPath.read_text()).unwrap()
|
||||
except tomlkit.exceptions.TOMLKitError:
|
||||
return []
|
||||
|
||||
Paths = Conf.get("DirectoryPaths", {})
|
||||
Found: list[Leftover] = []
|
||||
|
||||
Folders = []
|
||||
for Key in ("InputDir", "OutputDir"):
|
||||
Entries = Paths.get(Key, [])
|
||||
for Entry in Entries if isinstance(Entries, list) else [Entries]:
|
||||
Folders.append(Path(_Resolve(str(Entry), Conf)))
|
||||
|
||||
for Folder in Folders:
|
||||
for NameKey in ("InDirConfName", "OutDirConfName", "DirConfName"):
|
||||
FileName = _DirFileName(Conf, NameKey, Folder)
|
||||
if FileName:
|
||||
Found.append(Leftover(str(Folder / FileName), "Config", Note="this folder's SaraSortd rules"))
|
||||
HistoryName = _DirFileName(Conf, "FileHistoryName", Folder)
|
||||
if HistoryName:
|
||||
Found.append(Leftover(str(Folder / HistoryName), "Runtime", Note="sorting history"))
|
||||
Found.append(Leftover(str(Folder), "Runtime", "IfEmpty", "only if empty"))
|
||||
|
||||
LogDir = Paths.get("LogDir")
|
||||
if LogDir:
|
||||
LogDir = _Resolve(str(LogDir), Conf)
|
||||
Found.append(Leftover(f"{LogDir}/*.log", "Runtime", Note="log"))
|
||||
Found.append(Leftover(LogDir, "Runtime", "IfEmpty", "only if empty"))
|
||||
|
||||
FailedDir = Paths.get("FailedDir")
|
||||
if FailedDir:
|
||||
Found.append(Leftover(_Resolve(str(FailedDir), Conf), "Runtime", "IfEmpty", "only if empty, it holds files that failed to sort"))
|
||||
|
||||
RootDir = Paths.get("RootDir")
|
||||
if RootDir and RootDir != "Unset":
|
||||
Found.append(Leftover(_Resolve(str(RootDir), Conf), "Runtime", "IfEmpty", "only if empty"))
|
||||
|
||||
return Found
|
||||
@@ -0,0 +1,51 @@
|
||||
Name = "SaraSortd"
|
||||
Description = "Sorts files from input folders into output folders by the rules in its configs. Runs in the background as a systemd user service."
|
||||
|
||||
[Version]
|
||||
File = "SaraSortd.py"
|
||||
Pattern = '# Version = "([^"]+)"'
|
||||
|
||||
[Source]
|
||||
Git = "{GitHost}/SaraSortd"
|
||||
Include = ["SaraSortd.py", "LICENSE", "README.md"]
|
||||
|
||||
[Python]
|
||||
Requirements = ["toml"]
|
||||
|
||||
[[Questions]]
|
||||
Id = "RootDir"
|
||||
Prompt = "Folder SaraSortd sorts in (RootDir)"
|
||||
Default = "~/Sorting"
|
||||
Kind = "Path"
|
||||
|
||||
[[Options]]
|
||||
Id = "Autostart"
|
||||
Label = "Start SaraSortd now and on every login"
|
||||
Default = true
|
||||
|
||||
# SaraSortd reads ./GlobalConf.toml and copies ./InputDirConf.toml and
|
||||
# ./OutputDirConf.toml into its folders, so all three live together in
|
||||
# ConfigDir and the service runs from there.
|
||||
[[Configs]]
|
||||
Source = "GlobalConf.toml"
|
||||
Target = "{ConfigDir}/GlobalConf.toml"
|
||||
Set = { "DirectoryPaths.RootDir" = "{RootDir}" }
|
||||
|
||||
[[Configs]]
|
||||
Source = "InputDirConf.toml"
|
||||
Target = "{ConfigDir}/InputDirConf.toml"
|
||||
|
||||
[[Configs]]
|
||||
Source = "OutputDirConf.toml"
|
||||
Target = "{ConfigDir}/OutputDirConf.toml"
|
||||
|
||||
[[Templates]]
|
||||
Source = "SaraSortd.service"
|
||||
Target = "{SystemdUserDir}/SaraSortd.service"
|
||||
|
||||
[[Services]]
|
||||
Unit = "SaraSortd.service"
|
||||
Option = "Autostart"
|
||||
|
||||
# Everything SaraSortd makes inside the sorting folders is worked out from
|
||||
# GlobalConf.toml by Hooks.py.
|
||||
@@ -0,0 +1,15 @@
|
||||
# Written by SaraSuite. Changes here are overwritten on update:
|
||||
# edit the manifest template in SaraSuite/Programs/SaraSortd/ instead.
|
||||
[Unit]
|
||||
Description=SaraSortd file sorting daemon
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory={ConfigDir}
|
||||
ExecStart={Python} {AppDir}/SaraSortd.py
|
||||
Environment=PYTHONUNBUFFERED=1
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
Reference in new issue
Block a user