downloadmanager/Rule.cs
Robert Morrison 9a1838becc
EVIL COMMIT!
That one evil commit that means you've actually started development like
a real developer.
But before that you just wrote things
2023-05-12 22:58:58 +01:00

254 lines
7.3 KiB
C#

using Serilog;
using Spectre.Console;
using System.Text.Json;
using System.Text.RegularExpressions;
namespace DownloadManager;
record struct rule
{
public required string Name { get; init; }
public required PatternType Type { get; init; }
public required string Pattern { get; init; }
public required string Destination { get; init; }
}
/* PatternType is included here for ease of use */
enum PatternType
{
ExactMatch = 1,
Regex = 2,
Glob = 3,
Danbooru = 4,
}
/* Management class to make handing rules easier */
class RuleManager
{
private List<rule> _rules;
private configuration _config;
private static ImageProcessing IP = new();
public RuleManager(ref configuration config)
{
_rules = new(); /* Start with no rules and load them later */
_config = config;
loadRules();
}
// attempt to load as many rules as possible
private void loadRules()
{
string[] ruleDirectories = _config.ruleDirectories;
string[] ruleFiles = { };
Log.Information("Attempting to load rules");
if (ruleDirectories.Length == 0)
{
Log.Warning("No rule directories loaded");
return;
}
try
{
Log.Information("Attempting to find rules in {dir}", ruleDirectories.Last());
ruleFiles = Directory.GetFiles(ruleDirectories.Last()); /* Use last since it will be the users choice */
}
catch (IOException e)
{
Log.Error(e, "{path} may be a file and not a directory", ruleDirectories.Last());
}
catch (UnauthorizedAccessException e)
{
Log.Error(e, "You do not have permission to open {path}", ruleDirectories.Last());
}
catch (Exception e)
{
Log.Fatal(e, "Unexpected exception occured, Please open a GitHub issue");
Environment.Exit(70);
}
Log.Information("found {count} rules in {path}", ruleFiles.Length, ruleDirectories.Last());
Log.Debug("{@rules}", ruleFiles);
if (ruleFiles.Length == 0)
{
Log.Warning("No rules loaded");
return;
}
Log.Information("Loading rules from ruleFiles");
foreach (var file in ruleFiles)
{
try
{
Log.Information("Attempting to load rule {name}", Path.GetFileName(file));
using (var ruleStream = File.OpenText(file))
{
string json = ruleStream.ReadToEnd();
Log.Debug("{json}", json);
rule r = JsonSerializer.Deserialize<rule>(json);
Log.Information("Deserialised rule {@rule}", r);
_rules.Add(r);
}
}
catch (JsonException e)
{
Log.Error(e, "Could not deserialise JSON rule");
}
catch (UnauthorizedAccessException e)
{
Log.Error(e, "I/O error while reading rule");
}
catch (Exception e)
{
Log.Fatal(e, "Unexpected exception occurred, Please open a GitHub issue");
Environment.Exit(70);
}
}
_rules.Sort(delegate (rule x, rule y)
{
return x.Name.CompareTo(y.Name);
});
}
public bool ApplyRules()
{
if (_rules.Count() == 0)
{
Log.Warning("ApplyRules was called but there are no rules configured");
return false;
}
Log.Information("Attempting to run {count} rules, Dryrun:{dryrun}", _rules.Count(), _config.dryRun);
foreach (var rule in _rules)
{
bool result = false;
switch (rule.Type)
{
case PatternType.ExactMatch:
result = ApplyExact(rule);
break;
case PatternType.Regex:
result = ApplyRegex(rule);
break;
case PatternType.Glob:
result = ApplyGlob(rule);
break;
case PatternType.Danbooru:
result = ApplyDanbooru(rule);
break;
}
if (result)
{
Log.Information("Successfully applied rule {name}", rule.Name);
}
}
return false;
}
private bool ApplyExact(rule rule)
{
// this ruletype should only match one file
string? file = Directory.GetFiles(_config.downloadDirectory)
.Where(x => Path.GetFileName(x) == rule.Pattern)
.FirstOrDefault(defaultValue: null);
if (file is null)
{
Log.Information("Could not apply rule {name} as nothing matched {pattern}", rule.Name, rule.Pattern);
return false;
}
safeMove(file, rule.Destination);
return true;
}
private bool ApplyRegex(rule rule)
{
Regex rx = new Regex(rule.Pattern, RegexOptions.Compiled);
string[] files = Directory.GetFiles(_config.downloadDirectory)
.Where(path => rx.IsMatch(Path.GetFileName(path))) // Match against file name to make regex more logical
.ToArray();
if (files.Length == 0)
{
Log.Information("Could not apply rule {name} as nothing matched {pattern}", rule.Name, rule.Pattern);
return false;
}
foreach (var file in files)
{
safeMove(file, rule.Destination);
}
return false;
}
private bool ApplyGlob(rule rule) { return false; }
private bool ApplyDanbooru(rule rule)
{
string[] files = Directory.GetFiles(_config.downloadDirectory)
.Where(path => Path.GetFileName(path).Substring(0, 2) == "__")
.ToArray();
if (files.Length == 0)
{
Log.Information("Could not apply rule {name}, no files applicable", rule.Name);
}
foreach (var file in files)
{
string? aspect = IP.GetAspectRatioString(file);
if (aspect is null)
{
continue; // probably not an image since cannot calculate aspect ratio
}
string dest = Path.Combine(rule.Destination, aspect);
safeMove(file, dest);
}
return false;
}
// TODO: add appropriate exception avoidance/handling here
private void safeMove(string file, string targetDir)
{
Log.Information("Moving {file} to {targetdir}", file, targetDir);
string target = Path.Combine(targetDir, Path.GetFileName(file));
if (File.Exists(target))
{
Log.Warning("target {target} already exists", target);
target = Path.Combine(targetDir,
Path.GetFileNameWithoutExtension(file),
DateTime.Today.ToString("yyyy-MM-dd"),
Path.GetExtension(file));
Log.Warning("Saving as: {target}", target);
}
if (_config.dryRun) { return; }
if (_config.confirm)
{
if (!AnsiConsole.Confirm("Move file?"))
{
return;
}
}
Directory.CreateDirectory(targetDir);
File.Move(file, target);
}
}