mirror of
https://git.srhtnin.garden/sarah/SaraSortd.git
synced 2026-10-13 18:38:11 +02:00
7.2: directory configs can live outside their directories
DirConfDir keeps every directory's config (and history file) in one folder, and [DirConfs] names a file for single directories, like SaraTag's Lexicons and Records. Both are optional; without them configs stay inside the directories as before. Clone now copies straight to the new name. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
aef32f6f3b
commit
4389a1c76e
5 files changed
+97
-53
No files matched your search
+4
-1
@@ -1,5 +1,5 @@
|
||||
Title = "SaraSortd Config"
|
||||
Version = "7.1"
|
||||
Version = "7.2"
|
||||
SafeMode = 1
|
||||
SilentMode = 0
|
||||
SortTimer = 1
|
||||
@@ -27,6 +27,9 @@ InputDir = [ "%RootDir%/Input" ]
|
||||
OutputDir = [ "%RootDir%/Output" ]
|
||||
LogDir = "%RootDir%/.Logs"
|
||||
FailedDir = "%RootDir%/.Failed"
|
||||
DirConfDir = ""
|
||||
|
||||
[DirConfs]
|
||||
|
||||
[Names]
|
||||
LogFileName = "[y].[m].[d]"
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
Title = "Unset"
|
||||
Version = "7.1"
|
||||
Version = "7.2"
|
||||
ParentDir = "Unset"
|
||||
SortDirs = 0
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
Title = "Unset"
|
||||
Version = "7.1"
|
||||
Version = "7.2"
|
||||
LastFile = "Unset"
|
||||
ParentDir = "Unset"
|
||||
ValidInputDirs = ["*"]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<img align="center" src="https://git.srhtnin.garden/sarah/RepoAssets/raw/branch/main/SaraSortd/Banners/SaraSortd.png" width="100%" alt="SaraSortd: a file and directory sorting daemon built with python."/>
|
||||
|
||||
(Version = "7.1")
|
||||
(Version = "7.2")
|
||||
|
||||
|
||||
It's made by Sarah, hence the name. :3
|
||||
@@ -58,7 +58,7 @@ Here's a step-by-step tutorial (in case you don't want to watch the video).
|
||||
```
|
||||
Title = "SaraSortd Config" # NOTE: Do not change the title. This is so that the program knows which config file is which.
|
||||
|
||||
Version = "7.1" # Just the version number for the config file.
|
||||
Version = "7.2" # Just the version number for the config file.
|
||||
|
||||
SafeMode = 1 # If SaraSortd should stop if an error with the config appears. 1 is stop, 0 is continue.
|
||||
|
||||
@@ -108,6 +108,13 @@ LogDir = "%RootDir%/.Logs" # Where log files from SaraSortd go.
|
||||
|
||||
FailedDir ="%RootDir%/.Failed" # For files that failed to be sorted.
|
||||
|
||||
DirConfDir = "" # Optional (since 7.2). Where directory configs live. If this is "", every directory keeps its config inside itself. If it's set, the configs (and history files) of all directories go here instead, so the directories stay clean. Relative paths count from GlobalConf.toml's own folder. E.G: DirConfDir = "Dirs"
|
||||
# Each config is named after its directory's path below RootDir, with "/" turned into "_", and InDirConfName/OutDirConfName applied without the leading ".". E.G: "%RootDir%/Pictures/Cats" gets "Pictures_CatsConfig.toml".
|
||||
|
||||
[DirConfs] # Optional (since 7.2). A config file for one directory, wherever you like. These win over DirConfDir. Relative paths count from GlobalConf.toml's own folder. E.G:
|
||||
# "%RootDir%/Downloads" = "Configs/Downloads.toml"
|
||||
# "%RootDir%/Pictures" = { In = "Configs/PicturesIn.toml", Out = "Configs/PicturesOut.toml" } # Separate input and output configs.
|
||||
|
||||
[Names]
|
||||
LogFileName = "[y].[m].[d]" # Name for log files.
|
||||
|
||||
@@ -160,7 +167,7 @@ The output directory config references the SaraSortd config (GlobalConf.toml). T
|
||||
```
|
||||
Title = "Unset" # NOTE: Do not change the title. This is so that the program knows which config file is which.
|
||||
|
||||
Version = "7.1" # The version number of the config.
|
||||
Version = "7.2" # The version number of the config.
|
||||
|
||||
LastFile = "Unset" # The last file that was sorted to this directory. It gets automatically updated whenever a file gets sorted to this directory.
|
||||
|
||||
@@ -216,7 +223,7 @@ Overwrite = 1 # Whether this specific file type can be overwritten by others of
|
||||
```
|
||||
Title = "Unset" # NOTE: Do not change the title. This is so that the program knows which config file is which.
|
||||
|
||||
Version = "7.1"
|
||||
Version = "7.2"
|
||||
|
||||
ParentDir = "Unset" # The name of this directory. It gets automatically updated when this config gets copied to an output directory.
|
||||
|
||||
|
||||
+80
-46
@@ -6,7 +6,7 @@ import shutil
|
||||
import time
|
||||
import toml
|
||||
|
||||
# Version = "7.1"
|
||||
# Version = "7.2"
|
||||
|
||||
ConfPath = "./GlobalConf.toml"
|
||||
|
||||
@@ -189,10 +189,69 @@ def Parse(
|
||||
return String
|
||||
|
||||
|
||||
def ConfFolder(PathText):
|
||||
# Relative paths in GlobalConf.toml (DirConfDir, [DirConfs]) count from GlobalConf.toml's own folder.
|
||||
PathText = Parse(String = PathText)
|
||||
if os.path.isabs(PathText):
|
||||
return os.path.normpath(PathText)
|
||||
return os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(ConfPath)), PathText))
|
||||
|
||||
|
||||
def FlatName(Path):
|
||||
# A directory's name for files kept outside it: its path below RootDir (or its whole path), "/" turned into "_".
|
||||
# E.G: "%RootDir%/Pictures/Cats" becomes "Pictures_Cats".
|
||||
Root = Parse(String = ConfDirs.get("RootDir", "")).rstrip("/")
|
||||
Path = Path.rstrip("/")
|
||||
if Root and Path.startswith(Root + "/"):
|
||||
Relative = Path[len(Root) + 1:]
|
||||
else:
|
||||
Relative = Path.strip("/") or os.path.basename(Path)
|
||||
return Relative.replace("/", "_")
|
||||
|
||||
|
||||
def CentralConf(Path, Kind):
|
||||
# Since 7.2, a directory's config can live outside it, like SaraTag's Lexicons:
|
||||
# [DirConfs] names a file per directory ("dir" = "file", or "dir" = { In = "file", Out = "file" }),
|
||||
# and DirConfDir holds the configs of every directory [DirConfs] doesn't name. None means inside the directory.
|
||||
for Key, Value in (Conf.get("DirConfs") or {}).items():
|
||||
if Parse(String = Key).rstrip("/") == Path.rstrip("/"):
|
||||
if isinstance(Value, dict):
|
||||
Value = Value.get(Kind)
|
||||
if Value:
|
||||
return ConfFolder(Value)
|
||||
|
||||
if ConfDirs.get("DirConfDir"):
|
||||
NameKey = "InDirConfName" if Kind == "In" else "OutDirConfName"
|
||||
Name = Parse(String = ConfNames[NameKey], Parent = FlatName(Path)).lstrip(".")
|
||||
return f"{ConfFolder(ConfDirs['DirConfDir'])}/{Name}.toml"
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def DirConfPath(Path, Kind):
|
||||
# The config of an input ("In") or output ("Out") directory.
|
||||
Central = CentralConf(Path, Kind)
|
||||
if Central:
|
||||
return Central
|
||||
|
||||
NameKey = "InDirConfName" if Kind == "In" else "OutDirConfName"
|
||||
return f"{Path}/{Parse(String = ConfNames[NameKey], Path = ConfPath, Parent = os.path.basename(Path))}.toml"
|
||||
|
||||
|
||||
def HistoryPath(Path):
|
||||
# The history file of a directory that sorts into itself. It goes next to the directory's config
|
||||
# when that's kept outside the directory.
|
||||
Central = CentralConf(Path, "In") or CentralConf(Path, "Out")
|
||||
if Central:
|
||||
Name = Parse(String = ConfNames["FileHistoryName"], Parent = FlatName(Path)).lstrip(".")
|
||||
return f"{os.path.dirname(Central)}/{Name}.toml"
|
||||
|
||||
return f"{Path}/{Parse(String = ConfNames['FileHistoryName'], Parent = os.path.basename(Path))}.toml"
|
||||
|
||||
|
||||
def Clone(Source, Destination, NewName, Delete = False):
|
||||
if (os.path.dirname(Source) == Destination):
|
||||
FileHistoryPath = f"{Destination}/{Parse(ConfNames["FileHistoryName"], Parent = os.path.basename(Destination))}.toml"
|
||||
print("FileHistoryPath:",FileHistoryPath)
|
||||
FileHistoryPath = HistoryPath(Destination)
|
||||
|
||||
if not os.path.exists(FileHistoryPath):
|
||||
with open(FileHistoryPath, "w", encoding = "utf-8") as HistoryFile:
|
||||
@@ -208,10 +267,9 @@ def Clone(Source, Destination, NewName, Delete = False):
|
||||
return
|
||||
|
||||
Dir(Destination, Output = False, CopyConf = False)
|
||||
shutil.copy2(Source, Destination)
|
||||
|
||||
BaseName = os.path.basename(Source)
|
||||
shutil.move(os.path.join(Destination, BaseName), os.path.join(Destination, NewName))
|
||||
# Straight to the new name: copying under the old name first could land on a file that's already
|
||||
# there (E.G. a template, when DirConfDir is GlobalConf.toml's own folder).
|
||||
shutil.copy2(Source, os.path.join(Destination, NewName))
|
||||
|
||||
if Delete:
|
||||
os.remove(Source)
|
||||
@@ -222,39 +280,16 @@ def Dir(Path, Output = True, CopyConf = "OutputDirConf.toml"):
|
||||
os.makedirs(Path, exist_ok=True)
|
||||
|
||||
if CopyConf:
|
||||
if "Out" in CopyConf:
|
||||
NewDirConf = f"{Parse(String = ConfNames['OutDirConfName'], Path = ConfPath, Parent = ParentName)}.toml"
|
||||
for Kind in ("Out", "In"):
|
||||
if Kind not in CopyConf:
|
||||
continue
|
||||
|
||||
if Conf["OverwriteDirConf"] == 1 or not os.path.exists(os.path.join(f"{Path}/{NewDirConf}")):
|
||||
Clone(CopyConf, Path, NewDirConf)
|
||||
UpdateConf(
|
||||
f"{Path}/{Parse(String = ConfNames['OutDirConfName'], Path = ConfPath, Parent = ParentName)}.toml",
|
||||
"ParentDir",
|
||||
ParentName,
|
||||
)
|
||||
DirConf = DirConfPath(Path, Kind)
|
||||
|
||||
UpdateConf(
|
||||
f"{Path}/{Parse(String = ConfNames['OutDirConfName'], Path = ConfPath, Parent = ParentName)}.toml",
|
||||
"Title",
|
||||
f"{ParentName} Config",
|
||||
)
|
||||
|
||||
if "In" in CopyConf:
|
||||
NewDirConf = f"{Parse(String = ConfNames['InDirConfName'], Path = ConfPath, Parent = ParentName)}.toml"
|
||||
|
||||
if Conf["OverwriteDirConf"] == 1 or not os.path.exists(os.path.join(f"{Path}/{NewDirConf}")):
|
||||
Clone(CopyConf, Path, NewDirConf)
|
||||
UpdateConf(
|
||||
f"{Path}/{Parse(String = ConfNames['InDirConfName'], Path = ConfPath, Parent = ParentName)}.toml",
|
||||
"ParentDir",
|
||||
ParentName,
|
||||
)
|
||||
|
||||
UpdateConf(
|
||||
f"{Path}/{Parse(String = ConfNames['InDirConfName'], Path = ConfPath, Parent = ParentName)}.toml",
|
||||
"Title",
|
||||
f"{ParentName} Config",
|
||||
)
|
||||
if Conf["OverwriteDirConf"] == 1 or not os.path.exists(DirConf):
|
||||
Clone(CopyConf, os.path.dirname(DirConf), os.path.basename(DirConf))
|
||||
UpdateConf(DirConf, "ParentDir", ParentName)
|
||||
UpdateConf(DirConf, "Title", f"{ParentName} Config")
|
||||
|
||||
if Output:
|
||||
ParsedPath = Parse(String = Path)
|
||||
@@ -299,8 +334,7 @@ def DecideNewPath(FilePath):
|
||||
|
||||
ParentName = os.path.basename(Output)
|
||||
|
||||
OutDirConfName = Parse(String = ConfNames["OutDirConfName"], Path = ConfPath, Parent = ParentName)
|
||||
OutDirConfPath = f"{Output}/{OutDirConfName}.toml"
|
||||
OutDirConfPath = DirConfPath(Output, "Out")
|
||||
|
||||
ValidInputDirs = GetConf("ValidInputDirs", OutDirConfPath) or []
|
||||
InputDirValid = False
|
||||
@@ -429,7 +463,7 @@ def Sort(FilePath):
|
||||
NewDirPath = os.path.dirname(NewPath)
|
||||
NewName = os.path.basename(NewPath)
|
||||
|
||||
if (GetConf("DeleteOrg", f"{NewDirPath}/{Parse(String = ConfNames['OutDirConfName'], Parent = (os.path.basename(NewDirPath)))}.toml") == 1):
|
||||
if (GetConf("DeleteOrg", DirConfPath(NewDirPath, "Out")) == 1):
|
||||
Delete = True
|
||||
|
||||
else:
|
||||
@@ -437,7 +471,7 @@ def Sort(FilePath):
|
||||
|
||||
try:
|
||||
Clone(FilePath, NewDirPath, NewName, Delete)
|
||||
UpdateConf(f"{NewDirPath}/{Parse(String = ConfNames['OutDirConfName'], Parent = os.path.basename(NewDirPath))}.toml", "LastFile", NewName)
|
||||
UpdateConf(DirConfPath(NewDirPath, "Out"), "LastFile", NewName)
|
||||
|
||||
TextOutput = Parse(String = ConfLog["Sorted"], VarCall = f"{FilePath} to {NewDirPath}/{NewName}")
|
||||
LogWrite(TextOutput)
|
||||
@@ -470,7 +504,7 @@ def Init():
|
||||
Output = Parse(String = Output, Parent = os.path.basename(os.path.dirname(Output)))
|
||||
Dir(Output)
|
||||
|
||||
OutDirConfPath = f"{Output}/{Parse(String = ConfNames["OutDirConfName"], Parent = os.path.basename(Output))}.toml"
|
||||
OutDirConfPath = DirConfPath(Output, "Out")
|
||||
ConfData = toml.load(OutDirConfPath)
|
||||
|
||||
for Item in ConfData.get("Files", []):
|
||||
@@ -509,12 +543,12 @@ def Main():
|
||||
if not os.path.exists(InputPath):
|
||||
continue
|
||||
|
||||
InputDirConfPath = f"{InputPath}/{Parse(ConfNames["InDirConfName"], Parent = os.path.basename(InputPath))}.toml"
|
||||
InputDirConfPath = DirConfPath(InputPath, "In")
|
||||
InputDirConf = toml.load(InputDirConfPath)
|
||||
|
||||
for Output in ConfDirs["OutputDir"]:
|
||||
OutputPath = Parse(Output)
|
||||
OutDirConfPath = f"{OutputPath}/{Parse(ConfNames["OutDirConfName"], Parent = os.path.basename(OutputPath))}.toml"
|
||||
OutDirConfPath = DirConfPath(OutputPath, "Out")
|
||||
|
||||
if len(GetConf("Unzip", OutDirConfPath)) >= 1:
|
||||
ZipTypes = GetConf("Unzip", OutDirConfPath)
|
||||
@@ -541,7 +575,7 @@ def Main():
|
||||
|
||||
if (OutputPath == InputPath):
|
||||
RecurseDir = True
|
||||
FileHistoryPath = f"{InputPath}/{Parse(ConfNames["FileHistoryName"], Parent = os.path.basename(InputPath))}.toml"
|
||||
FileHistoryPath = HistoryPath(InputPath)
|
||||
|
||||
if not os.path.exists(FileHistoryPath):
|
||||
with open(FileHistoryPath, "w", encoding = "utf-8") as HistoryFile:
|
||||
|
||||
Reference in new issue
Block a user