2025-04-03 16:22:31 +08:00
|
|
|
|
using System.Net;
|
2025-02-27 16:58:21 +08:00
|
|
|
|
using System.Xml;
|
|
|
|
|
|
using FluentScheduler;
|
2025-04-03 16:22:31 +08:00
|
|
|
|
using FreeSql;
|
|
|
|
|
|
using FreeSql.DataAnnotations;
|
2025-02-27 16:58:21 +08:00
|
|
|
|
using Interface.Jobs;
|
|
|
|
|
|
using Microsoft.Extensions.Configuration;
|
|
|
|
|
|
using Microsoft.Extensions.Logging;
|
2025-04-03 16:22:31 +08:00
|
|
|
|
using Newtonsoft.Json.Linq;
|
2025-02-27 16:58:21 +08:00
|
|
|
|
|
|
|
|
|
|
namespace Service.Jobs;
|
|
|
|
|
|
|
|
|
|
|
|
public class ChineseNfoRegistry : Registry, IChineseNfoRegistry
|
|
|
|
|
|
{
|
2025-04-03 16:22:31 +08:00
|
|
|
|
private readonly HttpClient _apiClient;
|
|
|
|
|
|
private readonly HttpClient _imageClient;
|
2025-02-27 16:58:21 +08:00
|
|
|
|
private readonly IConfiguration _configuration;
|
|
|
|
|
|
private readonly ILogger<ChineseNfoRegistry> _logger;
|
2025-04-03 16:22:31 +08:00
|
|
|
|
private readonly IFreeSql _freeSql;
|
2025-02-27 16:58:21 +08:00
|
|
|
|
|
|
|
|
|
|
public ChineseNfoRegistry(IConfiguration configuration, ILogger<ChineseNfoRegistry> logger)
|
|
|
|
|
|
{
|
|
|
|
|
|
_configuration = configuration;
|
|
|
|
|
|
_logger = logger;
|
2025-03-19 14:03:33 +08:00
|
|
|
|
|
2025-03-06 17:25:10 +08:00
|
|
|
|
var httpClientHandler = new HttpClientHandler();
|
2025-03-19 14:03:33 +08:00
|
|
|
|
|
2025-02-27 16:58:21 +08:00
|
|
|
|
var proxyAddress = _configuration["ChineseNfo:HttpProxy"];
|
2025-03-19 14:03:33 +08:00
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(proxyAddress) == false)
|
2025-02-27 16:58:21 +08:00
|
|
|
|
{
|
2025-03-06 17:25:10 +08:00
|
|
|
|
httpClientHandler.Proxy = string.IsNullOrEmpty(proxyAddress) ? null : new WebProxy(proxyAddress, false);
|
|
|
|
|
|
httpClientHandler.UseProxy = !string.IsNullOrEmpty(proxyAddress);
|
|
|
|
|
|
}
|
2025-02-27 16:58:21 +08:00
|
|
|
|
|
2025-04-03 16:22:31 +08:00
|
|
|
|
_apiClient = new HttpClient(httpClientHandler);
|
|
|
|
|
|
_apiClient.BaseAddress = new Uri("http://api.themoviedb.org");
|
|
|
|
|
|
|
|
|
|
|
|
_imageClient = new HttpClient(httpClientHandler);
|
|
|
|
|
|
_imageClient.BaseAddress = new Uri("http://image.tmdb.org");
|
|
|
|
|
|
|
|
|
|
|
|
_freeSql = new FreeSqlBuilder()
|
|
|
|
|
|
.UseConnectionString(FreeSql.DataType.Sqlite, _configuration["ChineseNfo:ConnectionString"])
|
|
|
|
|
|
.UseAutoSyncStructure(true)
|
|
|
|
|
|
.Build();
|
2025-03-19 14:03:33 +08:00
|
|
|
|
|
2025-02-27 16:58:21 +08:00
|
|
|
|
Schedule(() => Job(true, true)).ToRunEvery(1).Days();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public async void Job(bool ignoreLocked, bool ignoreCompleted)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
await JobExecute(ignoreLocked, ignoreCompleted);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogError(e, "ChineseNfoRegistry.Job() error");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-03 16:22:31 +08:00
|
|
|
|
private async Task JobExecute(
|
|
|
|
|
|
bool ignoreLocked,
|
|
|
|
|
|
bool ignoreCompleted,
|
|
|
|
|
|
string[]? includeFields = null)
|
2025-02-27 16:58:21 +08:00
|
|
|
|
{
|
|
|
|
|
|
var tvFolder = _configuration["ChineseNfo:TvFolder"];
|
2025-04-03 16:22:31 +08:00
|
|
|
|
|
2025-02-27 16:58:21 +08:00
|
|
|
|
if (string.IsNullOrEmpty(tvFolder))
|
|
|
|
|
|
{
|
2025-04-03 16:22:31 +08:00
|
|
|
|
_logger.LogError("ChineseNfoRegistry.Job() tvFolder is null or empty");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* 1. 支持指定字段
|
|
|
|
|
|
* 2. 接口数据保留
|
|
|
|
|
|
* 3. 支持语言降级到繁体
|
|
|
|
|
|
* 4. 支持演员名称翻译
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
var fields =
|
|
|
|
|
|
includeFields ??
|
|
|
|
|
|
[
|
|
|
|
|
|
"tv.title",
|
|
|
|
|
|
"tv.plot",
|
|
|
|
|
|
"tv.outline",
|
|
|
|
|
|
"tv.poster",
|
|
|
|
|
|
"tv.genre",
|
|
|
|
|
|
"tv.actor.name",
|
|
|
|
|
|
"tv.actor.role",
|
|
|
|
|
|
"season.title",
|
|
|
|
|
|
"season.plot",
|
|
|
|
|
|
"episode.title",
|
|
|
|
|
|
"episode.plot",
|
|
|
|
|
|
"episode.actor.name",
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
var tvNfos = Directory.GetFiles(tvFolder, "tvshow.nfo", SearchOption.AllDirectories);
|
|
|
|
|
|
|
|
|
|
|
|
if (tvNfos.Length == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogError("ChineseNfoRegistry.Job() tvNfo is null or empty");
|
2025-02-27 16:58:21 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-03 16:22:31 +08:00
|
|
|
|
foreach (var tv in tvNfos)
|
|
|
|
|
|
{
|
|
|
|
|
|
await HandleTv(tv);
|
|
|
|
|
|
}
|
2025-03-19 14:03:33 +08:00
|
|
|
|
|
2025-02-27 16:58:21 +08:00
|
|
|
|
|
2025-04-03 16:22:31 +08:00
|
|
|
|
async Task<string[]?> HandleTv(string tvNfo)
|
2025-02-27 16:58:21 +08:00
|
|
|
|
{
|
2025-04-03 16:22:31 +08:00
|
|
|
|
var nfoContent = File.ReadAllText(tvNfo);
|
2025-02-27 16:58:21 +08:00
|
|
|
|
|
|
|
|
|
|
var tvXml = new XmlDocument();
|
|
|
|
|
|
tvXml.LoadXml(nfoContent);
|
|
|
|
|
|
var uniqueIdNode = tvXml.SelectSingleNode("//uniqueid[@type='tmdb']");
|
2025-04-03 16:22:31 +08:00
|
|
|
|
|
2025-02-27 16:58:21 +08:00
|
|
|
|
if (uniqueIdNode == null)
|
|
|
|
|
|
{
|
2025-04-03 16:22:31 +08:00
|
|
|
|
_logger.LogError("ChineseNfoRegistry.Job() uniqueIdNode is null");
|
|
|
|
|
|
return null;
|
2025-02-27 16:58:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!int.TryParse(uniqueIdNode.InnerText, out var tmdbId))
|
|
|
|
|
|
{
|
2025-04-03 16:22:31 +08:00
|
|
|
|
_logger.LogError("ChineseNfoRegistry.Job() tmdbId is null");
|
|
|
|
|
|
return null;
|
2025-02-27 16:58:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-03 16:22:31 +08:00
|
|
|
|
var a = await GetTmdbTv(tvNfo, tmdbId);
|
2025-04-03 11:28:26 +08:00
|
|
|
|
|
2025-04-03 16:22:31 +08:00
|
|
|
|
_logger.LogInformation("ChineseNfoRegistry.Job() tmdbId: {tmdbId}, name: {name}", tmdbId,a?["name"]?.ToString());
|
|
|
|
|
|
return [];
|
2025-04-03 11:28:26 +08:00
|
|
|
|
}
|
2025-04-03 16:22:31 +08:00
|
|
|
|
}
|
2025-02-27 16:58:21 +08:00
|
|
|
|
|
2025-04-03 16:22:31 +08:00
|
|
|
|
private async Task<JObject?> GetTmdbTv(
|
|
|
|
|
|
string path,
|
|
|
|
|
|
int tmdbId,
|
|
|
|
|
|
TheMovieDbLanguage language = TheMovieDbLanguage.ZhCn)
|
|
|
|
|
|
{
|
|
|
|
|
|
var record = await _freeSql.Select<TheMovieDbRecord>()
|
|
|
|
|
|
.Where(x => x.UniqueId == tmdbId.ToString())
|
|
|
|
|
|
.FirstAsync();
|
2025-02-27 16:58:21 +08:00
|
|
|
|
|
2025-04-03 16:22:31 +08:00
|
|
|
|
if (record != null)
|
2025-04-03 11:28:26 +08:00
|
|
|
|
{
|
2025-04-03 16:22:31 +08:00
|
|
|
|
return JObject.Parse(record.Json);
|
2025-04-03 11:28:26 +08:00
|
|
|
|
}
|
2025-02-27 16:58:21 +08:00
|
|
|
|
|
2025-04-03 16:22:31 +08:00
|
|
|
|
record = new TheMovieDbRecord
|
2025-04-03 11:28:26 +08:00
|
|
|
|
{
|
2025-04-03 16:22:31 +08:00
|
|
|
|
Type = TheMovieDbType.Tv,
|
|
|
|
|
|
UniqueId = tmdbId.ToString(),
|
|
|
|
|
|
Language = language
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-04-03 16:39:32 +08:00
|
|
|
|
string tvUrl = "/3/tv/{0}?api_key=e28e1bc408db7adefc8bacce225c5085&language=";
|
|
|
|
|
|
|
|
|
|
|
|
if(language == TheMovieDbLanguage.ZhCn)
|
|
|
|
|
|
{
|
|
|
|
|
|
tvUrl += "zh-CN";
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
tvUrl += "zh-TW";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-03 11:28:26 +08:00
|
|
|
|
var requestUrl = string.Format(tvUrl, tmdbId);
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2025-04-03 16:22:31 +08:00
|
|
|
|
var response = await _apiClient.GetAsync(requestUrl);
|
|
|
|
|
|
|
2025-04-03 11:28:26 +08:00
|
|
|
|
if (!response.IsSuccessStatusCode)
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine($"{requestUrl} & {path} & {response.StatusCode}");
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-03 16:22:31 +08:00
|
|
|
|
var str = await response.Content.ReadAsStringAsync();
|
2025-03-19 14:03:33 +08:00
|
|
|
|
|
2025-04-03 16:22:31 +08:00
|
|
|
|
var json = JObject.Parse(str);
|
|
|
|
|
|
var name = json["name"];
|
|
|
|
|
|
// 判断是否包含中文字符
|
|
|
|
|
|
var isChinese = name != null && name.ToString().Any(c => c >= 0x4E00 && c <= 0x9FA5);
|
|
|
|
|
|
if (language == TheMovieDbLanguage.ZhCn && !isChinese)
|
2025-02-27 16:58:21 +08:00
|
|
|
|
{
|
2025-04-03 16:29:16 +08:00
|
|
|
|
_logger.LogWarning("ChineseNfoRegistry.GetTmdbTv() name: {name}, 无中文文字, 降级到繁体字", name);
|
2025-04-03 16:22:31 +08:00
|
|
|
|
// 降级到繁体
|
|
|
|
|
|
return await GetTmdbTv(path, tmdbId, TheMovieDbLanguage.ZhTw);
|
2025-02-27 16:58:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-03 16:22:31 +08:00
|
|
|
|
record.Json = str;
|
|
|
|
|
|
_freeSql.Insert(record);
|
|
|
|
|
|
|
|
|
|
|
|
return json;
|
2025-02-27 16:58:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
|
{
|
2025-03-19 15:27:00 +08:00
|
|
|
|
Console.WriteLine($"{requestUrl} & {path} \r {e}");
|
2025-02-27 16:58:21 +08:00
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-04-03 16:22:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public class TheMovieDbRecord
|
|
|
|
|
|
{
|
|
|
|
|
|
public long Id { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
public TheMovieDbType Type { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
public string UniqueId { get; set; } = string.Empty;
|
|
|
|
|
|
|
|
|
|
|
|
public TheMovieDbLanguage Language { get; set; }
|
|
|
|
|
|
|
2025-04-03 16:25:06 +08:00
|
|
|
|
[Column(DbType = "text")]
|
|
|
|
|
|
public string Json { get; set; } = string.Empty;
|
2025-04-03 16:22:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public enum TheMovieDbType
|
|
|
|
|
|
{
|
|
|
|
|
|
Tv = 1,
|
|
|
|
|
|
Season = 2,
|
|
|
|
|
|
Episode = 3,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public enum TheMovieDbLanguage
|
|
|
|
|
|
{
|
|
|
|
|
|
ZhCn = 1,
|
|
|
|
|
|
ZhTw = 2
|
2025-02-27 16:58:21 +08:00
|
|
|
|
}
|