Compare commits
2 Commits
d784357fe5
...
5269f28c41
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5269f28c41 | ||
|
|
66756b555f |
@ -21,6 +21,7 @@
|
|||||||
<PackageReference Include="Serilog" Version="4.3.0" />
|
<PackageReference Include="Serilog" Version="4.3.0" />
|
||||||
<PackageReference Include="Serilog.Enrichers.Thread" Version="4.0.0" />
|
<PackageReference Include="Serilog.Enrichers.Thread" Version="4.0.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
|
||||||
<PackageReference Include="TMDbLib" Version="2.3.0" />
|
<PackageReference Include="TMDbLib" Version="2.3.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@ -1,11 +0,0 @@
|
|||||||
namespace BangumiRenamer.Config;
|
|
||||||
|
|
||||||
public class ConfigItemAttribute : System.Attribute
|
|
||||||
{
|
|
||||||
public string Name;
|
|
||||||
|
|
||||||
public ConfigItemAttribute(string name)
|
|
||||||
{
|
|
||||||
Name = name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,115 +0,0 @@
|
|||||||
namespace BangumiRenamer.Config;
|
|
||||||
using System.Reflection;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
|
|
||||||
public class Config(string configPath)
|
|
||||||
{
|
|
||||||
private static readonly Lazy<Config> _lazy = new (() =>
|
|
||||||
{
|
|
||||||
var config = new Config("config.json");
|
|
||||||
config.ReLoad();
|
|
||||||
return config;
|
|
||||||
});
|
|
||||||
public static Config Default => _lazy.Value;
|
|
||||||
|
|
||||||
|
|
||||||
private static readonly Dictionary<Type, string> Cache = new();
|
|
||||||
private readonly Dictionary<string, object> _configObjects = new();
|
|
||||||
private readonly Dictionary<string, JObject> _configs = new();
|
|
||||||
|
|
||||||
private static string GetConfigItemName(Type type)
|
|
||||||
{
|
|
||||||
if (Cache.TryGetValue(type, out var name)) return name;
|
|
||||||
name = type.Name;
|
|
||||||
if(type.GetCustomAttribute(typeof(ConfigItemAttribute)) is ConfigItemAttribute info)
|
|
||||||
{
|
|
||||||
name = string.IsNullOrEmpty(info.Name) ? type.Name : info.Name;
|
|
||||||
}
|
|
||||||
Cache[type] = name;
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T Get<T>() where T : class, IConfigItem, new()
|
|
||||||
{
|
|
||||||
var name = GetConfigItemName(typeof(T));
|
|
||||||
if (_configObjects.TryGetValue(name, out var value))
|
|
||||||
{
|
|
||||||
return (T) value;
|
|
||||||
}
|
|
||||||
T result;
|
|
||||||
if (_configs.TryGetValue(name, out var jObject))
|
|
||||||
{
|
|
||||||
result = jObject.ToObject<T>();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result = new T();
|
|
||||||
}
|
|
||||||
_configObjects[name] = result;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset<T>() where T : class, IConfigItem, new()
|
|
||||||
{
|
|
||||||
var name = GetConfigItemName(typeof(T));
|
|
||||||
_configs.Remove(name);
|
|
||||||
_configObjects.Remove(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
_configs.Clear();
|
|
||||||
_configObjects.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ReLoad()
|
|
||||||
{
|
|
||||||
_configs.Clear();
|
|
||||||
_configObjects.Clear();
|
|
||||||
if (!File.Exists(configPath)) return;
|
|
||||||
var configJson = File.ReadAllText(configPath);
|
|
||||||
var config = JObject.Parse(configJson);
|
|
||||||
foreach (var kv in config)
|
|
||||||
{
|
|
||||||
_configs[kv.Key] = kv.Value.ToObject<JObject>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Save()
|
|
||||||
{
|
|
||||||
foreach (var config in _configObjects)
|
|
||||||
{
|
|
||||||
_configs[config.Key] = JObject.FromObject(config.Value);
|
|
||||||
}
|
|
||||||
File.WriteAllText(configPath, JsonConvert.SerializeObject(_configs, Formatting.Indented));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void CreateEmptyConfig()
|
|
||||||
{
|
|
||||||
var configItemTypes = AppDomain.CurrentDomain.GetAssemblies()
|
|
||||||
.SelectMany(assembly =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return assembly.GetTypes();
|
|
||||||
}
|
|
||||||
catch (ReflectionTypeLoadException)
|
|
||||||
{
|
|
||||||
return Array.Empty<Type>();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.Where(type => type.IsClass &&
|
|
||||||
!type.IsAbstract &&
|
|
||||||
typeof(IConfigItem).IsAssignableFrom(type))
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
var configs = new Dictionary<string, JObject>();
|
|
||||||
foreach (var type in configItemTypes)
|
|
||||||
{
|
|
||||||
var name = GetConfigItemName(type);
|
|
||||||
configs[name] = JObject.FromObject(Activator.CreateInstance(type));
|
|
||||||
}
|
|
||||||
File.WriteAllText("config_default.json", JsonConvert.SerializeObject(configs, Formatting.Indented));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
namespace BangumiRenamer.Config;
|
|
||||||
|
|
||||||
public interface IConfigItem;
|
|
||||||
@ -1,7 +1,9 @@
|
|||||||
namespace BangumiRenamer.Config;
|
using BangumiRenamer.Utils;
|
||||||
|
|
||||||
[ConfigItem("Proxy")]
|
namespace BangumiRenamer.ConfigSchema;
|
||||||
public class ProxyConfig : IConfigItem
|
|
||||||
{
|
[ConfigItem("Proxy")]
|
||||||
public string HttpProxy = "";
|
public class ProxyConfig : IConfigItem
|
||||||
|
{
|
||||||
|
public string HttpProxy = "";
|
||||||
}
|
}
|
||||||
@ -1,7 +1,9 @@
|
|||||||
namespace BangumiRenamer.Config;
|
using BangumiRenamer.Utils;
|
||||||
|
|
||||||
[ConfigItem("TMDB")]
|
namespace BangumiRenamer.Config;
|
||||||
public class TMDBConfig : IConfigItem
|
|
||||||
{
|
[ConfigItem("TMDB")]
|
||||||
public string ApiKey = "";
|
public class TMDBConfig : IConfigItem
|
||||||
|
{
|
||||||
|
public string ApiKey = "";
|
||||||
}
|
}
|
||||||
164
Src/Log/Log.cs
164
Src/Log/Log.cs
@ -1,164 +0,0 @@
|
|||||||
using Serilog.Sinks.SystemConsole.Themes;
|
|
||||||
|
|
||||||
namespace BangumiRenamer;
|
|
||||||
|
|
||||||
using Serilog;
|
|
||||||
using Serilog.Core;
|
|
||||||
using Serilog.Events;
|
|
||||||
|
|
||||||
public sealed class Log
|
|
||||||
{
|
|
||||||
private static readonly Lazy<Log> _instance = new Lazy<Log>(() => new Log());
|
|
||||||
private Logger _logger;
|
|
||||||
private bool _isInitialized = false;
|
|
||||||
private readonly object _lockObject = new object();
|
|
||||||
|
|
||||||
public static Log Instance => _instance.Value;
|
|
||||||
public static ILogger Logger => Instance._logger;
|
|
||||||
|
|
||||||
private Log() { }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 初始化日志配置
|
|
||||||
/// </summary>
|
|
||||||
public static void Initialize(Action<LoggerConfiguration> configure = null)
|
|
||||||
{
|
|
||||||
Instance.InitializeInternal(configure);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InitializeInternal(Action<LoggerConfiguration> configure = null)
|
|
||||||
{
|
|
||||||
lock (_lockObject)
|
|
||||||
{
|
|
||||||
if (_isInitialized)
|
|
||||||
{
|
|
||||||
Warn("日志系统已经初始化过");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var config = new LoggerConfiguration()
|
|
||||||
.MinimumLevel.Debug()
|
|
||||||
.WriteTo.Console(
|
|
||||||
outputTemplate: "[{Timestamp:HH:mm:ss}][{Level:u3}] {Message:lj}{NewLine}{Exception}",
|
|
||||||
theme: AnsiConsoleTheme.Sixteen
|
|
||||||
)
|
|
||||||
.Enrich.FromLogContext()
|
|
||||||
.Enrich.WithProperty("Application", "MyApp")
|
|
||||||
.Enrich.WithThreadId();
|
|
||||||
|
|
||||||
configure?.Invoke(config);
|
|
||||||
|
|
||||||
_logger = config.CreateLogger();
|
|
||||||
_isInitialized = true;
|
|
||||||
|
|
||||||
Info("日志系统初始化完成");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 静态快捷方法 - 使用更简短的名称
|
|
||||||
public static void Debug(string message, params object[] properties)
|
|
||||||
=> Instance.EnsureAndLog(LogEventLevel.Debug, message, properties);
|
|
||||||
|
|
||||||
public static void Info(string message, params object[] properties)
|
|
||||||
=> Instance.EnsureAndLog(LogEventLevel.Information, message, properties);
|
|
||||||
|
|
||||||
public static void Warn(string message, params object[] properties)
|
|
||||||
=> Instance.EnsureAndLog(LogEventLevel.Warning, message, properties);
|
|
||||||
|
|
||||||
public static void Error(string message, params object[] properties)
|
|
||||||
=> Instance.EnsureAndLog(LogEventLevel.Error, message, properties);
|
|
||||||
|
|
||||||
public static void Error(Exception ex, string message, params object[] properties)
|
|
||||||
=> Instance.EnsureAndLog(ex, message, properties);
|
|
||||||
|
|
||||||
public static void Fatal(string message, params object[] properties)
|
|
||||||
=> Instance.EnsureAndLog(LogEventLevel.Fatal, message, properties);
|
|
||||||
|
|
||||||
// 实例方法
|
|
||||||
private void EnsureAndLog(LogEventLevel level, string message, object[] properties)
|
|
||||||
{
|
|
||||||
EnsureInitialized();
|
|
||||||
_logger.Write(level, message, properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EnsureAndLog(Exception ex, string message, object[] properties)
|
|
||||||
{
|
|
||||||
EnsureInitialized();
|
|
||||||
_logger.Error(ex, message, properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 为特定类型创建Logger
|
|
||||||
/// </summary>
|
|
||||||
public static ILogger ForContext<T>()
|
|
||||||
{
|
|
||||||
Instance.EnsureInitialized();
|
|
||||||
return Instance._logger.ForContext<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 为特定源创建Logger
|
|
||||||
/// </summary>
|
|
||||||
public static ILogger ForContext(string source)
|
|
||||||
{
|
|
||||||
Instance.EnsureInitialized();
|
|
||||||
return Instance._logger.ForContext("SourceContext", source);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 开始一个带属性的日志上下文
|
|
||||||
/// </summary>
|
|
||||||
public static IDisposable BeginScope(string propertyName, object value)
|
|
||||||
{
|
|
||||||
Instance.EnsureInitialized();
|
|
||||||
return Serilog.Context.LogContext.PushProperty(propertyName, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 开始多个属性的日志上下文
|
|
||||||
/// </summary>
|
|
||||||
public static IDisposable BeginScope(params (string Name, object Value)[] properties)
|
|
||||||
{
|
|
||||||
Instance.EnsureInitialized();
|
|
||||||
var disposables = properties.Select(p =>
|
|
||||||
Serilog.Context.LogContext.PushProperty(p.Name, p.Value)).ToArray();
|
|
||||||
return new DisposableGroup(disposables);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 关闭并刷新日志
|
|
||||||
/// </summary>
|
|
||||||
public static void Close()
|
|
||||||
{
|
|
||||||
Instance._logger?.Dispose();
|
|
||||||
Serilog.Log.CloseAndFlush();
|
|
||||||
Instance._isInitialized = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EnsureInitialized()
|
|
||||||
{
|
|
||||||
if (!_isInitialized)
|
|
||||||
{
|
|
||||||
InitializeInternal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 辅助类:用于同时释放多个IDisposable
|
|
||||||
private class DisposableGroup : IDisposable
|
|
||||||
{
|
|
||||||
private readonly IDisposable[] _disposables;
|
|
||||||
|
|
||||||
public DisposableGroup(IDisposable[] disposables)
|
|
||||||
{
|
|
||||||
_disposables = disposables;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
foreach (var disposable in _disposables)
|
|
||||||
{
|
|
||||||
disposable?.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -4,6 +4,14 @@ namespace BangumiRenamer.Tools;
|
|||||||
|
|
||||||
public class EmbededResourceViewer
|
public class EmbededResourceViewer
|
||||||
{
|
{
|
||||||
|
private static readonly Lazy<Log> _lazy = new (() =>
|
||||||
|
{
|
||||||
|
var log = new Log(new Log.LogConfig());
|
||||||
|
if (!log.Init()) return null;
|
||||||
|
return log;
|
||||||
|
});
|
||||||
|
static ILog Log => _lazy.Value;
|
||||||
|
|
||||||
public static void PrintResourceNames()
|
public static void PrintResourceNames()
|
||||||
{
|
{
|
||||||
var names = ResourceLoader.GetAllResNames();
|
var names = ResourceLoader.GetAllResNames();
|
||||||
|
|||||||
@ -1,10 +1,19 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using BangumiRenamer.Utils;
|
||||||
using NativeFileDialogSharp;
|
using NativeFileDialogSharp;
|
||||||
|
|
||||||
namespace BangumiRenamer.Tools;
|
namespace BangumiRenamer.Tools;
|
||||||
|
|
||||||
public static class FolderCloner
|
public static class FolderCloner
|
||||||
{
|
{
|
||||||
|
private static readonly Lazy<Log> _lazy = new (() =>
|
||||||
|
{
|
||||||
|
var log = new Log(new Log.LogConfig());
|
||||||
|
if (!log.Init()) return null;
|
||||||
|
return log;
|
||||||
|
});
|
||||||
|
static ILog Log => _lazy.Value;
|
||||||
|
|
||||||
public static void Run()
|
public static void Run()
|
||||||
{
|
{
|
||||||
var result = Dialog.FolderPicker();
|
var result = Dialog.FolderPicker();
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
|
using BangumiRenamer.Config;
|
||||||
|
using BangumiRenamer.ConfigSchema;
|
||||||
|
|
||||||
namespace BangumiRenamer.Tools;
|
namespace BangumiRenamer.Tools;
|
||||||
|
|
||||||
using Config;
|
using Utils;
|
||||||
using TMDbLib.Client;
|
using TMDbLib.Client;
|
||||||
using Data;
|
using Data;
|
||||||
using NativeFileDialogSharp;
|
using NativeFileDialogSharp;
|
||||||
@ -10,6 +13,22 @@ using System.Text.RegularExpressions;
|
|||||||
|
|
||||||
public static class ShowCompletionChecker
|
public static class ShowCompletionChecker
|
||||||
{
|
{
|
||||||
|
private static readonly Lazy<Log> _lazyLog = new (() =>
|
||||||
|
{
|
||||||
|
var log = new Log(new Log.LogConfig());
|
||||||
|
if (!log.Init()) return null;
|
||||||
|
return log;
|
||||||
|
});
|
||||||
|
static ILog Log => _lazyLog.Value;
|
||||||
|
|
||||||
|
private static readonly Lazy<Config> _lazyConfig = new (() =>
|
||||||
|
{
|
||||||
|
var config = new Config("config.json");
|
||||||
|
if (!config.Load()) return null;
|
||||||
|
return config;
|
||||||
|
});
|
||||||
|
static IConfig Config => _lazyConfig.Value;
|
||||||
|
|
||||||
static List<Show> FindShows(string checkPath)
|
static List<Show> FindShows(string checkPath)
|
||||||
{
|
{
|
||||||
var shows = new List<Show>();
|
var shows = new List<Show>();
|
||||||
@ -56,8 +75,8 @@ public static class ShowCompletionChecker
|
|||||||
Log.Info($"Total Shows: {shows.Count}");
|
Log.Info($"Total Shows: {shows.Count}");
|
||||||
|
|
||||||
var client = new TMDbClient(
|
var client = new TMDbClient(
|
||||||
apiKey: Config.Default.Get<TMDBConfig>().ApiKey ,
|
apiKey: Config.Get<TMDBConfig>().ApiKey ,
|
||||||
proxy: new WebProxy(Config.Default.Get<ProxyConfig>().HttpProxy));
|
proxy: new WebProxy(Config.Get<ProxyConfig>().HttpProxy));
|
||||||
|
|
||||||
var output = new StringBuilder();
|
var output = new StringBuilder();
|
||||||
foreach (var t in shows)
|
foreach (var t in shows)
|
||||||
|
|||||||
161
Src/Utils/Config.cs
Normal file
161
Src/Utils/Config.cs
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Reflection;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace BangumiRenamer.Utils;
|
||||||
|
|
||||||
|
public class ConfigItemAttribute(string name) : Attribute
|
||||||
|
{
|
||||||
|
public readonly string Name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IConfigItem
|
||||||
|
{
|
||||||
|
public void BeforeSave()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AfterLoad()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IConfig
|
||||||
|
{
|
||||||
|
public T Get<T>() where T : class, IConfigItem, new();
|
||||||
|
|
||||||
|
public void Reset<T>() where T : class, IConfigItem, new();
|
||||||
|
|
||||||
|
public bool Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class Config(string configPath) : IConfig
|
||||||
|
{
|
||||||
|
public IConfig G => _G;
|
||||||
|
public Config _G { private get; set; }
|
||||||
|
|
||||||
|
private static readonly ConcurrentDictionary<Type, string> Cache = new();
|
||||||
|
private readonly ConcurrentDictionary<string, IConfigItem> _configObjects = new();
|
||||||
|
private readonly ConcurrentDictionary<string, JObject> _configs = new();
|
||||||
|
|
||||||
|
private static string GetConfigItemName(Type type)
|
||||||
|
{
|
||||||
|
if (Cache.TryGetValue(type, out var name)) return name;
|
||||||
|
name = type.Name;
|
||||||
|
if(type.GetCustomAttribute(typeof(ConfigItemAttribute)) is ConfigItemAttribute info)
|
||||||
|
{
|
||||||
|
name = string.IsNullOrEmpty(info.Name) ? type.Name : info.Name;
|
||||||
|
}
|
||||||
|
Cache[type] = name;
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T Get<T>() where T : class, IConfigItem, new()
|
||||||
|
{
|
||||||
|
var name = GetConfigItemName(typeof(T));
|
||||||
|
if (_configObjects.TryGetValue(name, out var value))
|
||||||
|
{
|
||||||
|
return (T) value;
|
||||||
|
}
|
||||||
|
T result;
|
||||||
|
if (_configs.TryGetValue(name, out var jObject))
|
||||||
|
{
|
||||||
|
result = jObject.ToObject<T>();
|
||||||
|
result.AfterLoad();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = new T();
|
||||||
|
}
|
||||||
|
_configObjects[name] = result;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset<T>() where T : class, IConfigItem, new()
|
||||||
|
{
|
||||||
|
var name = GetConfigItemName(typeof(T));
|
||||||
|
_configs.TryRemove(name, out _);
|
||||||
|
_configObjects.Remove(name, out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Load()
|
||||||
|
{
|
||||||
|
_configs.Clear();
|
||||||
|
_configObjects.Clear();
|
||||||
|
if (!File.Exists(configPath)) return false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var configJson = File.ReadAllText(configPath);
|
||||||
|
var config = JObject.Parse(configJson);
|
||||||
|
foreach (var kv in config)
|
||||||
|
{
|
||||||
|
_configs[kv.Key] = kv.Value.ToObject<JObject>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Save()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(configPath)) return false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (var config in _configObjects)
|
||||||
|
{
|
||||||
|
config.Value.BeforeSave();
|
||||||
|
_configs[config.Key] = JObject.FromObject(config.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
File.WriteAllText(configPath, JsonConvert.SerializeObject(_configs, Formatting.Indented));
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void CreateEmptyConfig()
|
||||||
|
{
|
||||||
|
var configItemTypes = AppDomain.CurrentDomain.GetAssemblies()
|
||||||
|
.SelectMany(assembly =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return assembly.GetTypes();
|
||||||
|
}
|
||||||
|
catch (ReflectionTypeLoadException)
|
||||||
|
{
|
||||||
|
return Array.Empty<Type>();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.Where(type => type.IsClass &&
|
||||||
|
!type.IsAbstract &&
|
||||||
|
typeof(IConfigItem).IsAssignableFrom(type))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var filePath = "config.json";
|
||||||
|
var config = new Config(filePath);
|
||||||
|
foreach (var type in configItemTypes)
|
||||||
|
{
|
||||||
|
var name = GetConfigItemName(type);
|
||||||
|
config._configObjects[name] = (IConfigItem) Activator.CreateInstance(type);
|
||||||
|
}
|
||||||
|
config.Save();
|
||||||
|
|
||||||
|
if (File.Exists(filePath))
|
||||||
|
{
|
||||||
|
// 使用 explorer.exe 的 /select 命令行参数
|
||||||
|
string argument = $"/select,\"{filePath}\"";
|
||||||
|
|
||||||
|
// 启动 explorer.exe 进程
|
||||||
|
Process.Start("explorer.exe", argument);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
169
Src/Utils/Log.cs
Normal file
169
Src/Utils/Log.cs
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Serilog;
|
||||||
|
using Serilog.Context;
|
||||||
|
using Serilog.Core;
|
||||||
|
using Serilog.Events;
|
||||||
|
using Serilog.Sinks.SystemConsole.Themes;
|
||||||
|
|
||||||
|
namespace BangumiRenamer.Utils;
|
||||||
|
|
||||||
|
public interface ILog
|
||||||
|
{
|
||||||
|
public void Debug(string message, [CallerFilePath] string callerFilePath = "",
|
||||||
|
[CallerMemberName] string callerMemberName = "");
|
||||||
|
|
||||||
|
public void Info(string message, [CallerFilePath] string callerFilePath = "",
|
||||||
|
[CallerMemberName] string callerMemberName = "");
|
||||||
|
|
||||||
|
public void Warn(string message, [CallerFilePath] string callerFilePath = "",
|
||||||
|
[CallerMemberName] string callerMemberName = "");
|
||||||
|
|
||||||
|
public void Error(string message, [CallerFilePath] string callerFilePath = "",
|
||||||
|
[CallerMemberName] string callerMemberName = "");
|
||||||
|
|
||||||
|
public void Error(Exception ex, string message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class Log : ILog
|
||||||
|
{
|
||||||
|
public enum LogLevel
|
||||||
|
{
|
||||||
|
Error,
|
||||||
|
Warn,
|
||||||
|
Info,
|
||||||
|
Debug,
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConfigItem("Log")]
|
||||||
|
public class LogConfig : IConfigItem
|
||||||
|
{
|
||||||
|
[JsonIgnore] public LogLevel Level = LogLevel.Debug;
|
||||||
|
[JsonProperty("Level")] private string _level;
|
||||||
|
|
||||||
|
public bool ToConsole = true;
|
||||||
|
|
||||||
|
public bool ToFile = false;
|
||||||
|
public string LogFilePath;
|
||||||
|
|
||||||
|
public void AfterLoad()
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(_level)) LogLevel.TryParse(_level, out Level);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BeforeSave()
|
||||||
|
{
|
||||||
|
_level = Level.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ILog G => _G;
|
||||||
|
public static Log _G { private get; set; }
|
||||||
|
|
||||||
|
private LogConfig _config;
|
||||||
|
|
||||||
|
private Logger _logger;
|
||||||
|
|
||||||
|
public Log(LogConfig config)
|
||||||
|
{
|
||||||
|
_config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Init()
|
||||||
|
{
|
||||||
|
if (_config == null) return false;
|
||||||
|
var config = new LoggerConfiguration();
|
||||||
|
switch (_config.Level)
|
||||||
|
{
|
||||||
|
case LogLevel.Error:
|
||||||
|
config.MinimumLevel.Error();
|
||||||
|
break;
|
||||||
|
case LogLevel.Warn:
|
||||||
|
config.MinimumLevel.Warning();
|
||||||
|
break;
|
||||||
|
case LogLevel.Debug:
|
||||||
|
config.MinimumLevel.Debug();
|
||||||
|
break;
|
||||||
|
case LogLevel.Info:
|
||||||
|
default:
|
||||||
|
config.MinimumLevel.Information();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var outputTemplate = "[{Timestamp:HH:mm:ss}][{CallerPlace}][{Level:u3}] {Message:lj}{NewLine}{Exception}";
|
||||||
|
|
||||||
|
if (_config.ToConsole)
|
||||||
|
{
|
||||||
|
config.WriteTo.Console(
|
||||||
|
outputTemplate: outputTemplate,
|
||||||
|
theme: AnsiConsoleTheme.Sixteen
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_config.ToFile)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(_config.LogFilePath))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
config.WriteTo.File(
|
||||||
|
outputTemplate: outputTemplate,
|
||||||
|
path: _config.LogFilePath
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
config.Enrich.FromLogContext()
|
||||||
|
.Enrich.WithThreadId();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger = config.CreateLogger();
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 静态快捷方法 - 使用更简短的名称
|
||||||
|
public void Debug(string message, [CallerFilePath] string callerFilePath = "", [CallerMemberName] string callerMemberName = "")
|
||||||
|
{
|
||||||
|
using (LogContext.PushProperty("CallerPlace", Path.GetFileNameWithoutExtension(callerFilePath) + "::" + callerMemberName))
|
||||||
|
{
|
||||||
|
_logger.Write(LogEventLevel.Debug, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Info(string message, [CallerFilePath] string callerFilePath = "", [CallerMemberName] string callerMemberName = "")
|
||||||
|
{
|
||||||
|
using (LogContext.PushProperty("CallerPlace", Path.GetFileNameWithoutExtension(callerFilePath) + "::" + callerMemberName))
|
||||||
|
{
|
||||||
|
_logger.Write(LogEventLevel.Information, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Warn(string message, [CallerFilePath] string callerFilePath = "", [CallerMemberName] string callerMemberName = "")
|
||||||
|
{
|
||||||
|
using (LogContext.PushProperty("CallerPlace", Path.GetFileNameWithoutExtension(callerFilePath) + "::" + callerMemberName))
|
||||||
|
{
|
||||||
|
_logger.Write(LogEventLevel.Warning, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Error(string message, [CallerFilePath] string callerFilePath = "", [CallerMemberName] string callerMemberName = "")
|
||||||
|
{
|
||||||
|
using (LogContext.PushProperty("CallerPlace", Path.GetFileNameWithoutExtension(callerFilePath) + "::" + callerMemberName))
|
||||||
|
{
|
||||||
|
_logger.Write(LogEventLevel.Error, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Error(Exception ex, string message)
|
||||||
|
=> WriteLog(ex, message);
|
||||||
|
|
||||||
|
private void WriteLog(Exception ex, string message)
|
||||||
|
{
|
||||||
|
_logger.Error(ex, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user