using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; #pragma warning disable CS0612 // obsolete #pragma warning disable CS0618 // Type or member is obsolete namespace BililiveRecorder.Core.Config { internal static class ConfigMapper { private static readonly Dictionary Map1to2 = new(); private static readonly Dictionary Map2to3GlobalConfig = new(); private static readonly Dictionary Map2to3RoomConfig = new(); static ConfigMapper() { // Map v1 to v2 { AddMap(Map1to2, x => x.TimingStreamRetry, x => x.TimingStreamRetry); AddMap(Map1to2, x => x.TimingStreamConnect, x => x.TimingStreamConnect); AddMap(Map1to2, x => x.TimingDanmakuRetry, x => x.TimingDanmakuRetry); AddMap(Map1to2, x => x.TimingCheckInterval, x => x.TimingCheckInterval); AddMap(Map1to2, x => x.TimingWatchdogTimeout, x => x.TimingWatchdogTimeout); AddMap(Map1to2, x => x.RecordDanmakuFlushInterval, x => x.RecordDanmakuFlushInterval); AddMap(Map1to2, x => x.Cookie, x => x.Cookie); AddMap(Map1to2, x => x.WebHookUrls, x => x.WebHookUrls); AddMap(Map1to2, x => x.LiveApiHost, x => x.LiveApiHost); AddMap(Map1to2, x => x.RecordFilenameFormat, x => x.RecordFilenameFormat); AddMap(Map1to2, x => x.CuttingMode, x => x.CuttingMode); AddMap(Map1to2, x => x.CuttingNumber, x => x.CuttingNumber); AddMap(Map1to2, x => x.RecordDanmaku, x => x.RecordDanmaku); AddMap(Map1to2, x => x.RecordDanmakuRaw, x => x.RecordDanmakuRaw); AddMap(Map1to2, x => x.RecordDanmakuSuperChat, x => x.RecordDanmakuSuperChat); AddMap(Map1to2, x => x.RecordDanmakuGift, x => x.RecordDanmakuGift); AddMap(Map1to2, x => x.RecordDanmakuGuard, x => x.RecordDanmakuGuard); } // Map v2 to v3 { Map2to3GlobalConfig = GetPropertyInfoPairs() .Where(x => x.Key.Name.StartsWith("Optional", StringComparison.Ordinal)) .ToDictionary(x => x.Key, x => x.Value); Map2to3RoomConfig = GetPropertyInfoPairs() .Where(x => x.Key.Name.StartsWith("Optional", StringComparison.Ordinal)) .ToDictionary(x => x.Key, x => x.Value); } } public static V2.ConfigV2 Map1To2(V1.ConfigV1 v1) { var def = new V1.ConfigV1(); // old default var v2 = new V2.ConfigV2(); foreach (var item in Map1to2) { var data = item.Key.GetValue(v1); if (!(data?.Equals(item.Key.GetValue(def)) ?? true)) item.Value.SetValue(v2.Global, data); } v2.Rooms = v1.RoomList.Select(x => new V2.RoomConfig { RoomId = x.Roomid, AutoRecord = x.Enabled }).ToList(); return v2; } public static V3.ConfigV3 Map2To3(V2.ConfigV2 v2) { var v3 = new V3.ConfigV3(); // 复制没有变动的房间独立设置 foreach (var v2room in v2.Rooms) { var v3room = new V3.RoomConfig(); CopyValueWithMap(Map2to3RoomConfig, v2room, v3room); v3.Rooms.Add(v3room); } // 复制没有变动的全局设置 CopyValueWithMap(Map2to3GlobalConfig, v2.Global, v3.Global); // 转换文件名格式 // 如果用户设置了自定义的文件名格式才需要转换,否则使用全局默认 if (v2.Global.HasRecordFilenameFormat && v2.Global.RecordFilenameFormat is not null) { v3.Global.FileNameRecordTemplate = v2.Global.RecordFilenameFormat .Replace("{date}", "{{ \"now\" | format_date: \"yyyyMMdd\" }}") .Replace("{time}", "{{ \"now\" | format_date: \"HHmmss\" }}") .Replace("{ms}", "{{ \"now\" | format_date: \"fff\" }}") .Replace("{random}", "{% random 3 %}") .Replace("{roomid}", "{{ roomId }}") .Replace("{title}", "{{ title }}") .Replace("{name}", "{{ name }}") .Replace("{parea}", "{{ areaParent }}") .Replace("{area}", "{{ areaChild }}") ; } return v3; } private static void CopyValueWithMap(Dictionary map, object source, object target) { foreach (var item in map) { var data = item.Key.GetValue(source); item.Value.SetValue(target, data); } } private static List> GetPropertyInfoPairs() where T1 : class where T2 : class { var result = new List>(32); var t1Type = typeof(T1); var t2Type = typeof(T2); var t1 = t1Type.GetProperties(BindingFlags.Instance | BindingFlags.Public); foreach (var p1 in t1) { var p2 = t2Type.GetProperty(p1.Name, BindingFlags.Instance | BindingFlags.Public); if (p2 != null) result.Add(new KeyValuePair(p1, p2)); } return result; } private static void AddMap(Dictionary map, Expression> keyExpr, Expression> valueExpr) { var key = GetProperty(keyExpr); var value = GetProperty(valueExpr); if ((key is null) || (value is null)) return; map.Add(key, value); } private static PropertyInfo? GetProperty(Expression> expression) => (expression.Body as MemberExpression)?.Member as PropertyInfo; } }