Compare commits
3 Commits
Author | SHA1 | Date |
---|---|---|
|
945930d75e | |
|
b2264405d5 | |
|
4ae795d503 |
|
@ -54,6 +54,7 @@ jobs:
|
||||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
file: ./artifacts/${{ env.artifact }}_*.zip
|
file: ./artifacts/${{ env.artifact }}_*.zip
|
||||||
tag: ${{ github.ref }}
|
tag: ${{ github.ref }}
|
||||||
|
release_name: '${{ github.ref_name }}: Jellyfin v10.8'
|
||||||
file_glob: true
|
file_glob: true
|
||||||
overwrite: true
|
overwrite: true
|
||||||
- name: Publish manifest
|
- name: Publish manifest
|
||||||
|
|
|
@ -67,7 +67,7 @@ namespace AnitomySharp
|
||||||
"番外編", "總集編","DRAMA",
|
"番外編", "總集編","DRAMA",
|
||||||
"映像特典","特典","特典アニメ",
|
"映像特典","特典","特典アニメ",
|
||||||
// 特典 Special 剩下的各种类型可以全部命名成 SP,对于较特殊意义的特典也可以自定义命名
|
// 特典 Special 剩下的各种类型可以全部命名成 SP,对于较特殊意义的特典也可以自定义命名
|
||||||
"SPECIAL", "SPECIALS", "SP", "SPs",
|
"SPECIAL", "SPECIALS", "SP", "SPs", "特報",
|
||||||
// 真人特典 Interview/Talk/Stage... 目前我们对于节目、采访、舞台活动、制作等三次元画面的长视频,一概怼成 IV。
|
// 真人特典 Interview/Talk/Stage... 目前我们对于节目、采访、舞台活动、制作等三次元画面的长视频,一概怼成 IV。
|
||||||
"IV",
|
"IV",
|
||||||
// 音乐视频 Music Video
|
// 音乐视频 Music Video
|
||||||
|
@ -85,7 +85,7 @@ namespace AnitomySharp
|
||||||
// 无字 OP/ED Non-Credit Opening/Ending
|
// 无字 OP/ED Non-Credit Opening/Ending
|
||||||
"ED", "ENDING", "NCED", "NCOP", "OP", "OPENING",
|
"ED", "ENDING", "NCED", "NCOP", "OP", "OPENING",
|
||||||
// 预告 Preview 预告下一话内容 注意编号表示其预告的是第几话的内容而不是跟在哪一话后面
|
// 预告 Preview 预告下一话内容 注意编号表示其预告的是第几话的内容而不是跟在哪一话后面
|
||||||
"PREVIEW", "YOKOKU",
|
"PREVIEW", "YOKOKU", "予告",
|
||||||
// 菜单 Menu BD/DVD 播放选择菜单
|
// 菜单 Menu BD/DVD 播放选择菜单
|
||||||
"MENU",
|
"MENU",
|
||||||
// 广告 Commercial Message 电视放送广告,时长一般在 7s/15s/30s/45s/... 左右
|
// 广告 Commercial Message 电视放送广告,时长一般在 7s/15s/30s/45s/... 左右
|
||||||
|
|
|
@ -119,6 +119,29 @@ namespace AnitomySharp
|
||||||
if (string.IsNullOrEmpty(str)) return "";
|
if (string.IsNullOrEmpty(str)) return "";
|
||||||
return Ordinals.TryGetValue(str, out var foundString) ? foundString : "";
|
return Ordinals.TryGetValue(str, out var foundString) ? foundString : "";
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 转换原始值中的全角数字
|
||||||
|
/// 1234567890
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="str"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetNumberFromFullWidth(string str)
|
||||||
|
{
|
||||||
|
string output = str;
|
||||||
|
for (int i = 0; i < str.Length; i++)
|
||||||
|
{
|
||||||
|
if (char.IsDigit(str[i]))
|
||||||
|
{
|
||||||
|
int fullwidthDigit = (int)str[i];
|
||||||
|
if (fullwidthDigit >= 65296 && fullwidthDigit <= 65305)
|
||||||
|
{
|
||||||
|
int halfwidthDigit = fullwidthDigit - 65248;
|
||||||
|
output = output.Replace(str[i], (char)halfwidthDigit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the index of the first digit in the <c>str</c>; -1 otherwise.
|
/// Returns the index of the first digit in the <c>str</c>; -1 otherwise.
|
||||||
|
@ -273,7 +296,7 @@ namespace AnitomySharp
|
||||||
var prevToken = Token.FindPrevToken(_parser.Tokens, pos, Token.TokenFlag.FlagNotDelimiter);
|
var prevToken = Token.FindPrevToken(_parser.Tokens, pos, Token.TokenFlag.FlagNotDelimiter);
|
||||||
if (!IsTokenCategory(prevToken, Token.TokenCategory.Bracket)) return false;
|
if (!IsTokenCategory(prevToken, Token.TokenCategory.Bracket)) return false;
|
||||||
var nextToken = Token.FindNextToken(_parser.Tokens, pos, Token.TokenFlag.FlagNotDelimiter);
|
var nextToken = Token.FindNextToken(_parser.Tokens, pos, Token.TokenFlag.FlagNotDelimiter);
|
||||||
if (nextToken < 0) return false;
|
if (!Token.InListRange(prevToken, _parser.Tokens) || !Token.InListRange(nextToken, _parser.Tokens)) return false;
|
||||||
return KeywordManager.Contains(Element.ElementCategory.ElementAnimeType, _parser.Tokens[nextToken].Content);
|
return KeywordManager.Contains(Element.ElementCategory.ElementAnimeType, _parser.Tokens[nextToken].Content);
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -285,8 +308,8 @@ namespace AnitomySharp
|
||||||
{
|
{
|
||||||
var prevToken = Token.FindPrevToken(_parser.Tokens, pos, Token.TokenFlag.FlagNotDelimiter);
|
var prevToken = Token.FindPrevToken(_parser.Tokens, pos, Token.TokenFlag.FlagNotDelimiter);
|
||||||
var nextToken = Token.FindNextToken(_parser.Tokens, pos, Token.TokenFlag.FlagNotDelimiter);
|
var nextToken = Token.FindNextToken(_parser.Tokens, pos, Token.TokenFlag.FlagNotDelimiter);
|
||||||
|
if(!Token.InListRange(prevToken, _parser.Tokens)||!Token.InListRange(nextToken, _parser.Tokens)) return false;
|
||||||
if (!IsTokenCategory(nextToken, Token.TokenCategory.Bracket)) return false;
|
if (!IsTokenCategory(nextToken, Token.TokenCategory.Bracket)) return false;
|
||||||
if (prevToken < 0) return false;
|
|
||||||
return KeywordManager.Contains(Element.ElementCategory.ElementAnimeType, _parser.Tokens[prevToken].Content);
|
return KeywordManager.Contains(Element.ElementCategory.ElementAnimeType, _parser.Tokens[prevToken].Content);
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -298,8 +321,8 @@ namespace AnitomySharp
|
||||||
{
|
{
|
||||||
var prevToken = Token.FindPrevToken(_parser.Tokens, pos, Token.TokenFlag.FlagNotDelimiter);
|
var prevToken = Token.FindPrevToken(_parser.Tokens, pos, Token.TokenFlag.FlagNotDelimiter);
|
||||||
var nextToken = Token.FindNextToken(_parser.Tokens, pos, Token.TokenFlag.FlagNotDelimiter);
|
var nextToken = Token.FindNextToken(_parser.Tokens, pos, Token.TokenFlag.FlagNotDelimiter);
|
||||||
|
if (!Token.InListRange(prevToken, _parser.Tokens) || !Token.InListRange(nextToken, _parser.Tokens)) return false;
|
||||||
if (!IsTokenCategory(nextToken, Token.TokenCategory.Bracket)) return false;
|
if (!IsTokenCategory(nextToken, Token.TokenCategory.Bracket)) return false;
|
||||||
if (prevToken < 0) return false;
|
|
||||||
return KeywordManager.ContainsInPeekEntries(Element.ElementCategory.ElementAnimeType, _parser.Tokens[prevToken].Content);
|
return KeywordManager.ContainsInPeekEntries(Element.ElementCategory.ElementAnimeType, _parser.Tokens[prevToken].Content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -435,4 +458,4 @@ namespace AnitomySharp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -513,7 +513,8 @@ namespace AnitomySharp
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
regexPattern = @"([第全]?)([0-9一二三四五六七八九十壱弐参]+)([期章話话巻卷幕夜期発縛])";
|
// 全角数字:\uFF10-\uFF19
|
||||||
|
regexPattern = @"([第全]?)([0-9一二三四五六七八九十壱弐参\uFF10-\uFF19]+)([回集話话幕夜発縛])";
|
||||||
match = Regex.Match(word, RegexMatchOnlyStart + regexPattern + RegexMatchOnlyEnd, RegexOptions.IgnoreCase);
|
match = Regex.Match(word, RegexMatchOnlyStart + regexPattern + RegexMatchOnlyEnd, RegexOptions.IgnoreCase);
|
||||||
if (match.Success)
|
if (match.Success)
|
||||||
{
|
{
|
||||||
|
@ -522,11 +523,33 @@ namespace AnitomySharp
|
||||||
{
|
{
|
||||||
episodeNumber = ParserHelper.GetNumberFromOrdinal(episodeNumber);
|
episodeNumber = ParserHelper.GetNumberFromOrdinal(episodeNumber);
|
||||||
}
|
}
|
||||||
|
episodeNumber = ParserHelper.GetNumberFromFullWidth(episodeNumber);
|
||||||
|
SetEpisodeNumber(episodeNumber, token, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
regexPattern = @"([第全]?)([0-9一二三四五六七八九十壱弐参\uFF10-\uFF19]+)([期章巻卷])";
|
||||||
|
match = Regex.Match(word, RegexMatchOnlyStart + regexPattern + RegexMatchOnlyEnd, RegexOptions.IgnoreCase);
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
var episodeNumber = match.Groups[2].Value;
|
||||||
|
if (!StringHelper.IsNumericString(episodeNumber))
|
||||||
|
{
|
||||||
|
episodeNumber = ParserHelper.GetNumberFromOrdinal(episodeNumber);
|
||||||
|
}
|
||||||
|
episodeNumber = ParserHelper.GetNumberFromFullWidth(episodeNumber);
|
||||||
SetEpisodeNumber(episodeNumber, token, false);
|
SetEpisodeNumber(episodeNumber, token, false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
regexPattern = @"(vol|EPISODE|ACT|scene|ep|volume|screen|voice|case|menu|rail|round|game|page|collection|cage|office|doll|Princess)([ \.\-_])([0-9]+)";
|
regexPattern = @"(EPISODE|ACT|scene|ep|screen|voice|case|menu|rail|round|game|page|collection|cage|office|doll|Princess)([ \.\-_])([0-9]+)";
|
||||||
|
match = Regex.Match(word, RegexMatchOnlyStart + regexPattern + RegexMatchOnlyEnd, RegexOptions.IgnoreCase);
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
var episodeNumber = match.Groups[3].Value;
|
||||||
|
SetEpisodeNumber(episodeNumber, token, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
regexPattern = @"(vol|volume)([ \.\-_])([0-9]+)";
|
||||||
match = Regex.Match(word, RegexMatchOnlyStart + regexPattern + RegexMatchOnlyEnd, RegexOptions.IgnoreCase);
|
match = Regex.Match(word, RegexMatchOnlyStart + regexPattern + RegexMatchOnlyEnd, RegexOptions.IgnoreCase);
|
||||||
if (match.Success)
|
if (match.Success)
|
||||||
{
|
{
|
||||||
|
@ -874,4 +897,4 @@ namespace AnitomySharp
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -167,23 +167,36 @@ namespace Jellyfin.Plugin.MetaShark.Test
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void TestEposideParse()
|
public void TestEposideParse()
|
||||||
{
|
{
|
||||||
|
// 普通数字
|
||||||
|
var fileName = "03.mp4";
|
||||||
|
var parseResult = NameParser.ParseEpisode(fileName);
|
||||||
|
Assert.AreEqual(parseResult.Name, "03");
|
||||||
|
Assert.AreEqual(parseResult.ParentIndexNumber, null);
|
||||||
|
Assert.AreEqual(parseResult.IndexNumber, 3);
|
||||||
|
|
||||||
|
fileName = "03 4K.mp4";
|
||||||
|
parseResult = NameParser.ParseEpisode(fileName);
|
||||||
|
Assert.AreEqual(parseResult.Name, "03");
|
||||||
|
Assert.AreEqual(parseResult.ParentIndexNumber, null);
|
||||||
|
Assert.AreEqual(parseResult.IndexNumber, 3);
|
||||||
|
|
||||||
// 混合中英文
|
// 混合中英文
|
||||||
var fileName = "新世界.New.World.2013.BluRay.1080p.x265.10bit.MNHD-FRDS";
|
fileName = "新世界.New.World.2013.BluRay.1080p.x265.10bit.MNHD-FRDS";
|
||||||
var parseResult = NameParser.Parse(fileName);
|
parseResult = NameParser.ParseEpisode(fileName);
|
||||||
Assert.AreEqual(parseResult.ChineseName, "新世界");
|
Assert.AreEqual(parseResult.ChineseName, "新世界");
|
||||||
Assert.AreEqual(parseResult.Name, "New World");
|
Assert.AreEqual(parseResult.Name, "New World");
|
||||||
Assert.AreEqual(parseResult.Year, 2013);
|
Assert.AreEqual(parseResult.Year, 2013);
|
||||||
|
|
||||||
// 只英文 S01E01
|
// 只英文 S01E01
|
||||||
fileName = "She-Hulk.Attorney.At.Law.S01E01.1080p.WEBRip.x265-RARBG";
|
fileName = "She-Hulk.Attorney.At.Law.S01E01.1080p.WEBRip.x265-RARBG";
|
||||||
parseResult = NameParser.Parse(fileName);
|
parseResult = NameParser.ParseEpisode(fileName);
|
||||||
Assert.AreEqual(parseResult.Name, "She-Hulk Attorney At Law");
|
Assert.AreEqual(parseResult.Name, "She-Hulk Attorney At Law");
|
||||||
Assert.AreEqual(parseResult.ParentIndexNumber, 1);
|
Assert.AreEqual(parseResult.ParentIndexNumber, 1);
|
||||||
Assert.AreEqual(parseResult.IndexNumber, 1);
|
Assert.AreEqual(parseResult.IndexNumber, 1);
|
||||||
|
|
||||||
// 测试 SXXEPXX 格式
|
// 测试 SXXEPXX 格式
|
||||||
fileName = "神探狄仁杰2 Detective.Dee.Ⅱ.S02EP02.2006.2160p.WEB-DL.x264.AAC-HQC";
|
fileName = "神探狄仁杰2 Detective.Dee.Ⅱ.S02EP02.2006.2160p.WEB-DL.x264.AAC-HQC";
|
||||||
parseResult = NameParser.Parse(fileName);
|
parseResult = NameParser.ParseEpisode(fileName);
|
||||||
Assert.AreEqual(parseResult.ChineseName, "神探狄仁杰2");
|
Assert.AreEqual(parseResult.ChineseName, "神探狄仁杰2");
|
||||||
Assert.AreEqual(parseResult.Name, "Detective Dee Ⅱ");
|
Assert.AreEqual(parseResult.Name, "Detective Dee Ⅱ");
|
||||||
Assert.AreEqual(parseResult.ParentIndexNumber, 2);
|
Assert.AreEqual(parseResult.ParentIndexNumber, 2);
|
||||||
|
@ -192,26 +205,26 @@ namespace Jellyfin.Plugin.MetaShark.Test
|
||||||
|
|
||||||
// 日文
|
// 日文
|
||||||
fileName = "プロポーズ大作戦Ep05_x264.mp4";
|
fileName = "プロポーズ大作戦Ep05_x264.mp4";
|
||||||
parseResult = NameParser.Parse(fileName);
|
parseResult = NameParser.ParseEpisode(fileName);
|
||||||
Assert.AreEqual(parseResult.Name, "プロポーズ大作戦Ep05");
|
Assert.AreEqual(parseResult.Name, "プロポーズ大作戦Ep05");
|
||||||
Assert.AreEqual(parseResult.ParentIndexNumber, null);
|
Assert.AreEqual(parseResult.ParentIndexNumber, null);
|
||||||
Assert.AreEqual(parseResult.IndexNumber, 5);
|
Assert.AreEqual(parseResult.IndexNumber, 5);
|
||||||
|
|
||||||
fileName = "[01] [ANK-Raws] あっちこっち 01 (BDrip 1920x1080 HEVC-YUV420P10 FLAC)";
|
fileName = "[01] [ANK-Raws] あっちこっち 01 (BDrip 1920x1080 HEVC-YUV420P10 FLAC)";
|
||||||
parseResult = NameParser.Parse(fileName);
|
parseResult = NameParser.ParseEpisode(fileName);
|
||||||
Assert.AreEqual(parseResult.Name, "あっちこっち 01");
|
Assert.AreEqual(parseResult.Name, "あっちこっち 01");
|
||||||
Assert.AreEqual(parseResult.ParentIndexNumber, null);
|
Assert.AreEqual(parseResult.ParentIndexNumber, null);
|
||||||
Assert.AreEqual(parseResult.IndexNumber, 1);
|
Assert.AreEqual(parseResult.IndexNumber, 1);
|
||||||
|
|
||||||
// 只中文
|
// 只中文
|
||||||
fileName = "齊天大聖 第02集";
|
fileName = "齊天大聖 第02集";
|
||||||
parseResult = NameParser.Parse(fileName);
|
parseResult = NameParser.ParseEpisode(fileName);
|
||||||
Assert.AreEqual(parseResult.Name, "齊天大聖 第02集");
|
Assert.AreEqual(parseResult.Name, "齊天大聖");
|
||||||
Assert.AreEqual(parseResult.ParentIndexNumber, null);
|
Assert.AreEqual(parseResult.ParentIndexNumber, null);
|
||||||
Assert.AreEqual(parseResult.IndexNumber, 2);
|
Assert.AreEqual(parseResult.IndexNumber, 2);
|
||||||
|
|
||||||
fileName = "齊天大聖 第 02 期";
|
fileName = "齊天大聖 第 02 期";
|
||||||
parseResult = NameParser.Parse(fileName);
|
parseResult = NameParser.ParseEpisode(fileName);
|
||||||
Assert.AreEqual(parseResult.Name, "齊天大聖");
|
Assert.AreEqual(parseResult.Name, "齊天大聖");
|
||||||
Assert.AreEqual(parseResult.ParentIndexNumber, null);
|
Assert.AreEqual(parseResult.ParentIndexNumber, null);
|
||||||
Assert.AreEqual(parseResult.IndexNumber, 2);
|
Assert.AreEqual(parseResult.IndexNumber, 2);
|
||||||
|
@ -219,38 +232,40 @@ namespace Jellyfin.Plugin.MetaShark.Test
|
||||||
|
|
||||||
// anime
|
// anime
|
||||||
fileName = "[YYDM-11FANS][THERMAE_ROMAE][02][BDRIP][720P][X264-10bit_AAC][7FF2269F]";
|
fileName = "[YYDM-11FANS][THERMAE_ROMAE][02][BDRIP][720P][X264-10bit_AAC][7FF2269F]";
|
||||||
parseResult = NameParser.Parse(fileName);
|
parseResult = NameParser.ParseEpisode(fileName);
|
||||||
Assert.AreEqual(parseResult.Name, "THERMAE ROMAE");
|
Assert.AreEqual(parseResult.Name, "THERMAE ROMAE");
|
||||||
Assert.AreEqual(parseResult.ParentIndexNumber, null);
|
Assert.AreEqual(parseResult.ParentIndexNumber, null);
|
||||||
Assert.AreEqual(parseResult.IndexNumber, 2);
|
Assert.AreEqual(parseResult.IndexNumber, 2);
|
||||||
|
|
||||||
// anime带季数
|
// anime带季数
|
||||||
fileName = "[WMSUB][Detective Conan - Zero‘s Tea Time ][S01][E06][BIG5][1080P].mp4";
|
fileName = "[WMSUB][Detective Conan - Zero‘s Tea Time ][S01][E06][BIG5][1080P].mp4";
|
||||||
parseResult = NameParser.Parse(fileName);
|
parseResult = NameParser.ParseEpisode(fileName);
|
||||||
Assert.AreEqual(parseResult.Name, "Detective Conan - Zero‘s Tea Time");
|
Assert.AreEqual(parseResult.Name, "Detective Conan - Zero‘s Tea Time");
|
||||||
Assert.AreEqual(parseResult.ParentIndexNumber, 1);
|
Assert.AreEqual(parseResult.ParentIndexNumber, 1);
|
||||||
Assert.AreEqual(parseResult.IndexNumber, 6);
|
Assert.AreEqual(parseResult.IndexNumber, 6);
|
||||||
|
|
||||||
fileName = "[KTXP][Machikado_Mazoku_S2][01][BIG5][1080p]";
|
fileName = "[KTXP][Machikado_Mazoku_S2][01][BIG5][1080p]";
|
||||||
parseResult = NameParser.Parse(fileName);
|
parseResult = NameParser.ParseEpisode(fileName);
|
||||||
Assert.AreEqual(parseResult.Name, "Machikado Mazoku");
|
Assert.AreEqual(parseResult.Name, "Machikado Mazoku");
|
||||||
Assert.AreEqual(parseResult.ParentIndexNumber, null);
|
Assert.AreEqual(parseResult.ParentIndexNumber, null);
|
||||||
Assert.AreEqual(parseResult.IndexNumber, 1);
|
Assert.AreEqual(parseResult.IndexNumber, 1);
|
||||||
|
|
||||||
fileName = "[異域字幕組][她和她的貓 - Everything Flows -][She and Her Cat - Everything Flows -][01][720p][繁體]";
|
fileName = "[異域字幕組][她和她的貓 - Everything Flows -][She and Her Cat - Everything Flows -][01][720p][繁體]";
|
||||||
parseResult = NameParser.Parse(fileName);
|
parseResult = NameParser.ParseEpisode(fileName);
|
||||||
Assert.AreEqual(parseResult.Name, "她和她的貓 - Everything Flows");
|
Assert.AreEqual(parseResult.Name, "她和她的貓 - Everything Flows");
|
||||||
Assert.AreEqual(parseResult.ParentIndexNumber, null);
|
Assert.AreEqual(parseResult.ParentIndexNumber, null);
|
||||||
Assert.AreEqual(parseResult.IndexNumber, 1);
|
Assert.AreEqual(parseResult.IndexNumber, 1);
|
||||||
|
|
||||||
// anime特典
|
// anime特典
|
||||||
fileName = "[KissSub][Steins;Gate][SP][GB_BIG5_JP][BDrip][1080P][HEVC] 边界曲面的缺失之环";
|
fileName = "[KissSub][Steins;Gate][SP][GB_BIG5_JP][BDrip][1080P][HEVC] 边界曲面的缺失之环";
|
||||||
parseResult = NameParser.Parse(fileName);
|
parseResult = NameParser.ParseEpisode(fileName);
|
||||||
Assert.IsTrue(parseResult.IsSpecial);
|
Assert.IsTrue(parseResult.IsSpecial);
|
||||||
Assert.AreEqual(parseResult.Name, "边界曲面的缺失之环");
|
Assert.AreEqual(parseResult.Name, "边界曲面的缺失之环");
|
||||||
Assert.AreEqual(parseResult.ParentIndexNumber, null);
|
Assert.AreEqual(parseResult.ParentIndexNumber, null);
|
||||||
Assert.AreEqual(parseResult.IndexNumber, null);
|
Assert.AreEqual(parseResult.IndexNumber, null);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -52,18 +52,10 @@ namespace Jellyfin.Plugin.MetaShark.Api
|
||||||
Regex regSubname = new Regex(@"又名: (.+?)\n", RegexOptions.Compiled);
|
Regex regSubname = new Regex(@"又名: (.+?)\n", RegexOptions.Compiled);
|
||||||
Regex regImdb = new Regex(@"IMDb: (tt\d+)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
Regex regImdb = new Regex(@"IMDb: (tt\d+)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||||
Regex regSite = new Regex(@"官方网站: (.+?)\n", RegexOptions.Compiled);
|
Regex regSite = new Regex(@"官方网站: (.+?)\n", RegexOptions.Compiled);
|
||||||
Regex regNameMath = new Regex(@"(.+第\w季|[\w\uff1a\uff01\uff0c\u00b7]+)\s*(.*)", RegexOptions.Compiled);
|
|
||||||
Regex regRole = new Regex(@"\([饰|配]?\s*?(.+?)\)", RegexOptions.Compiled);
|
Regex regRole = new Regex(@"\([饰|配]?\s*?(.+?)\)", RegexOptions.Compiled);
|
||||||
Regex regBackgroundImage = new Regex(@"url\(([^)]+?)\)$", RegexOptions.Compiled);
|
Regex regBackgroundImage = new Regex(@"url\(([^)]+?)\)$", RegexOptions.Compiled);
|
||||||
Regex regGender = new Regex(@"性别: \n(.+?)\n", RegexOptions.Compiled);
|
Regex regLifedate = new Regex(@"(.+?) 至 (.+)", RegexOptions.Compiled);
|
||||||
Regex regConstellation = new Regex(@"星座: \n(.+?)\n", RegexOptions.Compiled);
|
Regex regHtmlTag = new Regex(@"<.?>", RegexOptions.Compiled);
|
||||||
Regex regBirthdate = new Regex(@"出生日期: \n(.+?)\n", RegexOptions.Compiled);
|
|
||||||
Regex regLifedate = new Regex(@"生卒日期: \n(.+?) 至 (.+)", RegexOptions.Compiled);
|
|
||||||
Regex regBirthplace = new Regex(@"出生地: \n(.+?)\n", RegexOptions.Compiled);
|
|
||||||
Regex regCelebrityRole = new Regex(@"职业: \n(.+?)\n", RegexOptions.Compiled);
|
|
||||||
Regex regNickname = new Regex(@"更多外文名: \n(.+?)\n", RegexOptions.Compiled);
|
|
||||||
Regex regFamily = new Regex(@"家庭成员: \n(.+?)\n", RegexOptions.Compiled);
|
|
||||||
Regex regCelebrityImdb = new Regex(@"imdb编号:\s+?(nm\d+)", RegexOptions.Compiled);
|
|
||||||
Regex regImgHost = new Regex(@"\/\/(img\d+?)\.", RegexOptions.Compiled);
|
Regex regImgHost = new Regex(@"\/\/(img\d+?)\.", RegexOptions.Compiled);
|
||||||
// 匹配除了换行符之外所有空白
|
// 匹配除了换行符之外所有空白
|
||||||
Regex regOverviewSpace = new Regex(@"\n[^\S\n]+", RegexOptions.Compiled);
|
Regex regOverviewSpace = new Regex(@"\n[^\S\n]+", RegexOptions.Compiled);
|
||||||
|
@ -90,7 +82,7 @@ namespace Jellyfin.Plugin.MetaShark.Api
|
||||||
var handler = new HttpClientHandlerEx();
|
var handler = new HttpClientHandlerEx();
|
||||||
this._cookieContainer = handler.CookieContainer;
|
this._cookieContainer = handler.CookieContainer;
|
||||||
httpClient = new HttpClient(handler);
|
httpClient = new HttpClient(handler);
|
||||||
httpClient.Timeout = TimeSpan.FromSeconds(10);
|
httpClient.Timeout = TimeSpan.FromSeconds(20);
|
||||||
httpClient.DefaultRequestHeaders.Add("User-Agent", HTTP_USER_AGENT);
|
httpClient.DefaultRequestHeaders.Add("User-Agent", HTTP_USER_AGENT);
|
||||||
httpClient.DefaultRequestHeaders.Add("Origin", "https://movie.douban.com");
|
httpClient.DefaultRequestHeaders.Add("Origin", "https://movie.douban.com");
|
||||||
httpClient.DefaultRequestHeaders.Add("Referer", "https://movie.douban.com/");
|
httpClient.DefaultRequestHeaders.Add("Referer", "https://movie.douban.com/");
|
||||||
|
@ -519,48 +511,64 @@ namespace Jellyfin.Plugin.MetaShark.Api
|
||||||
var contentNode = doc.QuerySelector("#content");
|
var contentNode = doc.QuerySelector("#content");
|
||||||
if (contentNode != null)
|
if (contentNode != null)
|
||||||
{
|
{
|
||||||
var img = contentNode.GetAttr("#headline .nbg img", "src") ?? string.Empty;
|
celebrity.Img = contentNode.GetAttr("img.avatar", "src") ?? string.Empty;
|
||||||
var nameStr = contentNode.GetText("h1") ?? string.Empty;
|
var nameStr = contentNode.GetText("h1.subject-name") ?? string.Empty;
|
||||||
var name = this.ParseCelebrityName(nameStr);
|
celebrity.Name = this.ParseCelebrityName(nameStr);
|
||||||
var englishName = nameStr.Replace(name, "").Trim();
|
celebrity.EnglishName = nameStr.Replace(celebrity.Name, "").Trim();
|
||||||
|
|
||||||
var intro = contentNode.GetText("#intro span.all") ?? string.Empty;
|
|
||||||
if (string.IsNullOrEmpty(intro))
|
var family = string.Empty;
|
||||||
|
var propertyNodes = contentNode.QuerySelectorAll("ul.subject-property>li");
|
||||||
|
foreach (var li in propertyNodes)
|
||||||
{
|
{
|
||||||
intro = contentNode.GetText("#intro div.bd") ?? string.Empty;
|
var label = li.GetText("span.label") ?? string.Empty;
|
||||||
}
|
var value = li.GetText("span.value") ?? string.Empty;
|
||||||
var info = contentNode.GetText("div.info") ?? string.Empty;
|
switch (label)
|
||||||
var gender = info.GetMatchGroup(this.regGender);
|
{
|
||||||
var constellation = info.GetMatchGroup(this.regConstellation);
|
case "性别:":
|
||||||
var birthdate = info.GetMatchGroup(this.regBirthdate);
|
celebrity.Gender = value;
|
||||||
|
break;
|
||||||
// 生卒日期
|
case "星座:":
|
||||||
var enddate = string.Empty;
|
celebrity.Constellation = value;
|
||||||
var match = this.regLifedate.Match(info);
|
break;
|
||||||
if (match.Success && match.Groups.Count > 2)
|
case "出生日期:":
|
||||||
{
|
celebrity.Birthdate = value;
|
||||||
birthdate = match.Groups[1].Value.Trim();
|
break;
|
||||||
enddate = match.Groups[2].Value.Trim();
|
case "去世日期:":
|
||||||
|
celebrity.Enddate = value;
|
||||||
|
break;
|
||||||
|
case "生卒日期:":
|
||||||
|
var match = this.regLifedate.Match(value);
|
||||||
|
if (match.Success && match.Groups.Count > 2)
|
||||||
|
{
|
||||||
|
celebrity.Birthdate = match.Groups[1].Value.Trim();
|
||||||
|
celebrity.Enddate = match.Groups[2].Value.Trim();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "出生地:":
|
||||||
|
celebrity.Birthplace = value;
|
||||||
|
break;
|
||||||
|
case "职业:":
|
||||||
|
celebrity.Role = value;
|
||||||
|
break;
|
||||||
|
case "更多外文名:":
|
||||||
|
celebrity.NickName = value;
|
||||||
|
break;
|
||||||
|
case "家庭成员:":
|
||||||
|
family = value;
|
||||||
|
break;
|
||||||
|
case "IMDb编号:":
|
||||||
|
celebrity.Imdb = value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var birthplace = info.GetMatchGroup(this.regBirthplace);
|
// 保留段落关系,把段落替换为换行符
|
||||||
var role = info.GetMatchGroup(this.regCelebrityRole);
|
var intro = contentNode.GetHtml("section.subject-intro div.content") ?? string.Empty;
|
||||||
var nickname = info.GetMatchGroup(this.regNickname);
|
intro = regHtmlTag.Replace(intro.Replace("</p>", "\n"), "");
|
||||||
var family = info.GetMatchGroup(this.regFamily);
|
|
||||||
var imdb = info.GetMatchGroup(this.regCelebrityImdb);
|
|
||||||
|
|
||||||
celebrity.Img = img;
|
|
||||||
celebrity.Gender = gender;
|
|
||||||
celebrity.Birthdate = birthdate;
|
|
||||||
celebrity.Enddate = enddate;
|
|
||||||
celebrity.NickName = nickname;
|
|
||||||
celebrity.EnglishName = englishName;
|
|
||||||
celebrity.Imdb = imdb;
|
|
||||||
celebrity.Birthplace = birthplace;
|
|
||||||
celebrity.Name = name;
|
|
||||||
celebrity.Intro = formatOverview(intro);
|
celebrity.Intro = formatOverview(intro);
|
||||||
celebrity.Constellation = constellation;
|
|
||||||
celebrity.Role = role;
|
|
||||||
_memoryCache.Set<DoubanCelebrity?>(cacheKey, celebrity, expiredOption);
|
_memoryCache.Set<DoubanCelebrity?>(cacheKey, celebrity, expiredOption);
|
||||||
return celebrity;
|
return celebrity;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,17 @@ namespace Jellyfin.Plugin.MetaShark.Core
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string? GetHtml(this IElement el, string css)
|
||||||
|
{
|
||||||
|
var node = el.QuerySelector(css);
|
||||||
|
if (node != null)
|
||||||
|
{
|
||||||
|
return node.Html().Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public static string GetTextOrDefault(this IElement el, string css, string defaultVal = "")
|
public static string GetTextOrDefault(this IElement el, string css, string defaultVal = "")
|
||||||
{
|
{
|
||||||
var node = el.QuerySelector(css);
|
var node = el.QuerySelector(css);
|
||||||
|
|
|
@ -108,7 +108,7 @@ namespace Jellyfin.Plugin.MetaShark.Core
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 假如Anitomy解析不到year,尝试使用jellyfin默认parser,看能不能解析成功
|
// 假如 Anitomy 解析不到 year,尝试使用 jellyfin 默认 parser,看能不能解析成功
|
||||||
if (parseResult.Year == null && !isAnime)
|
if (parseResult.Year == null && !isAnime)
|
||||||
{
|
{
|
||||||
var nativeParseResult = ParseMovieByDefault(fileName);
|
var nativeParseResult = ParseMovieByDefault(fileName);
|
||||||
|
@ -118,13 +118,22 @@ namespace Jellyfin.Plugin.MetaShark.Core
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 假如 Anitomy 解析不到集数,判断 name 是否是数字集号
|
||||||
|
if (parseResult.IndexNumber is null && isEpisode)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(parseResult.Name) && parseResult.Name.IsNumericString())
|
||||||
|
{
|
||||||
|
parseResult.IndexNumber = parseResult.Name.ToInt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 修复纯中文集数/特殊标识集数
|
// 修复纯中文集数/特殊标识集数
|
||||||
if (parseResult.IndexNumber is null)
|
if (parseResult.IndexNumber is null)
|
||||||
{
|
{
|
||||||
parseResult.IndexNumber = ParseChineseOrSpecialIndexNumber(fileName);
|
parseResult.IndexNumber = ParseChineseOrSpecialIndexNumber(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解析不到title时,或解析出多个title时,使用默认名
|
// 解析不到 title 时,或解析出多个 title 时,使用默认名
|
||||||
if (string.IsNullOrEmpty(parseResult.Name))
|
if (string.IsNullOrEmpty(parseResult.Name))
|
||||||
{
|
{
|
||||||
parseResult.Name = fileName;
|
parseResult.Name = fileName;
|
||||||
|
@ -133,6 +142,11 @@ namespace Jellyfin.Plugin.MetaShark.Core
|
||||||
return parseResult;
|
return parseResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ParseNameResult ParseEpisode(string fileName)
|
||||||
|
{
|
||||||
|
return Parse(fileName, true);
|
||||||
|
}
|
||||||
|
|
||||||
private static string CleanName(string name)
|
private static string CleanName(string name)
|
||||||
{
|
{
|
||||||
// 电视剧名称后紧跟季信息时,会附加到名称中,需要去掉
|
// 电视剧名称后紧跟季信息时,会附加到名称中,需要去掉
|
||||||
|
@ -173,9 +187,11 @@ namespace Jellyfin.Plugin.MetaShark.Core
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static EpisodePathParserResult ParseEpisodeByDefault(string fileName)
|
public static EpisodePathParserResult ParseEpisodeByDefault(string fileName)
|
||||||
{
|
{
|
||||||
|
// EpisodePathParser需要路径信息, 这里添加一个分隔符模拟路径
|
||||||
|
var path = Path.DirectorySeparatorChar + fileName;
|
||||||
var nameOptions = new Emby.Naming.Common.NamingOptions();
|
var nameOptions = new Emby.Naming.Common.NamingOptions();
|
||||||
return new EpisodePathParser(nameOptions)
|
return new EpisodePathParser(nameOptions)
|
||||||
.Parse(fileName, false);
|
.Parse(path, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -78,5 +78,10 @@ namespace Jellyfin.Plugin.MetaShark.Core
|
||||||
|
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool IsNumericString(this string str)
|
||||||
|
{
|
||||||
|
return str.All(char.IsDigit);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -135,7 +135,7 @@ namespace Jellyfin.Plugin.MetaShark.Providers
|
||||||
{
|
{
|
||||||
// 使用AnitomySharp进行重新解析,解决anime识别错误
|
// 使用AnitomySharp进行重新解析,解决anime识别错误
|
||||||
var fileName = Path.GetFileNameWithoutExtension(info.Path) ?? info.Name;
|
var fileName = Path.GetFileNameWithoutExtension(info.Path) ?? info.Name;
|
||||||
var parseResult = NameParser.Parse(fileName);
|
var parseResult = NameParser.ParseEpisode(fileName);
|
||||||
info.Year = parseResult.Year;
|
info.Year = parseResult.Year;
|
||||||
info.Name = parseResult.ChineseName ?? parseResult.Name;
|
info.Name = parseResult.ChineseName ?? parseResult.Name;
|
||||||
|
|
||||||
|
@ -192,14 +192,14 @@ namespace Jellyfin.Plugin.MetaShark.Providers
|
||||||
// info.ParentIndexNumber = 1;
|
// info.ParentIndexNumber = 1;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// 特典优先使用文件名(特典除了前面特别设置,还有SXX/Season XX等默认的)
|
// 特典优先使用文件名(特典除了前面特别设置,还有 SXX/Season XX 等默认的)
|
||||||
if (info.ParentIndexNumber.HasValue && info.ParentIndexNumber == 0)
|
if (info.ParentIndexNumber.HasValue && info.ParentIndexNumber == 0)
|
||||||
{
|
{
|
||||||
info.Name = parseResult.SpecialName == info.Name ? fileName : parseResult.SpecialName;
|
info.Name = parseResult.SpecialName == info.Name ? fileName : parseResult.SpecialName;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 大于1000,可能错误解析了分辨率
|
// 修正 episode number
|
||||||
if (parseResult.IndexNumber.HasValue && parseResult.IndexNumber < 1000 && info.IndexNumber != parseResult.IndexNumber)
|
if (parseResult.IndexNumber.HasValue && info.IndexNumber != parseResult.IndexNumber)
|
||||||
{
|
{
|
||||||
this.Log("FixEpisodeNumber by anitomy. old: {0} new: {1}", info.IndexNumber, parseResult.IndexNumber);
|
this.Log("FixEpisodeNumber by anitomy. old: {0} new: {1}", info.IndexNumber, parseResult.IndexNumber);
|
||||||
info.IndexNumber = parseResult.IndexNumber;
|
info.IndexNumber = parseResult.IndexNumber;
|
||||||
|
@ -214,7 +214,7 @@ namespace Jellyfin.Plugin.MetaShark.Providers
|
||||||
{
|
{
|
||||||
// 特典或extra视频可能和正片剧集放在同一目录
|
// 特典或extra视频可能和正片剧集放在同一目录
|
||||||
var fileName = Path.GetFileNameWithoutExtension(info.Path) ?? info.Name;
|
var fileName = Path.GetFileNameWithoutExtension(info.Path) ?? info.Name;
|
||||||
var parseResult = NameParser.Parse(fileName);
|
var parseResult = NameParser.ParseEpisode(fileName);
|
||||||
if (parseResult.IsExtra)
|
if (parseResult.IsExtra)
|
||||||
{
|
{
|
||||||
this.Log($"Found anime extra of [name]: {fileName}");
|
this.Log($"Found anime extra of [name]: {fileName}");
|
||||||
|
|
|
@ -120,8 +120,9 @@ namespace Jellyfin.Plugin.MetaShark.Providers
|
||||||
var findResult = await this._tmdbApi.FindByExternalIdAsync(c.Imdb, FindExternalSource.Imdb, info.MetadataLanguage, cancellationToken).ConfigureAwait(false);
|
var findResult = await this._tmdbApi.FindByExternalIdAsync(c.Imdb, FindExternalSource.Imdb, info.MetadataLanguage, cancellationToken).ConfigureAwait(false);
|
||||||
if (findResult?.PersonResults != null && findResult.PersonResults.Count > 0)
|
if (findResult?.PersonResults != null && findResult.PersonResults.Count > 0)
|
||||||
{
|
{
|
||||||
this.Log($"GetPersonMetadata of found tmdb [id]: {findResult.PersonResults[0].Id}");
|
var foundTmdbId = findResult.PersonResults.First().Id.ToString();
|
||||||
item.SetProviderId(MetadataProvider.Tmdb, $"{findResult.PersonResults[0].Id}");
|
this.Log($"GetPersonMetadata of found tmdb [id]: {foundTmdbId}");
|
||||||
|
item.SetProviderId(MetadataProvider.Tmdb, $"{foundTmdbId}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue