Repository navigation
Expand file tree
/
Copy pathSmartTagsTask.cs
More file actions
189 lines (164 loc) · 6.81 KB
/
Copy pathSmartTagsTask.cs
File metadata and controls
189 lines (164 loc) · 6.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
using MediaBrowser.Model.Tasks;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Logging;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Entities;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
using System;
using System.Linq;
using System.IO;
namespace SmartTags;
public class SmartTagsTask : IScheduledTask
{
private readonly ILogger _logger;
private readonly ILibraryManager _libraryManager;
private readonly IJsonSerializer _jsonSerializer;
private readonly IHttpClient _httpClient;
private readonly IApplicationPaths _appPaths;
public SmartTagsTask(
ILogger logger,
ILibraryManager libraryManager,
IJsonSerializer jsonSerializer,
IHttpClient httpClient,
IApplicationPaths appPaths)
{
_logger = logger;
_libraryManager = libraryManager;
_jsonSerializer = jsonSerializer;
_httpClient = httpClient;
_appPaths = appPaths;
}
public string Name => "SmartTags Update";
public string Key => "SmartTagsTask";
public string Description => "基于 TMDB 数据更新原产地、年代及 IMDb Top 标签。";
public string Category => "SmartTags";
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
yield return new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerDaily, TimeOfDayTicks = TimeSpan.FromHours(4).Ticks };
}
public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
{
var config = Plugin.Instance?.Configuration;
if (config == null || string.IsNullOrEmpty(config.TmdbApiKey))
{
_logger.Warn("[SmartTags] 未配置 TMDB API Key,跳过任务。");
return;
}
_logger.Info("[SmartTags] 任务开始。正在初始化...");
// 1. 准备 IMDb Top 250 数据 (如果启用)
HashSet<string> imdbTopIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
if (config.EnableImdbTopTags)
{
_logger.Debug("[SmartTags] 正在获取 IMDb Top 250 列表...");
imdbTopIds = await GetImdbTop250IdsAsync(cancellationToken);
_logger.Info($"[SmartTags] 加载了 {imdbTopIds.Count} 个 IMDb Top 250 条目。");
}
// 2. 初始化数据管理器和打标服务
var dataManager = new TmdbDataManager(_appPaths, _jsonSerializer, _httpClient);
var service = new TaggingService(_logger, dataManager, imdbTopIds);
// 3. 查询
var query = new InternalItemsQuery
{
IncludeItemTypes = new[] { "Movie", "Series" },
Recursive = true,
IsVirtualItem = false
};
var items = _libraryManager.GetItemList(query);
var total = items.Length;
_logger.Info($"[SmartTags] 共扫描到 {total} 个媒体项。");
int processed = 0;
int updatedCount = 0;
foreach (var item in items)
{
cancellationToken.ThrowIfCancellationRequested();
processed++;
if (processed % 50 == 0) progress.Report((double)processed / total * 100);
// 复用 TaggingService 统一处理所有标签逻辑
bool isModified = await service.ProcessItemAsync(item, config, cancellationToken);
if (isModified)
{
item.UpdateToRepository(ItemUpdateType.MetadataEdit);
updatedCount++;
}
}
// 任务结束时刷新缓存到磁盘
dataManager.Flush();
_logger.Info($"[SmartTags] 任务完成。处理 {total} 个项目,实际更新 {updatedCount} 个。");
}
// --- 辅助方法: IMDb Top 250 ---
private async Task<HashSet<string>> GetImdbTop250IdsAsync(CancellationToken cancellationToken)
{
var cachePath = Path.Combine(_appPaths.PluginConfigurationsPath, "SmartTags_ImdbTop250.json");
var ids = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
// 1. 尝试从网络下载
bool needDownload = true;
if (File.Exists(cachePath))
{
var lastWrite = File.GetLastWriteTimeUtc(cachePath);
// 缓存有效期 24 小时
if ((DateTime.UtcNow - lastWrite).TotalHours < 24)
{
needDownload = false;
_logger.Debug("[SmartTags] IMDb Top 250 缓存未过期,使用本地文件。");
}
}
if (needDownload)
{
try
{
var url = "https://raw.githubusercontent.com/theapache64/top250/master/top250_min.json";
var options = new HttpRequestOptions
{
Url = url,
CancellationToken = cancellationToken,
BufferContent = true,
EnableHttpCompression = true
};
using var response = await _httpClient.GetResponse(options).ConfigureAwait(false);
using var stream = response.Content;
using var fileStream = new FileStream(cachePath, FileMode.Create, FileAccess.Write);
await stream.CopyToAsync(fileStream);
_logger.Info("[SmartTags] IMDb Top 250 数据已从 GitHub 更新。");
}
catch (Exception ex)
{
_logger.Error($"[SmartTags] 下载 IMDb Top 250 失败: {ex.Message}。将尝试使用旧缓存。");
}
}
// 2. 读取本地缓存并解析
if (File.Exists(cachePath))
{
try
{
var list = _jsonSerializer.DeserializeFromFile<List<ImdbSimpleItem>>(cachePath);
if (list != null)
{
foreach (var i in list)
{
// JSON 格式: "/title/tt0055630/"
if (!string.IsNullOrEmpty(i.ImdbUrl))
{
var parts = i.ImdbUrl.Split('/');
var ttId = parts.FirstOrDefault(p => p.StartsWith("tt", StringComparison.OrdinalIgnoreCase));
if (!string.IsNullOrEmpty(ttId))
{
ids.Add(ttId);
}
}
}
_logger.Info($"[SmartTags] 成功解析 {ids.Count} 个 IMDb Top 250 ID。");
}
}
catch (Exception ex)
{
_logger.Error($"[SmartTags] 解析 IMDb 缓存失败: {ex.Message}");
}
}
return ids;
}
}