fix: make -revert scan the PathInput tree like the no-parameter run
-revert was DB-driven, searching each blog's Blogs.TTFolderPath non-recursively for *.bak. That tree differs from the no-parameter run, which recursively walks PathInput. Rewrite RevertMode to recursively walk PathInput (filesystem-only, no DB), with the optional [blogname] argument now filtering by path substring. Restore mechanics unchanged. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
@@ -185,7 +185,7 @@ namespace URLNotesGrabberCORE
|
|||||||
|
|
||||||
Console.WriteLine("-output\t Export posts from TL.db back to .txt files in each blog's TTFolderPath");
|
Console.WriteLine("-output\t Export posts from TL.db back to .txt files in each blog's TTFolderPath");
|
||||||
|
|
||||||
Console.WriteLine("-revert [blogname]\t Inverse of -output: restore *.bak back to *.txt (current .txt saved as next-free .bkN)");
|
Console.WriteLine("-revert [blogname]\t Recursively scan the PathInput tree and restore *.bak back to *.txt (current .txt saved as next-free .bkN); optional blogname filters by path substring");
|
||||||
|
|
||||||
Console.WriteLine("-correct [bakPath]\t Dry-run: report multi-line field updates available from a BAK directory");
|
Console.WriteLine("-correct [bakPath]\t Dry-run: report multi-line field updates available from a BAK directory");
|
||||||
|
|
||||||
|
|||||||
@@ -2,38 +2,36 @@ using Microsoft.Extensions.Configuration;
|
|||||||
|
|
||||||
namespace URLNotesGrabberCORE
|
namespace URLNotesGrabberCORE
|
||||||
{
|
{
|
||||||
// Inverse of OutputMode. For each Blog with a TTFolderPath, restores every *.bak back
|
// Inverse of OutputMode. Recursively walks the PathInput tree (the same directory tree the
|
||||||
// to its *.txt, first preserving the current *.txt as the next-free *.bkN. Consumes
|
// no-parameter run uses) and restores every *.bak back to its *.txt, first preserving the
|
||||||
// the *.bak (File.Move). Filesystem-only; reads the DB only to enumerate folders.
|
// current *.txt as the next-free *.bkN. Consumes the *.bak (File.Move). Filesystem-only;
|
||||||
|
// does not read the DB. An optional blogname argument filters by path substring.
|
||||||
public static class RevertMode
|
public static class RevertMode
|
||||||
{
|
{
|
||||||
public static int Run(IConfiguration config, string? blogFilter = null)
|
public static int Run(IConfiguration config, string? blogFilter = null)
|
||||||
{
|
{
|
||||||
DataAccess.EnsureTTFileHelperColumnsExist();
|
string? root = config["appSettings:PathInput"];
|
||||||
|
if (string.IsNullOrWhiteSpace(root) || !Directory.Exists(root))
|
||||||
var blogs = DataAccess.GetAllBlogsWithTTFolderPath();
|
|
||||||
if (!string.IsNullOrWhiteSpace(blogFilter))
|
|
||||||
blogs = blogs.Where(b => string.Equals(b.BlogName, blogFilter, StringComparison.OrdinalIgnoreCase)).ToList();
|
|
||||||
|
|
||||||
Console.WriteLine($"Found {blogs.Count} blog(s) to process.");
|
|
||||||
|
|
||||||
// Count total *.bak files up front so the confirmation prompt is meaningful.
|
|
||||||
int totalBakFiles = 0;
|
|
||||||
foreach (var (_, ttFolderPath) in blogs)
|
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(ttFolderPath) || !Directory.Exists(ttFolderPath))
|
Console.WriteLine($"PathInput is not set or does not exist: '{root}'. Nothing to revert.");
|
||||||
continue;
|
|
||||||
try { totalBakFiles += Directory.GetFiles(ttFolderPath, "*.bak").Length; }
|
|
||||||
catch { }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (totalBakFiles == 0)
|
|
||||||
{
|
|
||||||
Console.WriteLine("No .bak files found in any blog folder. Nothing to revert.");
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.Write($"WARNING: This will restore {totalBakFiles} .bak file(s) over their .txt files across {blogs.Count} blog folder(s). " +
|
Console.WriteLine($"Searching for .bak files under: {root}");
|
||||||
|
|
||||||
|
// Recursively collect every *.bak, optionally filtered by path substring (blogname).
|
||||||
|
var bakFiles = EnumerateBakFiles(root)
|
||||||
|
.Where(f => string.IsNullOrWhiteSpace(blogFilter)
|
||||||
|
|| f.IndexOf(blogFilter, StringComparison.OrdinalIgnoreCase) >= 0)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (bakFiles.Count == 0)
|
||||||
|
{
|
||||||
|
Console.WriteLine("No .bak files found. Nothing to revert.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.Write($"WARNING: This will restore {bakFiles.Count} .bak file(s) over their .txt files. " +
|
||||||
$"Current .txt files are preserved as the next-free .bkN. Continue? (yes/no): ");
|
$"Current .txt files are preserved as the next-free .bkN. Continue? (yes/no): ");
|
||||||
string? response = Console.ReadLine();
|
string? response = Console.ReadLine();
|
||||||
if (string.IsNullOrWhiteSpace(response) || !response.Equals("yes", StringComparison.OrdinalIgnoreCase))
|
if (string.IsNullOrWhiteSpace(response) || !response.Equals("yes", StringComparison.OrdinalIgnoreCase))
|
||||||
@@ -42,38 +40,7 @@ namespace URLNotesGrabberCORE
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var (blogName, ttFolderPath) in blogs)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"\nProcessing blog: {blogName}");
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(ttFolderPath) || !Directory.Exists(ttFolderPath))
|
|
||||||
{
|
|
||||||
Console.WriteLine($" TTFolderPath does not exist or is not set. Skipping.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine($" TTFolderPath: {ttFolderPath}");
|
|
||||||
|
|
||||||
var (restored, backedUp) = RevertFolder(ttFolderPath);
|
|
||||||
Console.WriteLine($" Restored {restored} file(s); backed up {backedUp} current .txt file(s).");
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine("\nRevert mode complete.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For each *.bak: back up the current *.txt to the next-free *.bkN, then File.Move(bak -> txt).
|
|
||||||
private static (int restored, int backedUp) RevertFolder(string folderPath)
|
|
||||||
{
|
|
||||||
int restored = 0, backedUp = 0;
|
int restored = 0, backedUp = 0;
|
||||||
|
|
||||||
var bakFiles = Directory.GetFiles(folderPath, "*.bak");
|
|
||||||
if (bakFiles.Length == 0)
|
|
||||||
{
|
|
||||||
Console.WriteLine(" Nothing to revert (no .bak files).");
|
|
||||||
return (restored, backedUp);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var bakFile in bakFiles)
|
foreach (var bakFile in bakFiles)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -90,15 +57,44 @@ namespace URLNotesGrabberCORE
|
|||||||
|
|
||||||
File.Move(bakFile, txtPath);
|
File.Move(bakFile, txtPath);
|
||||||
restored++;
|
restored++;
|
||||||
Console.WriteLine($" Restored {Path.GetFileName(bakFile)} -> {Path.GetFileName(txtPath)}");
|
Console.WriteLine($" Restored {bakFile} -> {Path.GetFileName(txtPath)}");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Console.WriteLine($" Error reverting {Path.GetFileName(bakFile)}: {ex.Message}");
|
Console.WriteLine($" Error reverting {bakFile}: {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (restored, backedUp);
|
Console.WriteLine($"\nRevert mode complete. Restored {restored} file(s); backed up {backedUp} current .txt file(s).");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively yields every *.bak path under root. Per-directory try/catch so an
|
||||||
|
// inaccessible folder doesn't abort the whole walk (mirrors TraverseDirectory).
|
||||||
|
private static IEnumerable<string> EnumerateBakFiles(string path)
|
||||||
|
{
|
||||||
|
string[] subDirs;
|
||||||
|
try { subDirs = Directory.GetDirectories(path); }
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($" Skipping '{path}': {ex.Message}");
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var dir in subDirs)
|
||||||
|
foreach (var bak in EnumerateBakFiles(dir))
|
||||||
|
yield return bak;
|
||||||
|
|
||||||
|
string[] bakFiles;
|
||||||
|
try { bakFiles = Directory.GetFiles(path, "*.bak"); }
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($" Skipping files in '{path}': {ex.Message}");
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var bak in bakFiles)
|
||||||
|
yield return bak;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the lowest unused .bkN path for a given .txt file (.bk1, .bk2, ...).
|
// Returns the lowest unused .bkN path for a given .txt file (.bk1, .bk2, ...).
|
||||||
|
|||||||
Reference in New Issue
Block a user