2025-04-03 16:22:31 +08:00
|
|
|
|
using System.Net;
|
2025-04-16 18:26:31 +08:00
|
|
|
|
using System.Text.RegularExpressions;
|
2025-02-27 16:58:21 +08:00
|
|
|
|
using System.Xml;
|
2025-04-22 15:57:58 +08:00
|
|
|
|
using Core;
|
2025-02-27 16:58:21 +08:00
|
|
|
|
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-04-16 18:26:31 +08:00
|
|
|
|
Schedule(() => Job(ignoreLocked: true, ignoreCompleted: true)).ToRunEvery(1).Days();
|
2025-02-27 16:58:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-22 15:24:21 +08:00
|
|
|
|
public void Job(
|
2025-04-17 19:00:56 +08:00
|
|
|
|
string? path = null,
|
2025-04-22 15:57:58 +08:00
|
|
|
|
int? seasonNumber = null,
|
|
|
|
|
|
int? episodeNumber = null,
|
2025-04-16 18:26:31 +08:00
|
|
|
|
bool ignoreLocked = false,
|
|
|
|
|
|
bool ignoreCompleted = false
|
|
|
|
|
|
)
|
2025-02-27 16:58:21 +08:00
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2025-04-22 15:24:21 +08:00
|
|
|
|
var lockAll = "lock-all";
|
|
|
|
|
|
lock (lockAll)
|
|
|
|
|
|
{
|
|
|
|
|
|
var lockKey = $"{path}-{seasonNumber}-{episodeNumber}";
|
|
|
|
|
|
|
|
|
|
|
|
lock (lockKey)
|
|
|
|
|
|
{
|
|
|
|
|
|
JobExecute(path, seasonNumber, episodeNumber, ignoreLocked, ignoreCompleted).Wait();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-27 16:58:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
|
{
|
2025-04-22 15:57:58 +08:00
|
|
|
|
_logger.LogError(e, "error");
|
|
|
|
|
|
WxNotify.SendCommonAsync($"ChineseNfoRegistry.Job() 执行失败 {e.Message}").Wait();
|
2025-02-27 16:58:21 +08:00
|
|
|
|
}
|
2025-04-22 17:30:11 +08:00
|
|
|
|
finally
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogInformation("ChineseNfoRegistry.Job() 执行结束");
|
|
|
|
|
|
}
|
2025-02-27 16:58:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-03 16:22:31 +08:00
|
|
|
|
private async Task JobExecute(
|
2025-04-17 19:00:56 +08:00
|
|
|
|
string? requestPath = null,
|
2025-04-22 15:57:58 +08:00
|
|
|
|
int? requestSeasonNumber = null,
|
|
|
|
|
|
int? requestEpisodeNumber = null,
|
2025-04-17 18:46:41 +08:00
|
|
|
|
bool ignoreLocked = false,
|
|
|
|
|
|
bool ignoreCompleted = false)
|
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-22 15:57:58 +08:00
|
|
|
|
_logger.LogError("tvFolder is null or empty");
|
2025-04-03 16:22:31 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* 2. 接口数据保留
|
|
|
|
|
|
* 3. 支持语言降级到繁体
|
|
|
|
|
|
* 4. 支持演员名称翻译
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
var tvNfos = Directory.GetFiles(tvFolder, "tvshow.nfo", SearchOption.AllDirectories);
|
|
|
|
|
|
|
|
|
|
|
|
if (tvNfos.Length == 0)
|
|
|
|
|
|
{
|
2025-04-22 15:57:58 +08:00
|
|
|
|
_logger.LogError("tvNfo is null or empty");
|
2025-02-27 16:58:21 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-22 15:57:58 +08:00
|
|
|
|
var ctn = new ChineseNfoContent();
|
2025-04-17 19:00:56 +08:00
|
|
|
|
|
2025-04-03 16:22:31 +08:00
|
|
|
|
foreach (var tv in tvNfos)
|
|
|
|
|
|
{
|
2025-04-22 15:57:58 +08:00
|
|
|
|
ctn.tvNfoPath = tv;
|
2025-04-22 15:24:21 +08:00
|
|
|
|
|
2025-04-22 16:17:09 +08:00
|
|
|
|
if (await HandleTv() == false)
|
|
|
|
|
|
{
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
2025-04-21 17:43:48 +08:00
|
|
|
|
|
2025-04-22 17:33:44 +08:00
|
|
|
|
var sseasons = Directory.GetDirectories(Path.GetDirectoryName(tv) ?? string.Empty, "Season *", SearchOption.AllDirectories);
|
2025-04-22 17:43:09 +08:00
|
|
|
|
var sseasonsNumbers = sseasons.Select(x => x.Split("Season ")[1]).Select(int.Parse).ToList();
|
2025-04-16 18:26:31 +08:00
|
|
|
|
|
2025-04-22 17:33:44 +08:00
|
|
|
|
foreach (var seasonNumber in sseasonsNumbers)
|
2025-04-21 13:23:42 +08:00
|
|
|
|
{
|
2025-04-22 17:33:44 +08:00
|
|
|
|
ctn.seasonNfoPath = Path.Combine(Path.GetDirectoryName(tv) ?? string.Empty, $"Season {seasonNumber}", "season.nfo");
|
2025-04-21 13:23:42 +08:00
|
|
|
|
|
2025-04-22 17:33:44 +08:00
|
|
|
|
if (File.Exists(ctn.seasonNfoPath) && await HandleSeason() == false)
|
2025-04-16 18:54:25 +08:00
|
|
|
|
{
|
2025-04-22 16:17:09 +08:00
|
|
|
|
continue;
|
2025-04-16 18:54:25 +08:00
|
|
|
|
}
|
2025-04-22 15:57:58 +08:00
|
|
|
|
|
2025-04-16 18:26:31 +08:00
|
|
|
|
var episodeNfos = Directory
|
2025-04-22 17:33:44 +08:00
|
|
|
|
.GetFiles(Path.GetDirectoryName(ctn.seasonNfoPath) ?? string.Empty, "*.nfo", SearchOption.AllDirectories)
|
2025-04-16 18:26:31 +08:00
|
|
|
|
.Where(x => !x.EndsWith("season.nfo"))
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var episode in episodeNfos)
|
|
|
|
|
|
{
|
2025-04-22 15:57:58 +08:00
|
|
|
|
ctn.episodeNfoPath = episode;
|
2025-04-22 15:24:21 +08:00
|
|
|
|
|
2025-04-22 17:43:09 +08:00
|
|
|
|
_logger.LogInformation("ctn.episodeNfoPath: " + ctn.episodeNfoPath);
|
|
|
|
|
|
|
2025-04-22 15:57:58 +08:00
|
|
|
|
await HandleEpisode();
|
2025-04-16 18:26:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-04-03 16:22:31 +08:00
|
|
|
|
}
|
2025-03-19 14:03:33 +08:00
|
|
|
|
|
2025-02-27 16:58:21 +08:00
|
|
|
|
|
2025-04-22 16:17:09 +08:00
|
|
|
|
async Task<bool> HandleTv()
|
2025-02-27 16:58:21 +08:00
|
|
|
|
{
|
2025-04-22 15:57:58 +08:00
|
|
|
|
if (!string.IsNullOrEmpty(requestPath))
|
|
|
|
|
|
{
|
|
|
|
|
|
var latestPath = Path.GetFileName(requestPath) ?? string.Empty;
|
|
|
|
|
|
|
|
|
|
|
|
if (!ctn.tvNfoPath.Contains(latestPath))
|
|
|
|
|
|
{
|
2025-04-22 16:17:09 +08:00
|
|
|
|
return false;
|
2025-04-22 15:57:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-22 16:17:09 +08:00
|
|
|
|
_logger.LogInformation("开始处理 TV");
|
|
|
|
|
|
|
2025-04-22 15:57:58 +08:00
|
|
|
|
var nfoContent = File.ReadAllText(ctn.tvNfoPath);
|
2025-02-27 16:58:21 +08:00
|
|
|
|
|
|
|
|
|
|
var tvXml = new XmlDocument();
|
|
|
|
|
|
tvXml.LoadXml(nfoContent);
|
2025-04-16 18:26:31 +08:00
|
|
|
|
|
2025-04-22 16:17:09 +08:00
|
|
|
|
var uniqueIdNode = tvXml.SelectSingleNode("//uniqueid[@type='tmdb']");
|
2025-04-16 18:26:31 +08:00
|
|
|
|
|
2025-04-22 16:17:09 +08:00
|
|
|
|
if (uniqueIdNode == null)
|
2025-04-16 18:26:31 +08:00
|
|
|
|
{
|
2025-04-22 16:17:09 +08:00
|
|
|
|
_logger.LogError("uniqueIdNode is null");
|
|
|
|
|
|
return false;
|
2025-04-16 18:26:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-22 16:17:09 +08:00
|
|
|
|
if (!int.TryParse(uniqueIdNode.InnerText, out var tmdbId))
|
2025-04-16 18:26:31 +08:00
|
|
|
|
{
|
2025-04-22 16:17:09 +08:00
|
|
|
|
_logger.LogError("tmdbId is null");
|
|
|
|
|
|
return false;
|
2025-04-16 18:26:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-22 16:17:09 +08:00
|
|
|
|
ctn.tvId = tmdbId;
|
2025-04-03 16:22:31 +08:00
|
|
|
|
|
2025-04-22 16:17:09 +08:00
|
|
|
|
var isLockedNode = tvXml.SelectSingleNode("//locked");
|
|
|
|
|
|
|
|
|
|
|
|
if (isLockedNode != null && isLockedNode.InnerText == "true" && ignoreLocked)
|
2025-02-27 16:58:21 +08:00
|
|
|
|
{
|
2025-04-22 16:17:09 +08:00
|
|
|
|
_logger.LogInformation("tvNfo is locked");
|
|
|
|
|
|
return true;
|
2025-02-27 16:58:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-22 16:17:09 +08:00
|
|
|
|
var isCompletedNode = tvXml.SelectSingleNode("//completed");
|
|
|
|
|
|
|
|
|
|
|
|
if (isCompletedNode != null && isCompletedNode.InnerText == "true" && ignoreCompleted)
|
2025-02-27 16:58:21 +08:00
|
|
|
|
{
|
2025-04-22 16:17:09 +08:00
|
|
|
|
_logger.LogInformation("tvNfo is completed");
|
|
|
|
|
|
return true;
|
2025-04-16 18:26:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-22 15:57:58 +08:00
|
|
|
|
var tvInfo = await GetTmdbTv(ctn.tvNfoPath, tmdbId);
|
2025-04-16 18:26:31 +08:00
|
|
|
|
|
|
|
|
|
|
if (tvInfo == null)
|
|
|
|
|
|
{
|
2025-04-22 15:57:58 +08:00
|
|
|
|
_logger.LogError("tvInfo is null");
|
2025-04-22 16:17:09 +08:00
|
|
|
|
return true;
|
2025-04-16 18:26:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (tvInfo["name"] != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var titleNode = tvXml.SelectSingleNode("//title");
|
|
|
|
|
|
if (titleNode != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
titleNode.InnerXml = $"<![CDATA[{tvInfo["name"]}]]>";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var sorttitleNode = tvXml.SelectSingleNode("//sorttitle");
|
|
|
|
|
|
if (sorttitleNode != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
sorttitleNode.InnerXml = $"<![CDATA[{tvInfo["name"]}]]>";
|
|
|
|
|
|
}
|
2025-04-16 18:54:25 +08:00
|
|
|
|
|
2025-04-22 15:57:58 +08:00
|
|
|
|
_logger.LogInformation("tvInfo: {tvInfo}", tvInfo["name"]);
|
2025-04-16 18:26:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (tvInfo["overview"] != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var plotNode = tvXml.SelectSingleNode("//plot");
|
|
|
|
|
|
if (plotNode != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
plotNode.InnerXml = $"<![CDATA[{tvInfo["overview"]}]]>";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var outlineNode = tvXml.SelectSingleNode("//outline");
|
|
|
|
|
|
if (outlineNode != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
outlineNode.InnerXml = $"<![CDATA[{tvInfo["overview"]}]]>";
|
|
|
|
|
|
}
|
2025-04-16 18:54:25 +08:00
|
|
|
|
|
2025-04-22 15:57:58 +08:00
|
|
|
|
_logger.LogInformation("tvInfo: {tvInfo}", tvInfo["overview"]);
|
2025-04-16 18:26:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (tvInfo["poster_path"] != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var posterPath = tvInfo["poster_path"]!.ToString();
|
2025-04-22 15:57:58 +08:00
|
|
|
|
var image = await GetTmdbImage(ctn.tvNfoPath, posterPath);
|
2025-04-16 18:26:31 +08:00
|
|
|
|
if (image != null)
|
|
|
|
|
|
{
|
2025-04-22 15:57:58 +08:00
|
|
|
|
var imagePath = Path.Combine(Path.GetDirectoryName(ctn.tvNfoPath) ?? string.Empty, "poster" + Path.GetExtension(posterPath));
|
2025-04-16 18:26:31 +08:00
|
|
|
|
await File.WriteAllBytesAsync(imagePath, image);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var actors = tvXml.SelectNodes("//actor");
|
|
|
|
|
|
if (actors != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (XmlNode actor in actors)
|
|
|
|
|
|
{
|
|
|
|
|
|
await HandleActor(actor);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-22 15:57:58 +08:00
|
|
|
|
_logger.LogInformation("tmdbId: {tmdbId}, name: {name}", tmdbId, tvInfo["name"]?.ToString());
|
2025-04-16 18:26:31 +08:00
|
|
|
|
|
|
|
|
|
|
if (isLockedNode != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
isLockedNode.InnerText = "true";
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2025-04-21 13:23:42 +08:00
|
|
|
|
isLockedNode = tvXml.CreateElement("locked");
|
|
|
|
|
|
tvXml.DocumentElement?.AppendChild(isLockedNode);
|
|
|
|
|
|
isLockedNode.InnerText = "true";
|
2025-04-16 18:26:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (isCompletedNode != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
isCompletedNode.InnerText = "true";
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2025-04-21 13:23:42 +08:00
|
|
|
|
isCompletedNode = tvXml.CreateElement("completed");
|
|
|
|
|
|
tvXml.DocumentElement?.AppendChild(isCompletedNode);
|
|
|
|
|
|
isCompletedNode.InnerText = "true";
|
2025-04-16 18:26:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 保存
|
2025-04-22 15:57:58 +08:00
|
|
|
|
tvXml.Save(ctn.tvNfoPath);
|
2025-04-22 16:17:09 +08:00
|
|
|
|
|
|
|
|
|
|
return true;
|
2025-04-16 18:26:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-22 16:17:09 +08:00
|
|
|
|
async Task<bool> HandleSeason()
|
2025-04-16 18:26:31 +08:00
|
|
|
|
{
|
2025-04-22 16:17:09 +08:00
|
|
|
|
if (string.IsNullOrEmpty(ctn.seasonNfoPath))
|
2025-04-16 18:26:31 +08:00
|
|
|
|
{
|
2025-04-22 15:57:58 +08:00
|
|
|
|
_logger.LogError("seasonNfoPath is null or empty");
|
2025-04-22 16:17:09 +08:00
|
|
|
|
return false;
|
2025-04-16 18:26:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-22 15:57:58 +08:00
|
|
|
|
var nfoContent = File.ReadAllText(ctn.seasonNfoPath);
|
2025-04-16 18:26:31 +08:00
|
|
|
|
|
|
|
|
|
|
var seasonXml = new XmlDocument();
|
|
|
|
|
|
seasonXml.LoadXml(nfoContent);
|
|
|
|
|
|
|
2025-04-22 16:17:09 +08:00
|
|
|
|
var seasonNumberNode = seasonXml.SelectSingleNode("//seasonnumber");
|
2025-04-16 18:26:31 +08:00
|
|
|
|
|
2025-04-22 16:17:09 +08:00
|
|
|
|
if (seasonNumberNode == null)
|
2025-04-16 18:26:31 +08:00
|
|
|
|
{
|
2025-04-22 16:17:09 +08:00
|
|
|
|
_logger.LogError("seasonNumberNode is null");
|
|
|
|
|
|
return false;
|
2025-04-16 18:26:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-22 16:17:09 +08:00
|
|
|
|
if (!int.TryParse(seasonNumberNode.InnerText, out var seasonNumber))
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogError("seasonNumber is null");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2025-04-16 18:26:31 +08:00
|
|
|
|
|
2025-04-22 16:17:09 +08:00
|
|
|
|
ctn.seasonNumber = seasonNumber;
|
|
|
|
|
|
|
|
|
|
|
|
if (requestSeasonNumber != null && requestSeasonNumber != seasonNumber)
|
2025-04-16 18:26:31 +08:00
|
|
|
|
{
|
2025-04-22 16:17:09 +08:00
|
|
|
|
return false;
|
2025-04-16 18:26:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-22 16:17:09 +08:00
|
|
|
|
_logger.LogInformation("开始处理 Season");
|
2025-04-16 18:26:31 +08:00
|
|
|
|
|
2025-04-22 16:17:09 +08:00
|
|
|
|
var isLockedNode = seasonXml.SelectSingleNode("//locked");
|
|
|
|
|
|
|
|
|
|
|
|
if (isLockedNode != null && isLockedNode.InnerText == "true" && ignoreLocked)
|
2025-04-16 18:26:31 +08:00
|
|
|
|
{
|
2025-04-22 16:17:09 +08:00
|
|
|
|
_logger.LogInformation("seasonNfo is locked");
|
|
|
|
|
|
return true;
|
2025-04-16 18:26:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-22 16:17:09 +08:00
|
|
|
|
var isCompletedNode = seasonXml.SelectSingleNode("//completed");
|
|
|
|
|
|
|
|
|
|
|
|
if (isCompletedNode != null && isCompletedNode.InnerText == "true" && ignoreCompleted)
|
2025-04-16 18:26:31 +08:00
|
|
|
|
{
|
2025-04-22 16:17:09 +08:00
|
|
|
|
_logger.LogInformation("seasonNfo is completed");
|
|
|
|
|
|
return true;
|
2025-04-16 18:26:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-22 15:57:58 +08:00
|
|
|
|
var seasonInfo = await GetTmdbSeason(ctn);
|
2025-04-16 18:26:31 +08:00
|
|
|
|
|
|
|
|
|
|
if (seasonInfo == null)
|
|
|
|
|
|
{
|
2025-04-22 15:57:58 +08:00
|
|
|
|
_logger.LogError("seasonInfo is null");
|
2025-04-22 16:17:09 +08:00
|
|
|
|
return true;
|
2025-04-16 18:26:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var titleNode = seasonXml.SelectSingleNode("//sorttitle");
|
|
|
|
|
|
if (titleNode != null && seasonInfo["name"] != null)
|
|
|
|
|
|
{
|
2025-04-22 15:57:58 +08:00
|
|
|
|
_logger.LogInformation("seasonInfo: {seasonInfo}", seasonInfo["name"]);
|
2025-04-16 18:26:31 +08:00
|
|
|
|
titleNode.InnerXml = $"<![CDATA[{seasonInfo["name"]}]]>";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var plotNode = seasonXml.SelectSingleNode("//plot");
|
|
|
|
|
|
if (plotNode != null && seasonInfo["overview"] != null)
|
|
|
|
|
|
{
|
2025-04-22 15:57:58 +08:00
|
|
|
|
_logger.LogInformation("seasonInfo: {seasonInfo}", seasonInfo["overview"]);
|
2025-04-16 18:26:31 +08:00
|
|
|
|
plotNode.InnerXml = $"<![CDATA[{seasonInfo["overview"]}]]>";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var outlineNode = seasonXml.SelectSingleNode("//outline");
|
|
|
|
|
|
if (outlineNode != null && seasonInfo["overview"] != null)
|
|
|
|
|
|
{
|
2025-04-22 15:57:58 +08:00
|
|
|
|
_logger.LogInformation("seasonInfo: {seasonInfo}", seasonInfo["overview"]);
|
2025-04-16 18:26:31 +08:00
|
|
|
|
outlineNode.InnerXml = $"<![CDATA[{seasonInfo["overview"]}]]>";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var poster_path = seasonInfo["poster_path"]?.ToString();
|
|
|
|
|
|
if (poster_path != null)
|
|
|
|
|
|
{
|
2025-04-22 15:57:58 +08:00
|
|
|
|
var image = await GetTmdbImage(ctn.seasonNfoPath, poster_path);
|
2025-04-16 18:26:31 +08:00
|
|
|
|
if (image != null)
|
|
|
|
|
|
{
|
2025-04-22 15:57:58 +08:00
|
|
|
|
var imagePath = Path.Combine(Path.GetDirectoryName(ctn.tvNfoPath) ?? string.Empty, "season" + seasonNumber.ToString("D2") + "-poster" + Path.GetExtension(poster_path));
|
2025-04-16 18:26:31 +08:00
|
|
|
|
await File.WriteAllBytesAsync(imagePath, image);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (isLockedNode != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
isLockedNode.InnerText = "true";
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2025-04-21 13:23:42 +08:00
|
|
|
|
isLockedNode = seasonXml.CreateElement("locked");
|
|
|
|
|
|
seasonXml.DocumentElement?.AppendChild(isLockedNode);
|
|
|
|
|
|
isLockedNode.InnerText = "true";
|
2025-02-27 16:58:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-16 18:26:31 +08:00
|
|
|
|
if (isCompletedNode != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
isCompletedNode.InnerText = "true";
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2025-04-21 13:23:42 +08:00
|
|
|
|
isCompletedNode = seasonXml.CreateElement("completed");
|
|
|
|
|
|
seasonXml.DocumentElement?.AppendChild(isCompletedNode);
|
|
|
|
|
|
isCompletedNode.InnerText = "true";
|
2025-04-16 18:26:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-22 15:57:58 +08:00
|
|
|
|
seasonXml.Save(ctn.seasonNfoPath);
|
2025-04-22 16:17:09 +08:00
|
|
|
|
|
|
|
|
|
|
return true;
|
2025-04-16 18:26:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-22 15:57:58 +08:00
|
|
|
|
async Task HandleEpisode()
|
2025-04-16 18:26:31 +08:00
|
|
|
|
{
|
2025-04-22 15:57:58 +08:00
|
|
|
|
if (string.IsNullOrEmpty(ctn.episodeNfoPath))
|
2025-04-16 18:26:31 +08:00
|
|
|
|
{
|
2025-04-22 15:57:58 +08:00
|
|
|
|
_logger.LogError("episodeNfoPath is null or empty");
|
2025-04-16 18:26:31 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-22 15:57:58 +08:00
|
|
|
|
var nfoContent = File.ReadAllText(ctn.episodeNfoPath);
|
2025-04-16 18:26:31 +08:00
|
|
|
|
|
|
|
|
|
|
var episodeXml = new XmlDocument();
|
|
|
|
|
|
episodeXml.LoadXml(nfoContent);
|
|
|
|
|
|
|
|
|
|
|
|
var episodeNumberNode = episodeXml.SelectSingleNode("//episode");
|
|
|
|
|
|
|
|
|
|
|
|
if (episodeNumberNode == null)
|
|
|
|
|
|
{
|
2025-04-22 15:57:58 +08:00
|
|
|
|
_logger.LogError("episodeNumberNode is null");
|
2025-04-16 18:26:31 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!int.TryParse(episodeNumberNode.InnerText, out var episodeNumber))
|
|
|
|
|
|
{
|
2025-04-22 15:57:58 +08:00
|
|
|
|
_logger.LogError("episodeNumber is null");
|
2025-04-16 18:26:31 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-22 15:57:58 +08:00
|
|
|
|
ctn.episodeNumber = episodeNumber;
|
|
|
|
|
|
|
2025-04-22 16:17:09 +08:00
|
|
|
|
|
2025-04-17 19:00:56 +08:00
|
|
|
|
if (!string.IsNullOrEmpty(requestPath))
|
|
|
|
|
|
{
|
2025-04-22 15:57:58 +08:00
|
|
|
|
if (requestSeasonNumber != null)
|
2025-04-17 19:00:56 +08:00
|
|
|
|
{
|
2025-04-22 15:57:58 +08:00
|
|
|
|
if (requestEpisodeNumber != null)
|
2025-04-17 19:00:56 +08:00
|
|
|
|
{
|
2025-04-22 15:57:58 +08:00
|
|
|
|
if (episodeNumber != requestEpisodeNumber)
|
2025-04-17 19:00:56 +08:00
|
|
|
|
{
|
2025-04-21 17:27:19 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-04-17 19:00:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-22 16:17:09 +08:00
|
|
|
|
_logger.LogInformation("开始处理 Episode");
|
|
|
|
|
|
|
|
|
|
|
|
var isLockedNode = episodeXml.SelectSingleNode("//locked");
|
|
|
|
|
|
|
|
|
|
|
|
if (isLockedNode != null && isLockedNode.InnerText == "true" && ignoreLocked)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogInformation("episodeNfo is locked");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var isCompletedNode = episodeXml.SelectSingleNode("//completed");
|
|
|
|
|
|
|
|
|
|
|
|
if (isCompletedNode != null && isCompletedNode.InnerText == "true" && ignoreCompleted)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogInformation("episodeNfo is completed");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-22 15:57:58 +08:00
|
|
|
|
var episodeInfo = await GetTmdbEpisode(ctn);
|
2025-04-16 18:26:31 +08:00
|
|
|
|
|
|
|
|
|
|
if (episodeInfo == null)
|
|
|
|
|
|
{
|
2025-04-22 15:57:58 +08:00
|
|
|
|
_logger.LogError("episodeInfo is null");
|
2025-04-16 18:26:31 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var titleNode = episodeXml.SelectSingleNode("//title");
|
|
|
|
|
|
if (titleNode != null && episodeInfo["name"] != null)
|
|
|
|
|
|
{
|
2025-04-22 15:57:58 +08:00
|
|
|
|
_logger.LogInformation("episodeInfo: {episodeInfo}", episodeInfo["name"]);
|
2025-04-16 18:26:31 +08:00
|
|
|
|
titleNode.InnerXml = $"<![CDATA[{episodeInfo["name"]}]]>";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var plotNode = episodeXml.SelectSingleNode("//plot");
|
|
|
|
|
|
if (plotNode != null && episodeInfo["overview"] != null)
|
|
|
|
|
|
{
|
2025-04-22 15:57:58 +08:00
|
|
|
|
_logger.LogInformation("episodeInfo: {episodeInfo}", episodeInfo["overview"]);
|
2025-04-16 18:26:31 +08:00
|
|
|
|
plotNode.InnerXml = $"<![CDATA[{episodeInfo["overview"]}]]>";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var outlineNode = episodeXml.SelectSingleNode("//outline");
|
|
|
|
|
|
if (outlineNode != null && episodeInfo["overview"] != null)
|
|
|
|
|
|
{
|
2025-04-22 15:57:58 +08:00
|
|
|
|
_logger.LogInformation("episodeInfo: {episodeInfo}", episodeInfo["overview"]);
|
2025-04-16 18:26:31 +08:00
|
|
|
|
outlineNode.InnerXml = $"<![CDATA[{episodeInfo["overview"]}]]>";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var actors = episodeXml.SelectNodes("//actor");
|
|
|
|
|
|
if (actors != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (XmlNode actor in actors)
|
|
|
|
|
|
{
|
|
|
|
|
|
await HandleActor(actor);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-22 15:57:58 +08:00
|
|
|
|
_logger.LogInformation("tmdbId: {tmdbId}, name: {name}", ctn.tvId, episodeInfo["name"]?.ToString());
|
2025-04-16 18:26:31 +08:00
|
|
|
|
|
|
|
|
|
|
if (isLockedNode != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
isLockedNode.InnerText = "true";
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2025-04-21 13:23:42 +08:00
|
|
|
|
isLockedNode = episodeXml.CreateElement("locked");
|
|
|
|
|
|
episodeXml.DocumentElement?.AppendChild(isLockedNode);
|
|
|
|
|
|
isLockedNode.InnerText = "true";
|
2025-04-16 18:26:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (isCompletedNode != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
isCompletedNode.InnerText = "true";
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2025-04-21 13:23:42 +08:00
|
|
|
|
isCompletedNode = episodeXml.CreateElement("completed");
|
|
|
|
|
|
episodeXml.DocumentElement?.AppendChild(isCompletedNode);
|
|
|
|
|
|
isCompletedNode.InnerText = "true";
|
2025-04-16 18:26:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 保存
|
2025-04-22 15:57:58 +08:00
|
|
|
|
episodeXml.Save(ctn.episodeNfoPath);
|
2025-04-16 18:26:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async Task<string?> GetTmdbPersonName(
|
|
|
|
|
|
string path,
|
|
|
|
|
|
int? tmdbId = null,
|
|
|
|
|
|
string? name = null
|
|
|
|
|
|
)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (tmdbId != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var person = await GetTmdbPerson(path, tmdbId.Value);
|
|
|
|
|
|
return person?["name"]?.ToString() ?? name;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(name) == false)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 如果名称是中文直接返回
|
|
|
|
|
|
if (Regex.IsMatch(name, "^[\u4e00-\u9fa5]+$"))
|
|
|
|
|
|
{
|
|
|
|
|
|
return name;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var person = await GetTmdbPersonSearch(path, name);
|
|
|
|
|
|
return person?["name"]?.ToString() ?? name;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async Task HandleActor(XmlNode actor)
|
|
|
|
|
|
{
|
|
|
|
|
|
var nameNode = actor.SelectSingleNode("name");
|
|
|
|
|
|
var tmdbIdNode = actor.SelectSingleNode("tmdbid");
|
|
|
|
|
|
if (nameNode != null || tmdbIdNode != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var name = await GetTmdbPersonName(
|
|
|
|
|
|
tvFolder,
|
|
|
|
|
|
int.TryParse(tmdbIdNode?.InnerText, out var actorTmdbId) ? actorTmdbId : null,
|
|
|
|
|
|
nameNode?.InnerText
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
if (name == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (nameNode != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
nameNode.InnerXml = $"<![CDATA[{name}]]>";
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
actor.AppendChild(new XmlDocument().CreateElement("name"));
|
|
|
|
|
|
nameNode = actor.SelectSingleNode("name");
|
|
|
|
|
|
nameNode!.InnerXml = $"<![CDATA[{name}]]>";
|
|
|
|
|
|
}
|
2025-04-16 18:54:25 +08:00
|
|
|
|
|
2025-04-22 15:57:58 +08:00
|
|
|
|
_logger.LogInformation("actor: {actor}", name);
|
2025-04-16 18:26:31 +08:00
|
|
|
|
}
|
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,
|
2025-04-16 18:26:31 +08:00
|
|
|
|
int tmdbId)
|
2025-04-03 16:22:31 +08:00
|
|
|
|
{
|
|
|
|
|
|
var record = await _freeSql.Select<TheMovieDbRecord>()
|
2025-04-16 18:26:31 +08:00
|
|
|
|
.Where(x => x.Type == TheMovieDbType.Tv)
|
2025-04-03 16:22:31 +08:00
|
|
|
|
.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,
|
2025-04-16 18:26:31 +08:00
|
|
|
|
UniqueId = tmdbId.ToString()
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
string tvUrl = "/3/tv/{0}?api_key=e28e1bc408db7adefc8bacce225c5085&language=zh-CN";
|
|
|
|
|
|
var requestUrl = string.Format(tvUrl, tmdbId);
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
var response = await _apiClient.GetAsync(requestUrl);
|
|
|
|
|
|
|
|
|
|
|
|
if (!response.IsSuccessStatusCode)
|
|
|
|
|
|
{
|
2025-04-17 18:46:41 +08:00
|
|
|
|
_logger.LogError("ChineseNfoRegistry.GetTmdbTv() 接口调用失败 {requestUrl} & {path} & {response.StatusCode}", requestUrl, path, response.StatusCode);
|
2025-04-16 18:26:31 +08:00
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var str = await response.Content.ReadAsStringAsync();
|
|
|
|
|
|
|
|
|
|
|
|
var json = JObject.Parse(str);
|
|
|
|
|
|
record.Json = str;
|
|
|
|
|
|
await _freeSql.Insert(record).ExecuteIdentityAsync();
|
|
|
|
|
|
|
2025-04-16 18:54:25 +08:00
|
|
|
|
_logger.LogInformation("ChineseNfoRegistry.GetTmdbTv() 接口调用, 休眠 1S");
|
|
|
|
|
|
await Task.Delay(1000);
|
2025-04-16 18:26:31 +08:00
|
|
|
|
|
|
|
|
|
|
return json;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
|
{
|
2025-04-17 18:46:41 +08:00
|
|
|
|
_logger.LogError("ChineseNfoRegistry.GetTmdbTv() 接口调用失败 {requestUrl} & {path} \r {e}", requestUrl, path, e);
|
2025-04-16 18:26:31 +08:00
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private async Task<JObject?> GetTmdbPerson(
|
|
|
|
|
|
string path,
|
|
|
|
|
|
int tmdbId)
|
|
|
|
|
|
{
|
|
|
|
|
|
var record = await _freeSql.Select<TheMovieDbRecord>()
|
|
|
|
|
|
.Where(x => x.Type == TheMovieDbType.Person)
|
|
|
|
|
|
.Where(x => x.UniqueId == tmdbId.ToString())
|
|
|
|
|
|
.FirstAsync();
|
|
|
|
|
|
|
|
|
|
|
|
if (record != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return JObject.Parse(record.Json);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
record = new TheMovieDbRecord
|
|
|
|
|
|
{
|
|
|
|
|
|
Type = TheMovieDbType.Person,
|
2025-04-03 16:22:31 +08:00
|
|
|
|
UniqueId = tmdbId.ToString(),
|
2025-04-16 18:26:31 +08:00
|
|
|
|
Language = TheMovieDbLanguage.ZhCn
|
2025-04-03 16:22:31 +08:00
|
|
|
|
};
|
2025-04-16 18:26:31 +08:00
|
|
|
|
|
|
|
|
|
|
var requestUrl = string.Format("/3/person/{0}?api_key=e28e1bc408db7adefc8bacce225c5085&language=zh-CN", tmdbId);
|
|
|
|
|
|
|
|
|
|
|
|
try
|
2025-04-03 16:39:32 +08:00
|
|
|
|
{
|
2025-04-16 18:26:31 +08:00
|
|
|
|
var response = await _apiClient.GetAsync(requestUrl);
|
|
|
|
|
|
|
|
|
|
|
|
if (!response.IsSuccessStatusCode)
|
|
|
|
|
|
{
|
2025-04-17 18:46:41 +08:00
|
|
|
|
_logger.LogError("ChineseNfoRegistry.GetTmdbPerson() 接口调用失败 {requestUrl} & {path} & {response.StatusCode}", requestUrl, path, response.StatusCode);
|
2025-04-16 18:26:31 +08:00
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var str = await response.Content.ReadAsStringAsync();
|
|
|
|
|
|
|
|
|
|
|
|
var json = JObject.Parse(str);
|
|
|
|
|
|
|
|
|
|
|
|
record.Json = str;
|
|
|
|
|
|
await _freeSql.Insert(record).ExecuteIdentityAsync();
|
|
|
|
|
|
|
2025-04-16 18:54:25 +08:00
|
|
|
|
_logger.LogInformation("ChineseNfoRegistry.GetTmdbPerson() 接口调用, 休眠 1S");
|
|
|
|
|
|
await Task.Delay(1000);
|
2025-04-16 18:26:31 +08:00
|
|
|
|
|
|
|
|
|
|
return json;
|
2025-04-03 16:39:32 +08:00
|
|
|
|
}
|
2025-04-16 18:26:31 +08:00
|
|
|
|
catch (Exception e)
|
2025-04-03 16:39:32 +08:00
|
|
|
|
{
|
2025-04-17 18:46:41 +08:00
|
|
|
|
_logger.LogError("ChineseNfoRegistry.GetTmdbPerson() 接口调用失败 {requestUrl} & {path} \r {e}", requestUrl, path, e);
|
2025-04-16 18:26:31 +08:00
|
|
|
|
return null;
|
2025-04-03 16:39:32 +08:00
|
|
|
|
}
|
2025-04-16 18:26:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private async Task<JObject?> GetTmdbPersonSearch(
|
|
|
|
|
|
string path,
|
|
|
|
|
|
string name)
|
|
|
|
|
|
{
|
|
|
|
|
|
var record = await _freeSql.Select<TheMovieDbRecord>()
|
|
|
|
|
|
.Where(x => x.Type == TheMovieDbType.Person)
|
|
|
|
|
|
.Where(x => x.UniqueId == name)
|
|
|
|
|
|
.FirstAsync();
|
|
|
|
|
|
|
|
|
|
|
|
if (record != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return JObject.Parse(record.Json);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
record = new TheMovieDbRecord
|
|
|
|
|
|
{
|
|
|
|
|
|
Type = TheMovieDbType.Person,
|
|
|
|
|
|
UniqueId = name
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
var requestUrl = string.Format("/3/search/person?api_key=e28e1bc408db7adefc8bacce225c5085&language=zh-CN&query={0}", name);
|
2025-04-03 11:28:26 +08:00
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
{
|
2025-04-17 18:46:41 +08:00
|
|
|
|
_logger.LogError("ChineseNfoRegistry.GetTmdbPersonSearch() 接口调用失败 {requestUrl} & {path} & {response.StatusCode}", requestUrl, path, response.StatusCode);
|
2025-04-03 11:28:26 +08:00
|
|
|
|
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);
|
2025-04-16 18:26:31 +08:00
|
|
|
|
|
|
|
|
|
|
var results = json["results"];
|
|
|
|
|
|
|
|
|
|
|
|
if (results != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (var result in results.Cast<JObject>())
|
|
|
|
|
|
{
|
|
|
|
|
|
record.Json = result.ToString();
|
|
|
|
|
|
await _freeSql.Insert(record).ExecuteIdentityAsync();
|
|
|
|
|
|
|
2025-04-16 18:54:25 +08:00
|
|
|
|
_logger.LogInformation("ChineseNfoRegistry.GetTmdbPersonSearch() 接口调用, 休眠 1S");
|
|
|
|
|
|
await Task.Delay(1000);
|
2025-04-16 18:26:31 +08:00
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
|
{
|
2025-04-17 18:46:41 +08:00
|
|
|
|
_logger.LogError("ChineseNfoRegistry.GetTmdbPersonSearch() 接口调用失败 {requestUrl} & {path} \r {e}", requestUrl, path, e);
|
2025-04-16 18:26:31 +08:00
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private async Task<byte[]?> GetTmdbImage(
|
|
|
|
|
|
string path,
|
|
|
|
|
|
string posterPath)
|
|
|
|
|
|
{
|
|
|
|
|
|
var requestUrl = string.Format("/t/p/w1280/{0}", posterPath);
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
var response = await _imageClient.GetAsync(requestUrl);
|
|
|
|
|
|
|
|
|
|
|
|
if (!response.IsSuccessStatusCode)
|
2025-02-27 16:58:21 +08:00
|
|
|
|
{
|
2025-04-17 18:46:41 +08:00
|
|
|
|
_logger.LogError("ChineseNfoRegistry.GetTmdbImage() 接口调用失败 {requestUrl} & {path} & {response.StatusCode}", requestUrl, path, response.StatusCode);
|
2025-04-16 18:26:31 +08:00
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return await response.Content.ReadAsByteArrayAsync();
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
|
{
|
2025-04-17 18:46:41 +08:00
|
|
|
|
_logger.LogError("ChineseNfoRegistry.GetTmdbImage() 接口调用失败 {requestUrl} & {path} \r {e}", requestUrl, path, e);
|
2025-04-16 18:26:31 +08:00
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-22 15:57:58 +08:00
|
|
|
|
private async Task<JObject?> GetTmdbSeason(ChineseNfoContent ctn)
|
2025-04-16 18:26:31 +08:00
|
|
|
|
{
|
2025-04-22 15:57:58 +08:00
|
|
|
|
if (ctn.seasonNumber == 0 || string.IsNullOrEmpty(ctn.seasonNfoPath))
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogError("seasonNfoPath is null or empty");
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var uniqueId = $"{ctn.tvId}-{ctn.seasonNumber}";
|
2025-04-16 18:26:31 +08:00
|
|
|
|
|
|
|
|
|
|
var record = await _freeSql.Select<TheMovieDbRecord>()
|
|
|
|
|
|
.Where(x => x.Type == TheMovieDbType.Season)
|
|
|
|
|
|
.Where(x => x.UniqueId == uniqueId)
|
|
|
|
|
|
.FirstAsync();
|
|
|
|
|
|
|
|
|
|
|
|
if (record != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return JObject.Parse(record.Json);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
record = new TheMovieDbRecord
|
|
|
|
|
|
{
|
|
|
|
|
|
Type = TheMovieDbType.Season,
|
|
|
|
|
|
UniqueId = uniqueId
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-04-22 15:57:58 +08:00
|
|
|
|
var requestUrl = string.Format("/3/tv/{0}/season/{1}?api_key=e28e1bc408db7adefc8bacce225c5085&language=zh-CN", ctn.tvId, ctn.seasonNumber);
|
2025-04-16 18:26:31 +08:00
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
var response = await _apiClient.GetAsync(requestUrl);
|
|
|
|
|
|
|
|
|
|
|
|
if (!response.IsSuccessStatusCode)
|
|
|
|
|
|
{
|
2025-04-22 15:57:58 +08:00
|
|
|
|
_logger.LogError("ChineseNfoRegistry.GetTmdbSeason() 接口调用失败 {requestUrl} & {path} & {response.StatusCode}", requestUrl, ctn.tvNfoPath, response.StatusCode);
|
2025-04-16 18:26:31 +08:00
|
|
|
|
return null;
|
2025-02-27 16:58:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-16 18:26:31 +08:00
|
|
|
|
var str = await response.Content.ReadAsStringAsync();
|
|
|
|
|
|
|
2025-04-03 16:22:31 +08:00
|
|
|
|
record.Json = str;
|
2025-04-03 17:02:10 +08:00
|
|
|
|
await _freeSql.Insert(record).ExecuteIdentityAsync();
|
2025-04-16 18:26:31 +08:00
|
|
|
|
|
2025-04-16 18:54:25 +08:00
|
|
|
|
_logger.LogInformation("ChineseNfoRegistry.GetTmdbSeason() 接口调用, 休眠 1S");
|
|
|
|
|
|
await Task.Delay(1000);
|
2025-04-16 18:26:31 +08:00
|
|
|
|
|
|
|
|
|
|
return JObject.Parse(str);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
|
{
|
2025-04-22 15:57:58 +08:00
|
|
|
|
_logger.LogError("ChineseNfoRegistry.GetTmdbSeason() 接口调用失败 {requestUrl} & {path} \r {e}", requestUrl, ctn.tvNfoPath, e);
|
2025-04-16 18:26:31 +08:00
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-22 15:57:58 +08:00
|
|
|
|
private async Task<JObject?> GetTmdbEpisode(ChineseNfoContent ctn)
|
2025-04-16 18:26:31 +08:00
|
|
|
|
{
|
2025-04-22 15:57:58 +08:00
|
|
|
|
var uniqueId = $"{ctn.tvId}-{ctn.seasonNumber}-{ctn.episodeNumber}";
|
2025-04-16 18:26:31 +08:00
|
|
|
|
|
|
|
|
|
|
var record = await _freeSql.Select<TheMovieDbRecord>()
|
|
|
|
|
|
.Where(x => x.Type == TheMovieDbType.Episode)
|
|
|
|
|
|
.Where(x => x.UniqueId == uniqueId)
|
|
|
|
|
|
.FirstAsync();
|
|
|
|
|
|
|
|
|
|
|
|
if (record != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return JObject.Parse(record.Json);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
record = new TheMovieDbRecord
|
|
|
|
|
|
{
|
|
|
|
|
|
Type = TheMovieDbType.Episode,
|
|
|
|
|
|
UniqueId = uniqueId
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-04-22 15:57:58 +08:00
|
|
|
|
var requestUrl = string.Format("/3/tv/{0}/season/{1}/episode/{2}?api_key=e28e1bc408db7adefc8bacce225c5085&language=zh-CN", ctn.tvId, ctn.seasonNumber, ctn.episodeNumber);
|
2025-04-16 18:26:31 +08:00
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
var response = await _apiClient.GetAsync(requestUrl);
|
|
|
|
|
|
|
|
|
|
|
|
if (!response.IsSuccessStatusCode)
|
|
|
|
|
|
{
|
2025-04-22 15:57:58 +08:00
|
|
|
|
_logger.LogError("ChineseNfoRegistry.GetTmdbEpisode() 接口调用失败 {requestUrl} & {path} & {response.StatusCode}", requestUrl, ctn.episodeNfoPath, response.StatusCode);
|
2025-04-16 18:26:31 +08:00
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var str = await response.Content.ReadAsStringAsync();
|
|
|
|
|
|
|
|
|
|
|
|
record.Json = str;
|
|
|
|
|
|
await _freeSql.Insert(record).ExecuteIdentityAsync();
|
|
|
|
|
|
|
2025-04-16 18:54:25 +08:00
|
|
|
|
_logger.LogInformation("ChineseNfoRegistry.GetTmdbEpisode() 接口调用, 休眠 1S");
|
|
|
|
|
|
await Task.Delay(1000);
|
2025-04-16 18:26:31 +08:00
|
|
|
|
|
|
|
|
|
|
return JObject.Parse(str);
|
2025-02-27 16:58:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
|
{
|
2025-04-22 15:57:58 +08:00
|
|
|
|
_logger.LogError("ChineseNfoRegistry.GetTmdbEpisode() 接口调用失败 {requestUrl} & {path} \r {e}", requestUrl, ctn.episodeNfoPath, e);
|
2025-02-27 16:58:21 +08:00
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-04-03 16:22:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public class TheMovieDbRecord
|
|
|
|
|
|
{
|
2025-04-03 17:03:42 +08:00
|
|
|
|
[Column(IsPrimary = true, IsIdentity = true)]
|
2025-04-03 16:22:31 +08:00
|
|
|
|
public long Id { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
public TheMovieDbType Type { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
public string UniqueId { get; set; } = string.Empty;
|
|
|
|
|
|
|
|
|
|
|
|
public TheMovieDbLanguage Language { get; set; }
|
|
|
|
|
|
|
2025-04-16 18:26:31 +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,
|
2025-04-16 18:26:31 +08:00
|
|
|
|
Person = 4
|
2025-04-03 16:22:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public enum TheMovieDbLanguage
|
|
|
|
|
|
{
|
|
|
|
|
|
ZhCn = 1,
|
|
|
|
|
|
ZhTw = 2
|
2025-04-22 15:57:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public class ChineseNfoContent
|
|
|
|
|
|
{
|
|
|
|
|
|
public int tvId { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
public string tvNfoPath { get; set; } = string.Empty;
|
|
|
|
|
|
|
|
|
|
|
|
public int seasonNumber { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
public string seasonNfoPath { get; set; } = string.Empty;
|
|
|
|
|
|
|
|
|
|
|
|
public int episodeNumber { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
public string episodeNfoPath { get; set; } = string.Empty;
|
|
|
|
|
|
}
|