From fc6f9bd39948157fd0ff728a6d1d0cedfa8f99eb Mon Sep 17 00:00:00 2001 From: Sarahtonin Date: Fri, 10 Apr 2026 22:10:41 +0200 Subject: [PATCH 1/2] Update README.md --- .Github/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.Github/README.md b/.Github/README.md index 2e2092e..7abbb61 100644 --- a/.Github/README.md +++ b/.Github/README.md @@ -128,9 +128,9 @@ Pattern = "*" # A pattern used to sort files to this directory. * is a wildcard; NewFileName = "~^" # A pattern for the new names of the files that get sorted to this directory from matching this "Pattern". -NextNum = 1 # Used to keep track of the next available number for file names. It gets automatically incremented when NextNum is used in NewFileName, and a file gets sorted to this directory and renamed. +NextNum = 1 # Used to keep track of the next available number for file names. If you have two of the character that represents NextNum (Found in Config.toml), you can stack them together to make 01 or 001 instead of 1. It gets automatically incremented when NextNum is used in NewFileName, and a file gets sorted to this directory and renamed. -NextChar = "A" # Used to keep track of the next available letter for file names. It gets automatically incremented (E.G: A into B) when NextChar is used in NewFileName, and a file gets sorted to this directory and renamed. +NextChar = "A" # Used to keep track of the next available letter for file names. It gets automatically incremented (E.G: A into B or AA into AB then AZ into BA) when NextChar is used in NewFileName, and a file gets sorted to this directory and renamed. CaseSensitive = 0 # Whether this "Pattern" is case-sensitive or not. 1 is yes, 0 is no. E.G: if CaseSensitive = 1 and this "Pattern" is "*.PNG", a file named "picture.png" won't get sorted to this directory. If CaseSensitive = 0 and this "Pattern" is "*.PNG", a file named "picture.png" *will* get sorted to this directory. From 51c557d228dc88edb6f764f3b95ef9120813497b Mon Sep 17 00:00:00 2001 From: Sarahtonin Date: Mon, 20 Apr 2026 19:58:29 +0200 Subject: [PATCH 2/2] Added FileHistoryName to config, allowing an input and output dir to be the same and stopping recursion by saving the new file name. Also fixed hour, minute, second, and so to be at least 2 wide (19:2:3 => 19:02:03) --- .Github/README.md | 2 ++ Config.toml | 7 ++--- SaraSortd.py | 67 ++++++++++++++++++++++++++++++++++++----------- 3 files changed, 58 insertions(+), 18 deletions(-) diff --git a/.Github/README.md b/.Github/README.md index 593543f..5688f34 100644 --- a/.Github/README.md +++ b/.Github/README.md @@ -103,6 +103,8 @@ LogFileName = "[y].[m].[d]._*" # Name for log files. DirConfName = ".@Config" # Name for config files for directories. +FileHistoryName = ".@History" # Name for file history files. This keeps track of sorted files if an input and output are the same directory. + [Log] All = "[h]:[n]:[s] | " # What to put at the start of all lines in Log file. diff --git a/Config.toml b/Config.toml index 71ee501..dcad170 100644 --- a/Config.toml +++ b/Config.toml @@ -1,7 +1,7 @@ Title = "SaraSortd Config" SafeMode = 1 SilentMode = 0 -CheckInput = 10 +CheckInput = 5 DotFiles = 0 Logging = 1 OverwriteDirConf = 0 @@ -21,7 +21,7 @@ Second = "[s]" VarCall = "%%" [DirectoryPaths] -RootDir = "Unset" +RootDir = "/home/sarah/TestDir" InputDir = [ "%RootDir%/Input" ] OutputDir = [ "%RootDir%/Output" ] LogDir = "%RootDir%/.Logs" @@ -30,12 +30,13 @@ FailedDir = "%RootDir%/.Failed" [Names] LogFileName = "[y].[m].[d]" DirConfName = ".@Config" +FileHistoryName = ".@History" [Log] All = "[h]:[n]:[s] | " Start = "Started SaraSortd." NotStart = "Error: Failed to start SaraSortd." -Stop = "\nStopped SaraSortd." +Stop = "Stopped SaraSortd.\n" MatchPattern = "Checking %%." Sorted = "Sorted %%." NotSorted = "Error: Failed to sort %%. No valid output directory." diff --git a/SaraSortd.py b/SaraSortd.py index 9435f49..70dc726 100644 --- a/SaraSortd.py +++ b/SaraSortd.py @@ -166,11 +166,11 @@ def Parse( ConfVars["OrgFileName"]: str(OrgFileName), ConfVars["OrgFileType"]: str(OrgFileType), ConfVars["Year"]: str(Now.year), - ConfVars["Month"]: str(Now.month), - ConfVars["Day"]: str(Now.day), - ConfVars["Hour"]: str(Now.hour), - ConfVars["Minute"]: str(Now.minute), - ConfVars["Second"]: str(Now.second), + ConfVars["Month"]: str(Now.month).rjust(2, "0"), + ConfVars["Day"]: str(Now.day).rjust(2, "0"), + ConfVars["Hour"]: str(Now.hour).rjust(2, "0"), + ConfVars["Minute"]: str(Now.minute).rjust(2, "0"), + ConfVars["Second"]: str(Now.second).rjust(2, "0"), ConfVars["VarCall"]: str(VarCall), } @@ -188,6 +188,23 @@ def Parse( 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) + + if not os.path.exists(FileHistoryPath): + with open(FileHistoryPath, "w", encoding = "utf-8") as HistoryFile: + toml.dump({"Title": f"{os.path.basename(Destination)} History", "History": []}, HistoryFile) + + HistoryFileData = toml.load(FileHistoryPath) + if (NewName not in HistoryFileData["History"]): + HistoryFileData["History"].append(NewName) + with open(FileHistoryPath, "w", encoding = "utf-8") as HistoryFile: + toml.dump(HistoryFileData, HistoryFile) + + os.rename(Source, os.path.join(Destination, NewName)) + return + Dir(Destination, Output = False, CopyConf = False) shutil.copy2(Source, Destination) @@ -364,6 +381,16 @@ def DecideNewPath(FilePath): def Sort(FilePath): NewPath = DecideNewPath(FilePath) + if NewPath is None: + Dir(Parse(String = ConfDirs["FailedDir"]), Output = False, CopyConf = False) + NewName = os.path.basename(FilePath) + Clone(FilePath, Parse(String = ConfDirs["FailedDir"]), NewName, True) + + TextOutput = Parse(String = ConfLog["NotSorted"], VarCall = FilePath) + LogWrite(TextOutput) + Speak(TextOutput) + return + NewDirPath = os.path.dirname(NewPath) NewName = os.path.basename(NewPath) @@ -373,16 +400,6 @@ def Sort(FilePath): else: Delete = False - if NewPath is None: - Dir(Parse(String = ConfDirs["FailedDir"]), Output = False, CopyConf = False) - NewName = os.path.basename(FilePath) - Clone(FilePath, Parse(String = ConfDirs["FailedDir"]), NewName, Delete) - - TextOutput = Parse(String = ConfLog["NotSorted"], VarCall = FilePath) - LogWrite(TextOutput) - Speak(TextOutput) - return - try: Clone(FilePath, NewDirPath, NewName, Delete) UpdateConf(f"{NewDirPath}/{Parse(String = ConfNames['DirConfName'], Parent = os.path.basename(NewDirPath))}.toml", "LastFile", NewName) @@ -450,11 +467,24 @@ def Main(): try: while True: for Input in ConfDirs["InputDir"]: + RecurseDir = False + InputPath = Parse(Input) if not os.path.exists(InputPath): continue + for Output in ConfDirs["OutputDir"]: + OutputPath = Parse(Output) + + if (OutputPath == InputPath): + RecurseDir = True + FileHistoryPath = f"{InputPath}/{Parse(ConfNames["FileHistoryName"], Parent = os.path.basename(InputPath))}.toml" + + if not os.path.exists(FileHistoryPath): + with open(FileHistoryPath, "w", encoding = "utf-8") as HistoryFile: + toml.dump({"Title": f"{os.path.basename(InputPath)} History", "History": [] }, HistoryFile) + for FileName in os.listdir(InputPath): FilePath = f"{InputPath}/{FileName}" @@ -462,6 +492,13 @@ def Main(): FileName = os.path.basename(FilePath) if FileName.startswith(".") and Conf["DotFiles"] == 0: continue + + if RecurseDir: + HistoryFile = toml.load(FileHistoryPath) + + if (Parse(FileName, Parent = os.path.basename(InputPath)) in HistoryFile["History"]): + continue + Sort(FilePath) time.sleep(Conf.get("CheckInput", 10))