Compare commits

..

No commits in common. "1c1f30a2976aa40c0e4e6d1252a0d0928965bdc6" and "2c903375460d5a43618ab74745757a43549ae63c" have entirely different histories.

23 changed files with 743 additions and 292 deletions

View File

@ -1,13 +0,0 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# Rider 忽略的文件
/modules.xml
/contentModel.xml
/projectSettingsUpdater.xml
/.idea.BangumiRenamer.iml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="UserContentModel">
<attachedFolders />
<explicitIncludes />
<explicitExcludes>
<Path>Test</Path>
</explicitExcludes>
</component>
</project>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@ -8,18 +8,9 @@
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Res/**" />
</ItemGroup>
<ItemGroup>
<Folder Include="Res\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="LLCSharpUtils" Version="1.0.2" />
<PackageReference Include="NativeFileDialogSharp" Version="0.5.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="TMDbLib" Version="2.3.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="OllamaSharp" Version="5.1.14" />
<PackageReference Include="TMDbLib" Version="2.2.0" />
</ItemGroup>
</Project>

115
EpisodeGroup.cs Normal file
View File

@ -0,0 +1,115 @@
namespace ConsoleApp1;
public class Node
{
public string spot;
public List<Node> son;
public string session;
public string title;
public bool isOverride;
}
public class EpisodeGroup
{
private Node _root;
public readonly List<EpisodeInfo> episodes = new List<EpisodeInfo>();
private Node FindOrCreateShow(Node node, string spot)
{
if (node.son == null)
{
node.son = new List<Node>();
}
Node target = null;
foreach (var son in node.son)
{
if (son.spot == spot)
{
target = son;
break;
}
}
if (target == null)
{
target = new Node
{
spot = spot,
isOverride = false
};
node.son.Add(target);
}
return target;
}
private void Add(EpisodeInfo episode)
{
if (_root == null)
{
_root = new Node
{
spot = "",
isOverride = false
};
}
var curr = _root;
var spots = episode.path.Split('/');
foreach (var spot in spots)
{
curr = FindOrCreateShow(curr, spot);
}
curr.session = episode.session;
curr.title = episode.name;
curr.isOverride = true;
}
public void Run()
{
_root = null;
foreach (var episode in episodes)
{
Add(episode);
}
DoRun(_root);
foreach (var episode in episodes)
{
var curr = _root;
var spots = episode.path.Split('/');
foreach (var spot in spots)
{
curr = FindOrCreateShow(curr, spot);
if (curr.isOverride)
{
episode.name = curr.title;
episode.session = curr.session;
break;
}
}
}
}
private void DoRun(Node node)
{
if (node == null) return;
if (node.son == null) return;
foreach (var son in node.son)
{
DoRun(son);
}
var query = (from son in node.son
where son.isOverride
select son).GroupBy(node => (node.title, node.session));
foreach (var group in query)
{
if (group.Count() * 2 > node.son.Count)
{
node.isOverride = true;
(node.title, node.session) = group.Key;
}
}
}
}

146
EpisodeParser.cs Normal file
View File

@ -0,0 +1,146 @@
using System.Collections.Concurrent;
using System.Text;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
namespace ConsoleApp1;
public class EpisodeInfo
{
public string path;
public string name;
public string session;
public string episode;
public string group;
public string type; // others, episode, subtitle
public string language;
}
public class EpisodeParseResult
{
public bool success;
public string originalQuestion;
public EpisodeInfo parseResult;
}
public class EpisodeParser
{
// todo: 添加解析年份
private bool _running = false;
private const string PromptPath = "Prompt.txt";
private readonly string _prompt;
private readonly OllamaHelper _ollama;
private ConcurrentQueue<string> _questions;
private ConcurrentQueue<EpisodeParseResult> _results;
public bool Running => _running;
public int TotalQuestions => _questions.Count + _results.Count;
public int CompletedQuestions => _results.Count;
public int RestQuestions => _questions.Count;
public bool TryGetResult(out EpisodeParseResult result)
{
return _results.TryDequeue(out result);
}
public EpisodeParser()
{
_prompt = File.ReadAllText(PromptPath);
_ollama = new OllamaHelper();
_questions = new ConcurrentQueue<string>();
_results = new ConcurrentQueue<EpisodeParseResult>();
}
public void Append(string question)
{
_questions.Enqueue(question);
}
public void Start()
{
if (_running) return ;
_running = true;
DoParse();
}
private string Preprocess(string respoonds)
{
return respoonds.Replace("```json\n", "").Replace("```", "");
}
private static string RemoveNonDigits(string s)
{
return Regex.Replace(s, @"[^\d\.]*", "");
}
private static string RemoveFrontZeros(string s)
{
var result = new StringBuilder();
bool front = true;
foreach (var c in s)
{
if (front && c == '0') continue;
front = false;
result.Append(c);
}
return result.ToString();
}
private static string ProcessSession(string session)
{
session = RemoveFrontZeros(RemoveNonDigits(session));
if (session.Length > 2) session = "";
return session == "" ? "1" : session;
}
private static string ProcessEpisode(string episode)
{
episode = RemoveFrontZeros(RemoveNonDigits(episode));
return episode == "" ? "1" : episode;
}
private void FinalProcess(EpisodeInfo info)
{
if (info.type == "others")
{
info.episode = "";
info.session = ProcessSession(info.session);
}
else
{
info.episode = ProcessEpisode(info.episode);
info.session = ProcessSession(info.session);
}
}
private async void DoParse()
{
while (_questions.TryDequeue(out string question))
{
var result = new EpisodeParseResult();
result.originalQuestion = question;
var responds = await _ollama.Ask(_prompt + $"\"{question}\"");
try
{
responds = Preprocess(responds);
result.parseResult = JsonConvert.DeserializeObject<EpisodeInfo>(responds);
result.parseResult.path = question;
result.success = result.parseResult != null;
if (result.success)
{
FinalProcess(result.parseResult);
}
}
catch (Exception _)
{
result.success = false;
}
_results.Enqueue(result);
}
_running = false;
}
}

25
OllamaHelper.cs Normal file
View File

@ -0,0 +1,25 @@
using System.Text;
using OllamaSharp;
namespace ConsoleApp1;
public class OllamaHelper
{
private const string SelectedModel = "gemma3:12b";
private readonly OllamaApiClient _ollama;
public OllamaHelper()
{
var uri = new Uri("http://localhost:11434");
_ollama = new OllamaApiClient(uri);
_ollama.SelectedModel = SelectedModel;
}
public async Task<string> Ask(string question)
{
var result = new StringBuilder();
await foreach (var stream in _ollama.GenerateAsync(question))
result.Append(stream.Response);
return result.ToString();
}
}

9
PathExtension.cs Normal file
View File

@ -0,0 +1,9 @@
namespace ConsoleApp1;
public static class PathExtension
{
public static string ToUnixPath(this string path)
{
return path.Replace(@"\", "/");
}
}

174
Playgournd.cs Normal file
View File

@ -0,0 +1,174 @@
using System.Diagnostics;
using System.Text;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
namespace ConsoleApp1;
public static class Playgournd
{
public static void GetAllFiles(string basePath = @"\\192.168.31.10\media\downloads\aria2\TV\")
{
var files = Directory.GetFiles(basePath, "*", SearchOption.AllDirectories);
files = files.Select(path => path.Replace(basePath, "").ToUnixPath()).ToArray();
var result = new StringBuilder();
foreach (var file in files)
{
result.AppendLine(file);
}
File.WriteAllText("questions.txt", result.ToString(), Encoding.UTF8);
}
public static async Task ParseQuestions(string path = "questions.txt")
{
var parser = new EpisodeParser();
var questions = File.ReadAllLines(path);
foreach (var question in questions)
{
parser.Append(question);
}
parser.Start();
int current = -1;
var stopwatch = new Stopwatch();
stopwatch.Start();
while (parser.Running)
{
if (current != parser.CompletedQuestions)
{
var prompt = $"{parser.CompletedQuestions}/{parser.TotalQuestions}";
if (current != -1)
{
prompt += $", 预计剩余 {stopwatch.Elapsed.Seconds * parser.RestQuestions}s";
stopwatch.Restart();
}
current = parser.CompletedQuestions;
Console.WriteLine(prompt);
}
await Task.Delay(1000);
}
var grouper = new EpisodeGroup();
while (parser.TryGetResult(out var result))
{
if (!result.success)
{
Console.WriteLine($"解析失败: {result.originalQuestion}");
}
grouper.episodes.Add(result.parseResult);
}
grouper.Run();
File.WriteAllText($"results_{DateTime.Now:yyyy_MM_dd-HH_mm_ss}.json", JsonConvert.SerializeObject(grouper.episodes, Formatting.Indented), Encoding.UTF8);
}
public static async Task ReparseFailedQuestions(string path)
{
var parser = new EpisodeParser();
var resultsJson = File.ReadAllText(path);
var results = JsonConvert.DeserializeObject<List<EpisodeParseResult>>(resultsJson);
var dict = new Dictionary<string, EpisodeParseResult>();
for (int i = 0; i < results.Count; i++)
{
var result = results[i];
if (result.success == false)
{
dict[result.originalQuestion] = result;
parser.Append(result.originalQuestion);
}
}
parser.Start();
int current = -1;
var stopwatch = new Stopwatch();
stopwatch.Start();
while (parser.Running)
{
if (current != parser.CompletedQuestions)
{
var prompt = $"{parser.CompletedQuestions}/{parser.TotalQuestions}";
if (current != -1)
{
prompt += $", 预计剩余 {stopwatch.Elapsed.Seconds * parser.RestQuestions}s";
stopwatch.Restart();
}
current = parser.CompletedQuestions;
Console.WriteLine(prompt);
}
await Task.Delay(1000);
}
while (parser.TryGetResult(out var result))
{
dict[result.originalQuestion].success = result.success;
dict[result.originalQuestion].parseResult = result.parseResult;
}
File.WriteAllText($"results_{DateTime.Now:yyyy_MM_dd-HH_mm_ss}.json", JsonConvert.SerializeObject(results), Encoding.UTF8);
}
public static async Task<ShowsManager> CalcShows(string path)
{
var resultsJson = File.ReadAllText(path);
var results = JsonConvert.DeserializeObject<List<EpisodeParseResult>>(resultsJson);
var showsManager = new ShowsManager();
foreach (var result in results)
{
showsManager.AppendEpisode(result.parseResult);
}
return showsManager;
}
public static async Task Repair(string path, string qPath)
{
var parser = new EpisodeParser();
var questions = File.ReadAllLines(qPath);
var resultsJson = File.ReadAllText(path);
var results = JsonConvert.DeserializeObject<List<EpisodeParseResult>>(resultsJson);
var dict = new Dictionary<string, EpisodeParseResult>();
for (int i = 0; i < results.Count; i++)
{
var result = results[i];
if (questions.Contains(result.originalQuestion))
{
dict[result.originalQuestion] = result;
parser.Append(result.originalQuestion);
}
}
parser.Start();
int current = -1;
var stopwatch = new Stopwatch();
stopwatch.Start();
while (parser.Running)
{
if (current != parser.CompletedQuestions)
{
var prompt = $"{parser.CompletedQuestions}/{parser.TotalQuestions}";
if (current != -1)
{
prompt += $", 预计剩余 {stopwatch.Elapsed.Seconds * parser.RestQuestions}s";
stopwatch.Restart();
}
current = parser.CompletedQuestions;
Console.WriteLine(prompt);
}
await Task.Delay(1000);
}
while (parser.TryGetResult(out var result))
{
dict[result.originalQuestion].success = result.success;
dict[result.originalQuestion].parseResult = result.parseResult;
}
File.WriteAllText($"results_{DateTime.Now:yyyy_MM_dd-HH_mm_ss}.json", JsonConvert.SerializeObject(results), Encoding.UTF8);
}
}

10
Program.cs Normal file
View File

@ -0,0 +1,10 @@
using System.Diagnostics;
using ConsoleApp1;
using Newtonsoft.Json;
await Playgournd.ParseQuestions();
// var shows = new ShowsManager();
// shows.AppendEpisodeFromFile("results_2025_05_13-01_14_16.json");
// await shows.QueryTMDB(new Dictionary<string, string> {{"The Name of the People", "人民的名义"}});
// shows.MoveFiles(@"\\192.168.31.10\media\downloads\aria2\TV", @"\\192.168.31.10\media\downloads\aria2\Done");

226
ShowsManager.cs Normal file
View File

@ -0,0 +1,226 @@
using System.Net;
using Newtonsoft.Json;
using TMDbLib.Client;
namespace ConsoleApp1;
public class ShowSession
{
public string session;
public List<EpisodeInfo> extras = new List<EpisodeInfo>();
public List<EpisodeInfo> episodes = new List<EpisodeInfo>();
}
public class Show
{
public string rawTitle;
public string title;
public string year;
public string tmdbId;
public List<ShowSession> sessions = new List<ShowSession>();
}
public class ShowsManager
{
private List<Show> _shows = new List<Show>();
private TMDbClient _client;
public ShowsManager()
{
_client = new TMDbClient("991107af25913562cfa06622a52873e1", proxy: new WebProxy("http://127.0.0.1:7897"));
}
private static Show FindOrCreateShow(List<Show> shows, string rawTitle)
{
foreach (var show in shows)
{
if (show.rawTitle == rawTitle)
{
return show;
}
}
var result = new Show();
result.rawTitle = rawTitle;
shows.Add(result);
return result;
}
private static ShowSession FindOrCreateShowSession(List<ShowSession> sessions, string sessionNumber)
{
foreach (var session in sessions)
{
if (session.session == sessionNumber)
{
return session;
}
}
var result = new ShowSession();
result.session = sessionNumber;
sessions.Add(result);
return result;
}
private static string LCP(string str1, string str2)
{
if (string.IsNullOrEmpty(str1) || string.IsNullOrEmpty(str2))
{
return string.Empty;
}
int minLength = Math.Min(str1.Length, str2.Length);
int i = 0;
while (i < minLength && str1[i] == str2[i])
{
i++;
}
return str1.Substring(0, i);
}
public static string CalcBasePathOfSessionExtras(ShowSession session)
{
if(session.extras.Count == 0) return string.Empty;
var result = session.extras[0].path;
foreach (var extra in session.extras)
{
result = LCP(result, extra.path);
}
return result;
}
public void AppendEpisodeFromFile(string path)
{
var resultsJson = File.ReadAllText(path);
var results = JsonConvert.DeserializeObject<List<EpisodeInfo>>(resultsJson);
foreach (var episode in results)
{
AppendEpisode(episode);
}
}
public void AppendEpisode(EpisodeInfo episode)
{
var show = FindOrCreateShow(_shows, episode.name);
var session = FindOrCreateShowSession(show.sessions, episode.session);
if (episode.type == "others")
{
session.extras.Add(episode);
}
else
{
session.episodes.Add(episode);
}
}
public async Task QueryTMDB(Dictionary<string, string> mapping)
{
int current = 0;
foreach (var show in _shows)
{
current++;
Console.WriteLine($"{current}/{_shows.Count}");
var title = show.rawTitle;
if(mapping.TryGetValue(title, out var value)) title = value;
var result = await _client.SearchTvShowAsync(title, language:"zh-CN");
if (result == null || result.Results.Count == 0) continue;
var tv = result.Results[0];
show.title = tv.Name;
show.year = tv.FirstAirDate.Value.Year.ToString();
show.tmdbId = tv.Id.ToString();
}
}
public void Dump(string path)
{
var result = JsonConvert.SerializeObject(_shows, Formatting.Indented);
File.WriteAllText(path, result);
}
public void Load(string path)
{
var json = File.ReadAllText(path);
_shows = JsonConvert.DeserializeObject<List<Show>>(json);
}
private string AddZero(string s)
{
if(s.Length == 1) return $"0{s}";
return s;
}
private string AddNumberToFileName(string path, int n)
{
return Path.Combine(Path.GetDirectoryName(path)??"",
Path.GetFileNameWithoutExtension(path) + $"({n})" + Path.GetExtension(path));
}
public void MoveFiles(string basePath, string targetBasePath)
{
HashSet<string> files = new HashSet<string>();
Queue<(string, string)> moves = new Queue<(string, string)>();
foreach (var show in _shows)
{
foreach (var session in show.sessions)
{
foreach (var episode in session.episodes)
{
var oldPath = Path.Combine(basePath, episode.path);
var newSubPath = $"{show.title} ({show.year})/Season {session.session}/{show.title} ({show.year}) S{AddZero(episode.session)}E{AddZero(episode.episode)} [{episode.group}]";
if (episode.type == "subtitle" && !string.IsNullOrEmpty(episode.language))
{
newSubPath += $".{episode.language}";
}
newSubPath += Path.GetExtension(episode.path);
var newPath = Path.Combine(targetBasePath, newSubPath);
var testPath = newPath;
int n = 0;
while (files.Contains(testPath))
{
testPath = AddNumberToFileName(newPath, ++n);
}
newPath = testPath;
files.Add(newPath);
moves.Enqueue((oldPath, newPath));
Console.WriteLine($"{oldPath} -> {newPath}");
}
var extraPath = CalcBasePathOfSessionExtras(session);
foreach (var episode in session.extras)
{
var oldPath = Path.Combine(basePath, episode.path);
var newSubPath = $"{show.title} ({show.year})/Season {session.session}/extras";
var subPath = episode.path.Substring(extraPath.Length);
var newPath = Path.Combine(targetBasePath, newSubPath, subPath);
var testPath = newPath;
int n = 0;
while (files.Contains(testPath))
{
testPath = AddNumberToFileName(newPath, ++n);
}
newPath = testPath;
files.Add(newPath);
moves.Enqueue((oldPath, newPath));
Console.WriteLine($"{oldPath} -> {newPath}");
}
}
}
while (moves.Count > 0)
{
var move = moves.Dequeue();
if (!File.Exists(move.Item1)) continue;
Directory.CreateDirectory(Path.GetDirectoryName(move.Item2));
File.Move(move.Item1, move.Item2);
Console.WriteLine($"{move.Item1} -> {move.Item2}");
}
}
}

View File

@ -1,9 +0,0 @@
using LLCSharpUtils;
namespace BangumiRenamer.ConfigSchema;
[ConfigItem("Proxy")]
public class ProxyConfig : IConfigItem
{
public string HttpProxy = "";
}

View File

@ -1,9 +0,0 @@
using LLCSharpUtils;
namespace BangumiRenamer.Config;
[ConfigItem("TMDB")]
public class TMDBConfig : IConfigItem
{
public string ApiKey = "";
}

View File

@ -1,13 +0,0 @@
namespace BangumiRenamer.Data;
public class Show
{
public string title;
public List<Session> sessions;
}
public class Session
{
public int id;
public List<int> episodes;
}

View File

@ -1,7 +0,0 @@
class Program
{
static void Main()
{
}
}

View File

@ -1,15 +0,0 @@
namespace BangumiRenamer.Tools;
public class Config
{
private static readonly Lazy<LLCSharpUtils.Config> _lazyConfig = new (() =>
{
var config = new LLCSharpUtils.Config
{
ConfigPath = "config.json"
};
if (!config.Load(true)) throw new Exception("Failed to load config.json");
return config;
});
public static LLCSharpUtils.Config G => _lazyConfig.Value;
}

View File

@ -1,16 +0,0 @@
using LLCSharpUtils;
namespace BangumiRenamer.Tools;
public class EmbededResourceViewer
{
public static void PrintResourceNames()
{
var loader = new EmbeddedResourceLoader();
var names = loader.GetAllResNames();
foreach (var name in names)
{
Logger.G.Info(name);
}
}
}

View File

@ -1,71 +0,0 @@
using System.Diagnostics;
using NativeFileDialogSharp;
namespace BangumiRenamer.Tools;
public static class FolderCloner
{
public static void Run()
{
var result = Dialog.FolderPicker();
if (!result.IsOk) return;
var source = result.Path;
result = Dialog.FolderPicker();
if (!result.IsOk) return;
var dest = Path.Combine(result.Path, Path.GetFileNameWithoutExtension(source.Replace("\\\\", "")));
var finalDest = dest;
int suffix = 1;
while (Directory.Exists(finalDest))
{
finalDest = dest + suffix;
++suffix;
}
CloneStructureWithEmptyFiles(source, finalDest);
Process.Start(new ProcessStartInfo
{
FileName = finalDest,
UseShellExecute = true,
Verb = "open"
});
}
static void CloneStructureWithEmptyFiles(string source, string dest)
{
if (!Directory.Exists(source))
{
return;
}
if (Directory.Exists(dest))
{
return;
}
var queue = new Queue<string>();
queue.Enqueue(source);
while (queue.Count > 0)
{
var dir = queue.Dequeue();
try
{
foreach (var filePath in Directory.GetFiles(dir))
{
var newFilePath = Path.Combine(dest, Path.GetRelativePath(source, filePath));
using var _ = File.Create(newFilePath);
}
foreach (var dirPath in Directory.GetDirectories(dir))
{
queue.Enqueue(dirPath);
var newDirPath = Path.Combine(dest, Path.GetRelativePath(source, dirPath));
Directory.CreateDirectory(newDirPath);
}
}
catch (Exception ex)
{
Logger.G.Error(ex, "Clone");
}
}
}
}

View File

@ -1,15 +0,0 @@
using LLCSharpUtils;
namespace BangumiRenamer.Tools;
public class Logger
{
private static readonly Lazy<Log> _lazy = new (() =>
{
var log = new Log(new LogConfig());
if (!log.Init()) throw new Exception("Failed to initialize log");
return log;
});
public static Log G => _lazy.Value;
}

View File

@ -1,96 +0,0 @@
using BangumiRenamer.Config;
using BangumiRenamer.ConfigSchema;
using TMDbLib.Client;
using NativeFileDialogSharp;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using BangumiRenamer.Data;
namespace BangumiRenamer.Tools;
public static class ShowCompletionChecker
{
static List<Show> FindShows(string checkPath)
{
var shows = new List<Show>();
var showPaths = Directory.GetDirectories(checkPath);
foreach (var showPath in showPaths)
{
var sessions = new List<Session>();
var sessionPaths = Directory.GetDirectories(showPath);
foreach (var sessionPath in sessionPaths)
{
var episodePaths = Directory.GetFiles(sessionPath);
HashSet<int> episodes = new HashSet<int>();
foreach (var episodePath in episodePaths)
{
var matches = Regex.Matches(episodePath, @".*S\d+E(\d+).*");
if (matches.Count == 0) continue;
var episode = int.Parse(matches[0].Groups[1].Value);
episodes.Add(episode);
}
var session = new Session
{
id = int.Parse(Path.GetFileName(sessionPath).Replace("Season ", "")),
episodes = episodes.ToList()
};
sessions.Add(session);
}
shows.Add(new Show
{
title = Path.GetFileName(showPath),
sessions = sessions
});
}
return shows;
}
public static void Run()
{
var dir = Dialog.FolderPicker();
if (!dir.IsOk) return;
var checkPath = dir.Path;
var shows = FindShows(checkPath);
Logger.G.Info($"Total Shows: {shows.Count}");
var client = new TMDbClient(
apiKey: Config.G.Get<TMDBConfig>().ApiKey ,
proxy: new WebProxy(Config.G.Get<ProxyConfig>().HttpProxy));
var output = new StringBuilder();
foreach (var t in shows)
{
var match = Regex.Match(t.title, @"(.*) \((\d{4})\)");
var title = match.Groups[1].Value;
var year = int.Parse(match.Groups[2].Value);
var result = client.SearchTvShowAsync(title, firstAirDateYear: year).Result;
var info = result.Results.FirstOrDefault();
if (info == null)
{
Logger.G.Error($"找不到对应的TV{t.title}");
continue;
}
foreach (var session in t.sessions)
{
var sessionInfo = client.GetTvSeasonAsync(info.Id, session.id).Result;
if (sessionInfo == null)
{
Logger.G.Error($"季度对不上,可能找错了:{t.title} -> {info.OriginalName}");
break;
}
foreach (var episode in sessionInfo.Episodes)
{
if (DateTime.Now.AddDays(-7) < episode.AirDate) continue;
if (!session.episodes.Contains(episode.EpisodeNumber))
{
output.AppendLine($"{title} 的第 {session.id} 季少了第 {episode.EpisodeNumber} 集");
}
}
}
}
Logger.G.Info(output.ToString());
}
}

6
TMDBHelper.cs Normal file
View File

@ -0,0 +1,6 @@
namespace ConsoleApp1;
public class TMDBHelper
{
}

View File

@ -0,0 +1,29 @@
[
{
"path": "The.Name.of.the.People.2017.EP01-55.HD1080P.X264.AAC.Mandarin.CHS.Mp4Ba/The.Name.of.the.People.2017.EP01.HD1080P.X264.AAC.Mandarin.CHS.Mp4Ba.mp4",
"name": "The Name of the People",
"session": "1",
"episode": "1",
"group": "Mp4Ba",
"type": "episode",
"language": ""
},
{
"path": "The.Name.of.the.People.2017.EP01-55.HD1080P.X264.AAC.Mandarin.CHS.Mp4Ba/The.Name.of.the.People.2017.EP02.HD1080P.X264.AAC.Mandarin.CHS.Mp4Ba.mp4",
"name": "The Name of the People",
"session": "1",
"episode": "2",
"group": "Mp4Ba",
"type": "episode",
"language": ""
},
{
"path": "The.Name.of.the.People.2017.EP01-55.HD1080P.X264.AAC.Mandarin.CHS.Mp4Ba/The.Name.of.the.People.2017.EP03.HD1080P.X264.AAC.Mandarin.CHS.Mp4Ba.mp4",
"name": "The Name of the People",
"session": "1",
"episode": "3",
"group": "Mp4Ba",
"type": "episode",
"language": ""
}
]