更新
This commit is contained in:
parent
47df98a25e
commit
cf128159d1
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,3 +3,4 @@
|
|||||||
/android/
|
/android/
|
||||||
.idea/
|
.idea/
|
||||||
data.json
|
data.json
|
||||||
|
config.json
|
||||||
@ -1,8 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Godot;
|
using Godot;
|
||||||
using Learn.Models;
|
using Learn.Models;
|
||||||
|
using Learn.Parsers;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace Learn.Component;
|
namespace Learn.Component;
|
||||||
@ -95,15 +98,22 @@ public partial class MainTreePanel : Tree
|
|||||||
|
|
||||||
private void OnMultiSelected(TreeItem item, long column, bool selected)
|
private void OnMultiSelected(TreeItem item, long column, bool selected)
|
||||||
{
|
{
|
||||||
if(selected) _selectingNodes.Add(_mapper[item]);
|
for (int i = 0; i < Columns; i++)
|
||||||
else _selectingNodes.Remove(_mapper[item]);
|
{
|
||||||
|
if (i == column ? selected : item.IsSelected(i))
|
||||||
|
{
|
||||||
|
_selectingNodes.Add(_mapper[item]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_selectingNodes.Remove(_mapper[item]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private TreeItem CreateNode(TreeItem father, Item item)
|
private TreeItem CreateNode(TreeItem father, Item item)
|
||||||
{
|
{
|
||||||
var node = CreateItem(father);
|
var node = CreateItem(father);
|
||||||
var name = Path.GetFileName(item.MainInfo["Path"]);
|
var name = Path.GetFileName(item.MainInfo[ItemFields.MainKey_Path]);
|
||||||
node.SetText(0, name);
|
node.SetText(0, name);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
@ -118,8 +128,8 @@ public partial class MainTreePanel : Tree
|
|||||||
{
|
{
|
||||||
MainInfo =
|
MainInfo =
|
||||||
{
|
{
|
||||||
["IsDir"] = isDir ? "True" : "False",
|
[ItemFields.MainKey_IsDir] = isDir ? "True" : "False",
|
||||||
["Path"] = path
|
[ItemFields.MainKey_Path] = path
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
_items.Add(path, item);
|
_items.Add(path, item);
|
||||||
|
|||||||
@ -39,15 +39,16 @@ public partial class NodeInfoEditPanel : Tree
|
|||||||
|
|
||||||
private void OnMultiSelected(TreeItem item, long column, bool selected)
|
private void OnMultiSelected(TreeItem item, long column, bool selected)
|
||||||
{
|
{
|
||||||
if (selected)
|
for (int i = 0; i < Columns; i++)
|
||||||
|
{
|
||||||
|
if (i == column ? selected : item.IsSelected(i))
|
||||||
{
|
{
|
||||||
_selectingNodes.Add(item);
|
_selectingNodes.Add(item);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
_selectingNodes.Remove(item);
|
_selectingNodes.Remove(item);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void OnItemEdited()
|
private void OnItemEdited()
|
||||||
{
|
{
|
||||||
|
|||||||
9
Config/ProxyConfig.cs
Normal file
9
Config/ProxyConfig.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
using Learn.Utils;
|
||||||
|
|
||||||
|
namespace Learn.Config;
|
||||||
|
|
||||||
|
[ConfigItem("Proxy")]
|
||||||
|
public class ProxyConfig : IConfigItem
|
||||||
|
{
|
||||||
|
public string HttpProxy = "";
|
||||||
|
}
|
||||||
1
Config/ProxyConfig.cs.uid
Normal file
1
Config/ProxyConfig.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://k75xslv0u3pm
|
||||||
9
Config/TMDBConfig.cs
Normal file
9
Config/TMDBConfig.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
using Learn.Utils;
|
||||||
|
|
||||||
|
namespace Learn.Config;
|
||||||
|
|
||||||
|
[ConfigItem("TMDB")]
|
||||||
|
public class TMDBConfig : IConfigItem
|
||||||
|
{
|
||||||
|
public string ApiKey = "";
|
||||||
|
}
|
||||||
1
Config/TMDBConfig.cs.uid
Normal file
1
Config/TMDBConfig.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://v07s3j12o0lw
|
||||||
@ -6,5 +6,6 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||||
|
<PackageReference Include="TMDbLib" Version="2.3.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
54
Main.cs
54
Main.cs
@ -1,9 +1,13 @@
|
|||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Godot;
|
using Godot;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Learn.Component;
|
using Learn.Component;
|
||||||
using Learn.Parsers;
|
using Learn.Parsers;
|
||||||
|
using Learn.Utils;
|
||||||
|
|
||||||
public partial class Main : Node
|
public partial class Main : Node
|
||||||
{
|
{
|
||||||
@ -37,6 +41,8 @@ public partial class Main : Node
|
|||||||
|
|
||||||
private bool _refreshPanels;
|
private bool _refreshPanels;
|
||||||
|
|
||||||
|
private Configs _configs;
|
||||||
|
|
||||||
public override void _Ready()
|
public override void _Ready()
|
||||||
{
|
{
|
||||||
_nodeInfoEditPanel.OnNodeInfoEdited += _mainTreePanel.UpdateColumns;
|
_nodeInfoEditPanel.OnNodeInfoEdited += _mainTreePanel.UpdateColumns;
|
||||||
@ -57,6 +63,7 @@ public partial class Main : Node
|
|||||||
_saveButton.Pressed += DoSave;
|
_saveButton.Pressed += DoSave;
|
||||||
_resetButton.Pressed += DoReset;
|
_resetButton.Pressed += DoReset;
|
||||||
_loadButton.Pressed += LoadData;
|
_loadButton.Pressed += LoadData;
|
||||||
|
_doParseButton.Text = "开始解析";
|
||||||
|
|
||||||
LoadData();
|
LoadData();
|
||||||
|
|
||||||
@ -64,6 +71,10 @@ public partial class Main : Node
|
|||||||
|
|
||||||
FoldAll();
|
FoldAll();
|
||||||
_refreshPanels = true;
|
_refreshPanels = true;
|
||||||
|
|
||||||
|
_configs = new Configs();
|
||||||
|
_configs.ConfigPath = "config.json";
|
||||||
|
_configs.Load(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LoadData()
|
private void LoadData()
|
||||||
@ -129,29 +140,54 @@ public partial class Main : Node
|
|||||||
_refreshPanels = true;
|
_refreshPanels = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DoParse()
|
private async void DoParse()
|
||||||
{
|
{
|
||||||
var root = _mainTreePanel.GetRoot();
|
var root = _mainTreePanel.GetRoot();
|
||||||
if (root == null) return;
|
if (root == null) return;
|
||||||
|
|
||||||
ItemParser parser = new NormalParser();
|
var originName = _doParseButton.Text;
|
||||||
|
_doParseButton.Disabled = true;
|
||||||
|
|
||||||
var queue = new Queue<TreeItem>();
|
ItemParser parser = new TMDBParser(_configs);
|
||||||
queue.Enqueue(root);
|
|
||||||
while (queue.Count > 0)
|
var stopwatch = Stopwatch.StartNew();
|
||||||
|
var stack = new Stack<TreeItem>();
|
||||||
|
stack.Push(root);
|
||||||
|
|
||||||
|
while (stack.Count > 0)
|
||||||
{
|
{
|
||||||
var treeItem = queue.Dequeue();
|
_doParseButton.Text = "解析中";
|
||||||
|
if (stopwatch.ElapsedMilliseconds > 2000)
|
||||||
|
{
|
||||||
|
_mainTreePanel.UpdateColumns();
|
||||||
|
stopwatch.Restart();
|
||||||
|
}
|
||||||
|
var treeItem = stack.Pop();
|
||||||
foreach (var child in treeItem.GetChildren())
|
foreach (var child in treeItem.GetChildren())
|
||||||
{
|
{
|
||||||
if(child == null) continue;
|
if(child == null) continue;
|
||||||
if (_mainTreePanel.Query(child, out var node))
|
if (_mainTreePanel.Query(child, out var node))
|
||||||
{
|
{
|
||||||
parser.DoParse(node);
|
try
|
||||||
}
|
{
|
||||||
queue.Enqueue(child);
|
if (!await parser.DoParse(node))
|
||||||
|
{
|
||||||
|
GD.PrintErr($"解析{child.GetText(0)}失败");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
GD.PrintErr($"解析{child.GetText(0)}出现异常:" + ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stack.Push(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_doParseButton.Text = originName;
|
||||||
|
_doParseButton.Disabled = false;
|
||||||
_mainTreePanel.UpdateColumns();
|
_mainTreePanel.UpdateColumns();
|
||||||
|
GD.Print("Done");
|
||||||
}
|
}
|
||||||
|
|
||||||
private const string DataPath = "data.json";
|
private const string DataPath = "data.json";
|
||||||
|
|||||||
@ -1,49 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using Learn.Models;
|
|
||||||
|
|
||||||
namespace Learn.Parsers;
|
|
||||||
|
|
||||||
public static class ItemFieldExtension
|
|
||||||
{
|
|
||||||
#region 基础信息
|
|
||||||
|
|
||||||
public static string Name(this Item item)
|
|
||||||
{
|
|
||||||
var path = item.MainInfo["Path"];
|
|
||||||
return Path.GetFileName(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsFolder(this Item item)
|
|
||||||
{
|
|
||||||
return item.MainInfo["IsDir"] == "True";
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
public static void SetGroupIfNotExist(this Item item, string group)
|
|
||||||
{
|
|
||||||
item.Info.TryAdd("Group", group);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetTitleIfNotExist(this Item item, string title)
|
|
||||||
{
|
|
||||||
item.Info.TryAdd("Title", title);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetRawTitle(this Item item, string rawTitle)
|
|
||||||
{
|
|
||||||
item.Info.TryAdd("RawTitle", rawTitle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string RawTitle(this Item item)
|
|
||||||
{
|
|
||||||
return item.Info["RawTitle"];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetSeasonIfNotExist(this Item item, string season)
|
|
||||||
{
|
|
||||||
item.Info.TryAdd("Season", season);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1 +0,0 @@
|
|||||||
uid://fo70m114nwe7
|
|
||||||
60
Parsers/ItemFields.cs
Normal file
60
Parsers/ItemFields.cs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using Learn.Models;
|
||||||
|
|
||||||
|
namespace Learn.Parsers;
|
||||||
|
|
||||||
|
public static class ItemFields
|
||||||
|
{
|
||||||
|
#region 基础信息
|
||||||
|
|
||||||
|
public static string MainKey_Path => "Path";
|
||||||
|
|
||||||
|
public static string MainKey_IsDir => "IsDir";
|
||||||
|
|
||||||
|
public static string Name(this Item item)
|
||||||
|
{
|
||||||
|
var path = item.MainInfo[MainKey_Path];
|
||||||
|
return Path.GetFileName(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsFolder(this Item item)
|
||||||
|
{
|
||||||
|
return item.MainInfo[MainKey_IsDir] == "True";
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
public static string Key_Group => "Group";
|
||||||
|
public static string Key_Title => "Title";
|
||||||
|
public static string Key_RawTitle => "RawTitle";
|
||||||
|
public static string Key_Season => "Season";
|
||||||
|
public static string Key_Year => "Year";
|
||||||
|
|
||||||
|
|
||||||
|
public static void SetGroupIfNotExist(this Item item, string group)
|
||||||
|
{
|
||||||
|
item.Info.TryAdd(Key_Group, group);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetTitleIfNotExist(this Item item, string title)
|
||||||
|
{
|
||||||
|
item.Info.TryAdd(Key_Title, title);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetRawTitleIfNotExist(this Item item, string rawTitle)
|
||||||
|
{
|
||||||
|
item.Info.TryAdd(Key_RawTitle, rawTitle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetSeasonIfNotExist(this Item item, string season)
|
||||||
|
{
|
||||||
|
item.Info.TryAdd(Key_Season, season);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetYear(this Item item, int year)
|
||||||
|
{
|
||||||
|
item.Info[Key_Year] = year.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
1
Parsers/ItemFields.cs.uid
Normal file
1
Parsers/ItemFields.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://cavjlh7k1lb6m
|
||||||
@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Learn.Models;
|
using Learn.Models;
|
||||||
|
|
||||||
namespace Learn.Parsers;
|
namespace Learn.Parsers;
|
||||||
@ -15,5 +16,5 @@ namespace Learn.Parsers;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public interface ItemParser
|
public interface ItemParser
|
||||||
{
|
{
|
||||||
public bool DoParse(TreeNode node);
|
public Task<bool> DoParse(TreeNode node);
|
||||||
}
|
}
|
||||||
@ -1,61 +0,0 @@
|
|||||||
using System.Linq;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using Learn.Models;
|
|
||||||
|
|
||||||
namespace Learn.Parsers;
|
|
||||||
|
|
||||||
public class NormalParser : ItemParser
|
|
||||||
{
|
|
||||||
private string ParseSeason(string season)
|
|
||||||
{
|
|
||||||
if (int.TryParse(season, out _)) return season;
|
|
||||||
|
|
||||||
switch (season)
|
|
||||||
{
|
|
||||||
case "零": return "0";
|
|
||||||
case "一": return "1";
|
|
||||||
case "二": return "2";
|
|
||||||
case "三": return "3";
|
|
||||||
case "四": return "4";
|
|
||||||
case "五": return "5";
|
|
||||||
case "六": return "6";
|
|
||||||
case "七": return "7";
|
|
||||||
case "八": return "8";
|
|
||||||
case "九": return "9";
|
|
||||||
}
|
|
||||||
return season;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool DoParse(TreeNode node)
|
|
||||||
{
|
|
||||||
var item = node.Info;
|
|
||||||
var name = item.Name();
|
|
||||||
var matches = Regex.Matches(name, @"[^\[\]_]+").Select(match => match.Value).ToArray();
|
|
||||||
|
|
||||||
if (matches.Length == 1)
|
|
||||||
{
|
|
||||||
node.Info.SetRawTitle(matches[0]);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (matches.Length >= 2)
|
|
||||||
{
|
|
||||||
node.Info.SetGroupIfNotExist(matches[0]);
|
|
||||||
|
|
||||||
var title = matches[1].Trim();
|
|
||||||
var match = Regex.Match(title, "(.+)第(.+)季");
|
|
||||||
if (match.Success)
|
|
||||||
{
|
|
||||||
node.Info.SetSeasonIfNotExist(ParseSeason(match.Groups[2].Value.Trim()));
|
|
||||||
node.Info.SetRawTitle(match.Groups[1].Value.Trim());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
node.Info.SetRawTitle(title);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1 +0,0 @@
|
|||||||
uid://bfixayy5gpwd6
|
|
||||||
141
Parsers/TMDBParser.cs
Normal file
141
Parsers/TMDBParser.cs
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Godot;
|
||||||
|
using Learn.Config;
|
||||||
|
using Learn.Models;
|
||||||
|
using Learn.Utils;
|
||||||
|
using TMDbLib.Client;
|
||||||
|
using TMDbLib.Objects.Search;
|
||||||
|
|
||||||
|
namespace Learn.Parsers;
|
||||||
|
|
||||||
|
public class TMDBParser(Configs configs) : ItemParser
|
||||||
|
{
|
||||||
|
private readonly Dictionary<string, SearchTv> _cache = new();
|
||||||
|
|
||||||
|
private TMDbClient _client;
|
||||||
|
|
||||||
|
private async Task<SearchTv> QueryTMDB(string title)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(title)) return null;
|
||||||
|
|
||||||
|
if (_cache.TryGetValue(title, out var result))
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
var results = (await GetTMDbClient().SearchTvShowAsync(title, language: "zh-CN")).Results;
|
||||||
|
result = results.FirstOrDefault();
|
||||||
|
_cache[title] = result;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TMDbClient GetTMDbClient()
|
||||||
|
{
|
||||||
|
if (_client != null) return _client;
|
||||||
|
|
||||||
|
var apiKey = configs.Get<TMDBConfig>().ApiKey;
|
||||||
|
var proxy = configs.Get<ProxyConfig>().HttpProxy;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(proxy))
|
||||||
|
{
|
||||||
|
_client = new TMDbClient(apiKey);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_client = new TMDbClient(apiKey , proxy: new WebProxy(proxy));
|
||||||
|
}
|
||||||
|
|
||||||
|
return _client;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string ParseSeason(string season)
|
||||||
|
{
|
||||||
|
if (int.TryParse(season, out _)) return season;
|
||||||
|
|
||||||
|
switch (season)
|
||||||
|
{
|
||||||
|
case "零": return "0";
|
||||||
|
case "一": return "1";
|
||||||
|
case "二": return "2";
|
||||||
|
case "三": return "3";
|
||||||
|
case "四": return "4";
|
||||||
|
case "五": return "5";
|
||||||
|
case "六": return "6";
|
||||||
|
case "七": return "7";
|
||||||
|
case "八": return "8";
|
||||||
|
case "九": return "9";
|
||||||
|
}
|
||||||
|
return season;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool DoRawParse(TreeNode node)
|
||||||
|
{
|
||||||
|
var item = node.Info;
|
||||||
|
var name = item.Name();
|
||||||
|
var matches = Regex.Matches(name, @"[^\[\]_]+").Select(match => match.Value).ToArray();
|
||||||
|
|
||||||
|
if (matches.Length == 0) return false;
|
||||||
|
|
||||||
|
if (matches.Length == 1)
|
||||||
|
{
|
||||||
|
node.Info.SetRawTitleIfNotExist(matches[0]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matches.Length >= 2)
|
||||||
|
{
|
||||||
|
node.Info.SetGroupIfNotExist(matches[0]);
|
||||||
|
|
||||||
|
var title = matches[1].Trim();
|
||||||
|
var match = Regex.Match(title, "(.+)第(.+)季");
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
node.Info.SetSeasonIfNotExist(ParseSeason(match.Groups[2].Value.Trim()));
|
||||||
|
node.Info.SetRawTitleIfNotExist(match.Groups[1].Value.Trim());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
node.Info.SetRawTitleIfNotExist(title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> DoParse(TreeNode node)
|
||||||
|
{
|
||||||
|
if (!node.TryGetValue(ItemFields.Key_RawTitle, out var rawTitle, out _))
|
||||||
|
{
|
||||||
|
if (!DoRawParse(node))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
node.TryGetValue(ItemFields.Key_RawTitle, out rawTitle, out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(rawTitle))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = await QueryTMDB(rawTitle);
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
GD.PrintErr($"找不到对应的TV:{rawTitle}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.FirstAirDate != null)
|
||||||
|
{
|
||||||
|
node.Info.SetYear(result.FirstAirDate.Value.Year);
|
||||||
|
}
|
||||||
|
|
||||||
|
node.Info.SetTitleIfNotExist(result.Name);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
1
Parsers/TMDBParser.cs.uid
Normal file
1
Parsers/TMDBParser.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://bw1sbx8ay4qsw
|
||||||
184
Utils/Configs.cs
Normal file
184
Utils/Configs.cs
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace Learn.Utils;
|
||||||
|
|
||||||
|
public class ConfigItemAttribute(string name) : Attribute
|
||||||
|
{
|
||||||
|
public readonly string Name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IConfigItem
|
||||||
|
{
|
||||||
|
public void OnBeforeSave()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnAfterLoad()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class Configs
|
||||||
|
{
|
||||||
|
private static readonly ConcurrentDictionary<Type, string> Cache = new();
|
||||||
|
private readonly ConcurrentDictionary<string, IConfigItem> _configObjects = new();
|
||||||
|
private readonly ConcurrentDictionary<string, JObject> _configs = new();
|
||||||
|
|
||||||
|
public string ConfigPath { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取配置项对象
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">配置项类</typeparam>
|
||||||
|
/// <remarks>获取的配置项对象是单例的</remarks>
|
||||||
|
/// <returns>配置项对象</returns>
|
||||||
|
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.OnAfterLoad();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = new T();
|
||||||
|
}
|
||||||
|
_configObjects[name] = result;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 重置某个配置项。重置完后再取就是一个新的配置项
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">配置项类</typeparam>
|
||||||
|
public void Reset<T>() where T : class, IConfigItem, new()
|
||||||
|
{
|
||||||
|
var name = GetConfigItemName(typeof(T));
|
||||||
|
_configs.TryRemove(name, out _);
|
||||||
|
_configObjects.Remove(name, out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 清空当前配置并重新从文件中加载配置
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="createDefaultIfNotExist">当不存在配置文件路径创建默认配置</param>
|
||||||
|
/// <returns>加载是否成功。无论是否加载成功,当前配置都会被清空</returns>
|
||||||
|
public bool Load(bool createDefaultIfNotExist)
|
||||||
|
{
|
||||||
|
_configs.Clear();
|
||||||
|
_configObjects.Clear();
|
||||||
|
if (string.IsNullOrEmpty(ConfigPath))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!File.Exists(ConfigPath))
|
||||||
|
{
|
||||||
|
if (!createDefaultIfNotExist) return false;
|
||||||
|
if (!CreateEmptyConfig(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将当前配置保存到文件中
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public bool Save()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(ConfigPath)) return false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (var config in _configObjects)
|
||||||
|
{
|
||||||
|
config.Value.OnBeforeSave();
|
||||||
|
_configs[config.Key] = JObject.FromObject(config.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
File.WriteAllText(ConfigPath, JsonConvert.SerializeObject(_configs, Formatting.Indented));
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool CreateEmptyConfig(string filePath)
|
||||||
|
{
|
||||||
|
var configItemTypes = AppDomain.CurrentDomain.GetAssemblies()
|
||||||
|
.SelectMany(assembly =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return assembly.GetTypes();
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return Array.Empty<Type>();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.Where(type => type.IsClass &&
|
||||||
|
!type.IsAbstract &&
|
||||||
|
typeof(IConfigItem).IsAssignableFrom(type))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var configs = new Dictionary<string, IConfigItem>();
|
||||||
|
foreach (var type in configItemTypes)
|
||||||
|
{
|
||||||
|
var name = GetConfigItemName(type);
|
||||||
|
var item = (IConfigItem) Activator.CreateInstance(type);
|
||||||
|
item.OnBeforeSave();
|
||||||
|
configs[name] = item;
|
||||||
|
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.WriteAllText(filePath, JsonConvert.SerializeObject(configs, Formatting.Indented));
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
1
Utils/Configs.cs.uid
Normal file
1
Utils/Configs.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://b8ta3kj0ksopd
|
||||||
Loading…
x
Reference in New Issue
Block a user