feat: add -revert mode to restore *.bak back to *.txt
Inverse of -output. For each blog with a TTFolderPath, restores every *.bak over its *.txt, first preserving the current *.txt as the next-free *.bkN, then consuming the *.bak. Confirms before running and supports an optional single-blog filter. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace URLNotesGrabberCORE
|
||||
{
|
||||
// Inverse of OutputMode. For each Blog with a TTFolderPath, restores every *.bak back
|
||||
// to its *.txt, first preserving the current *.txt as the next-free *.bkN. Consumes
|
||||
// the *.bak (File.Move). Filesystem-only; reads the DB only to enumerate folders.
|
||||
public static class RevertMode
|
||||
{
|
||||
public static int Run(IConfiguration config, string? blogFilter = null)
|
||||
{
|
||||
DataAccess.EnsureTTFileHelperColumnsExist();
|
||||
|
||||
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))
|
||||
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;
|
||||
}
|
||||
|
||||
Console.Write($"WARNING: This will restore {totalBakFiles} .bak file(s) over their .txt files across {blogs.Count} blog folder(s). " +
|
||||
$"Current .txt files are preserved as the next-free .bkN. Continue? (yes/no): ");
|
||||
string? response = Console.ReadLine();
|
||||
if (string.IsNullOrWhiteSpace(response) || !response.Equals("yes", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Console.WriteLine("Operation cancelled.");
|
||||
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;
|
||||
|
||||
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)
|
||||
{
|
||||
try
|
||||
{
|
||||
string txtPath = Path.ChangeExtension(bakFile, ".txt");
|
||||
|
||||
if (File.Exists(txtPath))
|
||||
{
|
||||
string bkPath = NextFreeBkPath(txtPath);
|
||||
File.Move(txtPath, bkPath);
|
||||
backedUp++;
|
||||
Console.WriteLine($" Backed up {Path.GetFileName(txtPath)} -> {Path.GetFileName(bkPath)}");
|
||||
}
|
||||
|
||||
File.Move(bakFile, txtPath);
|
||||
restored++;
|
||||
Console.WriteLine($" Restored {Path.GetFileName(bakFile)} -> {Path.GetFileName(txtPath)}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($" Error reverting {Path.GetFileName(bakFile)}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
return (restored, backedUp);
|
||||
}
|
||||
|
||||
// Returns the lowest unused .bkN path for a given .txt file (.bk1, .bk2, ...).
|
||||
private static string NextFreeBkPath(string txtFile)
|
||||
{
|
||||
for (int n = 1; ; n++)
|
||||
{
|
||||
string candidate = Path.ChangeExtension(txtFile, $".bk{n}");
|
||||
if (!File.Exists(candidate)) return candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user