Optimize indentity mixed zh&en name

This commit is contained in:
cxfksword 2022-12-15 16:38:30 +08:00
parent b7ee0b956d
commit 488bf80551
15 changed files with 398 additions and 726 deletions

View File

@ -15,6 +15,7 @@ jobs:
actions: "close-issues" actions: "close-issues"
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
inactive-day: 30 inactive-day: 30
exclude-labels: "enhancement,bug"
close-reason: "not_planned" close-reason: "not_planned"
body: | body: |
This issue was closed due to inactive more than 30 days. You can reopen or recreate it if you think it should continue. This issue was closed due to inactive more than 30 days. You can reopen or recreate it if you think it should continue.

View File

@ -48,5 +48,26 @@ namespace Jellyfin.Plugin.MetaShark.Test
}).GetAwaiter().GetResult(); }).GetAwaiter().GetResult();
} }
[TestMethod]
public void TestGetMetadataAnime()
{
var info = new MovieInfo() { Name = "[SAIO-Raws] もののけ姫 Mononoke Hime [BD 1920x1036 HEVC-10bit OPUSx2 AC3]" };
var doubanApi = new DoubanApi(loggerFactory);
var tmdbApi = new TmdbApi(loggerFactory);
var omdbApi = new OmdbApi(loggerFactory);
var httpClientFactory = new DefaultHttpClientFactory();
var libraryManagerStub = new Mock<ILibraryManager>();
Task.Run(async () =>
{
var provider = new MovieProvider(httpClientFactory, loggerFactory, libraryManagerStub.Object, doubanApi, tmdbApi, omdbApi);
var result = await provider.GetMetadata(info, CancellationToken.None);
Assert.IsNotNull(result);
var str = result.ToJson();
Console.WriteLine(result.ToJson());
}).GetAwaiter().GetResult();
}
} }
} }

View File

@ -0,0 +1,133 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Jellyfin.Plugin.MetaShark.Api;
using Jellyfin.Plugin.MetaShark.Core;
using Jellyfin.Plugin.MetaShark.Model;
using Jellyfin.Plugin.MetaShark.Providers;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
namespace Jellyfin.Plugin.MetaShark.Test
{
[TestClass]
public class ParseNameTest
{
ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
builder.AddSimpleConsole(options =>
{
options.IncludeScopes = true;
options.SingleLine = true;
options.TimestampFormat = "hh:mm:ss ";
}));
[TestMethod]
public void TestMovieParse()
{
// 混合中英文
var fileName = "新世界.New.World.2013.BluRay.1080p.x265.10bit.MNHD-FRDS";
var parseResult = NameParser.Parse(fileName);
Console.WriteLine(parseResult.ToJson());
fileName = "V字仇杀队.V.for.Vendetta.2006";
parseResult = NameParser.Parse(fileName);
Console.WriteLine(parseResult.ToJson());
// 只英文
fileName = "New.World.2013.BluRay.1080p.x265.10bit.MNHD-FRDS";
parseResult = NameParser.Parse(fileName);
Console.WriteLine(parseResult.ToJson());
fileName = "Who.Am.I.1998.1080p.BluRay.x264.DTS-FGT";
parseResult = NameParser.Parse(fileName);
Console.WriteLine(parseResult.ToJson());
// 标题加年份
fileName = "V字仇杀队 (2006)";
parseResult = NameParser.Parse(fileName);
Console.WriteLine(parseResult.ToJson());
// anime
fileName = "[SAIO-Raws] もののけ姫 Mononoke Hime [BD 1920x1036 HEVC-10bit OPUSx2 AC3].mp4";
parseResult = NameParser.Parse(fileName);
Console.WriteLine(parseResult.ToJson());
}
[TestMethod]
public void TestTVSeriesParse()
{
// 混合中英文
var fileName = "新世界.New.World.2013.BluRay.1080p.x265.10bit.MNHD-FRDS";
var parseResult = NameParser.Parse(fileName);
Console.WriteLine(parseResult.ToJson());
// 混合中英文带副标题
fileName = "航海王:狂热行动.One.Piece.Stampede.2019.BD720P.X264.AAC.Japanese&Mandarin.CHS.Mp4Ba";
parseResult = NameParser.Parse(fileName);
Console.WriteLine(parseResult.ToJson());
// 只英文
fileName = "She-Hulk.Attorney.at.Law.S01.1080p.WEBRip.x265-RARBG";
parseResult = NameParser.Parse(fileName);
Console.WriteLine(parseResult.ToJson());
fileName = "Bright.Future.S01.2022.2160p.HDR.WEB-DL.H265.AAC-BlackTV[BTBTT]";
parseResult = NameParser.Parse(fileName);
Console.WriteLine(parseResult.ToJson());
fileName = "Back.to.the.Future.Part.II.1989.BluRay.1080p.x265.10bit.2Audio-MiniHD[BTBTT]";
parseResult = NameParser.Parse(fileName);
Console.WriteLine(parseResult.ToJson());
// anime混合中日文
fileName = "[异域-11番小队][罗马浴场 THERMAE_ROMAE][1-6+SP][BDRIP][720P][X264-10bit_AAC]";
parseResult = NameParser.Parse(fileName);
Console.WriteLine(parseResult.ToJson());
// anime
fileName = "[Nekomoe kissaten][Shin Ikkitousen][01-03][720p][CHT]";
parseResult = NameParser.Parse(fileName);
Console.WriteLine(parseResult.ToJson());
fileName = "[SAIO-Raws] Fullmetal Alchemist Brotherhood [BD 1920x1080 HEVC-10bit OPUS][2009]";
parseResult = NameParser.Parse(fileName);
Console.WriteLine(parseResult.ToJson());
}
[TestMethod]
public void TestEposideParse()
{
// 混合中英文
var fileName = "新世界.New.World.2013.BluRay.1080p.x265.10bit.MNHD-FRDS";
var parseResult = NameParser.Parse(fileName);
Console.WriteLine(parseResult.ToJson());
// 只英文
fileName = "She-Hulk.Attorney.At.Law.S01E01.1080p.WEBRip.x265-RARBG";
var anitomyResult = AnitomySharp.AnitomySharp.Parse(fileName);
Console.WriteLine(anitomyResult.ToJson());
parseResult = NameParser.Parse(fileName);
Console.WriteLine(parseResult.ToJson());
// anime
fileName = "[YYDM-11FANS][THERMAE_ROMAE][02][BDRIP][720P][X264-10bit_AAC][7FF2269F]";
parseResult = NameParser.Parse(fileName);
Console.WriteLine(parseResult.ToJson());
fileName = "[WMSUB][Detective Conan - Zeros Tea Time ][S01][E06][BIG5][1080P].mp4";
parseResult = NameParser.Parse(fileName);
Console.WriteLine(parseResult.ToJson());
}
}
}

View File

@ -48,6 +48,7 @@ namespace Jellyfin.Plugin.MetaShark.Api
Regex regSid = new Regex(@"sid: (\d+?),", RegexOptions.Compiled); Regex regSid = new Regex(@"sid: (\d+?),", RegexOptions.Compiled);
Regex regCat = new Regex(@"\[(.+?)\]", RegexOptions.Compiled); Regex regCat = new Regex(@"\[(.+?)\]", RegexOptions.Compiled);
Regex regYear = new Regex(@"(\d{4})", RegexOptions.Compiled); Regex regYear = new Regex(@"(\d{4})", RegexOptions.Compiled);
Regex regOriginalName = new Regex(@"原名[:](.+?)\s*?\/", RegexOptions.Compiled);
Regex regDirector = new Regex(@"导演: (.+?)\n", RegexOptions.Compiled); Regex regDirector = new Regex(@"导演: (.+?)\n", RegexOptions.Compiled);
Regex regWriter = new Regex(@"编剧: (.+?)\n", RegexOptions.Compiled); Regex regWriter = new Regex(@"编剧: (.+?)\n", RegexOptions.Compiled);
Regex regActor = new Regex(@"主演: (.+?)\n", RegexOptions.Compiled); Regex regActor = new Regex(@"主演: (.+?)\n", RegexOptions.Compiled);
@ -165,6 +166,7 @@ namespace Jellyfin.Plugin.MetaShark.Api
var cat = titleStr.GetMatchGroup(this.regCat); var cat = titleStr.GetMatchGroup(this.regCat);
var subjectStr = movieElement.GetText("div.rating-info>.subject-cast") ?? string.Empty; var subjectStr = movieElement.GetText("div.rating-info>.subject-cast") ?? string.Empty;
var year = subjectStr.GetMatchGroup(this.regYear); var year = subjectStr.GetMatchGroup(this.regYear);
var originalName = subjectStr.GetMatchGroup(this.regOriginalName);
var desc = movieElement.GetText("div.content>p") ?? string.Empty; var desc = movieElement.GetText("div.content>p") ?? string.Empty;
if (cat != "电影" && cat != "电视剧") if (cat != "电影" && cat != "电视剧")
{ {
@ -174,7 +176,7 @@ namespace Jellyfin.Plugin.MetaShark.Api
var movie = new DoubanSubject(); var movie = new DoubanSubject();
movie.Sid = sid; movie.Sid = sid;
movie.Name = name; movie.Name = name;
movie.OriginalName = subjectStr.Split("/").FirstOrDefault(a => a.Contains("原名:"),"").Replace("原名:",""); movie.OriginalName = !string.IsNullOrEmpty(originalName) ? originalName : name;
movie.Genre = cat; movie.Genre = cat;
movie.Category = cat; movie.Category = cat;
movie.Img = img; movie.Img = img;

View File

@ -57,7 +57,8 @@
<label class="inputLabel inputLabelUnfocused" for="TmdbHost">Api Host</label> <label class="inputLabel inputLabelUnfocused" for="TmdbHost">Api Host</label>
<input id="TmdbHost" name="TmdbHost" type="text" is="emby-input" <input id="TmdbHost" name="TmdbHost" type="text" is="emby-input"
placeholder="api.tmdb.org" /> placeholder="api.tmdb.org" />
<div class="fieldDescription">填写Api域名默认api.tmdb.org.(需重启才能生效)</div> <div class="fieldDescription">
填写Api域名可选api.tmdb.org/api.themoviedb.org默认api.tmdb.org.(需重启才能生效)</div>
</div> </div>
</fieldset> </fieldset>
<button is="emby-button" type="submit" class="raised button-submit block emby-button"> <button is="emby-button" type="submit" class="raised button-submit block emby-button">

View File

@ -0,0 +1,154 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Jellyfin.Plugin.MetaShark.Model;
namespace Jellyfin.Plugin.MetaShark.Core
{
public static class NameParser
{
private static readonly Regex yearReg = new Regex(@"[12][890][78901234][0-9]", RegexOptions.Compiled);
private static readonly Regex seasonSuffixReg = new Regex(@"[ .]S\d{1,2}$", RegexOptions.Compiled);
public static ParseNameResult Parse(string fileName, bool isTvSeries = false)
{
var parseResult = new ParseNameResult();
var anitomyResult = AnitomySharp.AnitomySharp.Parse(fileName);
foreach (var item in anitomyResult)
{
switch (item.Category)
{
case AnitomySharp.Element.ElementCategory.ElementAnimeTitle:
// 处理混合中英文的标题中文一般在最前面如V字仇杀队.V.for.Vendetta
char[] seperatorChars = { ' ', '.' };
var firstSpaceIndex = item.Value.IndexOfAny(seperatorChars);
if (firstSpaceIndex > 0)
{
var firstString = item.Value.Substring(0, firstSpaceIndex);
if (firstString.HasChinese())
{
parseResult.ChineseName = CleanName(firstString);
parseResult.Name = CleanName(item.Value.Substring(firstSpaceIndex + 1));
}
else
{
parseResult.Name = CleanName(item.Value);
}
}
else
{
parseResult.Name = CleanName(item.Value);
}
break;
case AnitomySharp.Element.ElementCategory.ElementEpisodeNumber:
var year = ParseYear(item.Value);
if (year > 0)
{
parseResult.Year = year;
}
else
{
var indexNumber = item.Value.ToInt();
if (indexNumber > 0)
{
parseResult.IndexNumber = item.Value.ToInt();
}
}
break;
case AnitomySharp.Element.ElementCategory.ElementAnimeType:
if (item.Value == "SP")
{
parseResult.IsSpecial = true;
}
break;
case AnitomySharp.Element.ElementCategory.ElementAnimeYear:
parseResult.Year = item.Value.ToInt();
break;
default:
break;
}
}
if (string.IsNullOrEmpty(parseResult.Name))
{
parseResult.Name = fileName;
}
return parseResult;
}
private static string CleanName(string name)
{
// 电视剧名称后紧跟季信息时,会附加到名称中,需要去掉
name = seasonSuffixReg.Replace(name, string.Empty);
return name.Replace(".", " ").Trim();
}
// emby原始电影解析
public static ParseNameResult ParseMovie(string fileName)
{
var parseResult = new ParseNameResult();
var nameOptions = new Emby.Naming.Common.NamingOptions();
var result = Emby.Naming.Video.VideoResolver.CleanDateTime(fileName, nameOptions);
if (Emby.Naming.Video.VideoResolver.TryCleanString(result.Name, nameOptions, out var cleanName))
{
parseResult.Name = cleanName;
parseResult.Year = result.Year;
}
else
{
parseResult.Name = result.Name;
parseResult.Year = result.Year;
}
return parseResult;
}
private static int ParseYear(string val)
{
var match = yearReg.Match(val);
if (match.Success && match.Groups.Count > 0)
{
return match.Groups[0].Value.ToInt();
}
return 0;
}
// 判断是否为动漫
// https://github.com/jxxghp/nas-tools/blob/f549c924558fd49e183333285bc6a804af1a2cb7/app/media/meta/metainfo.py#L51
private static bool IsAnime(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
return false;
}
if (Regex.Match(name, @"【[+0-9XVPI-]+】\s*【", RegexOptions.IgnoreCase).Success)
{
return true;
}
if (Regex.Match(name, @"\s+-\s+[\dv]{1,4}\s+", RegexOptions.IgnoreCase).Success)
{
return true;
}
if (Regex.Match(name, @"S\d{2}\s*-\s*S\d{2}|S\d{2}|\s+S\d{1,2}|EP?\d{2,4}\s*-\s*EP?\d{2,4}|EP?\d{2,4}|\s+EP?\d{1,4}", RegexOptions.IgnoreCase).Success)
{
return true;
}
if (Regex.Match(name, @"\[[+0-9XVPI-]+]\s*\[", RegexOptions.IgnoreCase).Success)
{
return true;
}
return false;
}
}
}

View File

@ -46,10 +46,21 @@ namespace Jellyfin.Plugin.MetaShark.Core
public static bool IsChinese(this string s) public static bool IsChinese(this string s)
{ {
Regex chineseReg = new Regex(@"[\u4e00-\u9fa5]{1,}", RegexOptions.Compiled); Regex chineseReg = new Regex(@"[\u4e00-\u9fa5]{1,}", RegexOptions.Compiled);
return chineseReg.IsMatch(s.Replace(" ", string.Empty).Trim()); return chineseReg.IsMatch(s.Replace(" ", string.Empty).Trim());
} }
public static bool HasChinese(this string s)
{
Regex chineseReg = new Regex(@"[\u4e00-\u9fa5]", RegexOptions.Compiled);
return chineseReg.Match(s).Success;
}
public static bool IsSameLanguage(this string s1, string s2)
{
return s1.IsChinese() && s2.IsChinese();
}
public static double Distance(this string s1, string s2) public static double Distance(this string s1, string s2)
{ {
var jw = new JaroWinkler(); var jw = new JaroWinkler();

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using MediaBrowser.Controller.Providers;
namespace Jellyfin.Plugin.MetaShark.Model
{
public class ParseNameResult : ItemLookupInfo
{
public string ChineseName { get; set; }
public bool IsSpecial { get; set; }
}
}

View File

@ -1,90 +0,0 @@
namespace Jellyfin.Plugin.MetaShark.Parser
{
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
public class BTNamePareser
{
private Regex btGroupReg = new Regex(@"\[[a-zA-Z\-]*\]", RegexOptions.Compiled);
private Regex yearReg = new Regex(@"[12][890][78901234][0-9]", RegexOptions.Compiled);
private Regex resoReg = new Regex(@"([0-9]{3,4}[pP])|([0-9]{3,4}[iI])|([hH][dD])|(4[kK])|([sS][dD])", RegexOptions.Compiled);
private Regex codeReg = new Regex(@"([hH]\.[0-9]{3})|([vV][cC]-?1)|([xX][vV][iI][dD])
|([mM][pP][eE][Gg]-?\d)|([fF][lL][aA][Cc])|([aA][pP][eE])|([dD][tT][sS])|([aA][cC]-?\d)|([wW][aA][vV])
|([mM][pP]\d)|([aA][lL][aA][cC])|([aA]{2}[cC])"
, RegexOptions.Compiled);
private Regex chineseReg = new Regex(@"[\u4e00-\u9fa5]{1,}", RegexOptions.Compiled);
private Regex serisReg = new Regex(@"([sS][0-9]{1,2})|([Ss][eE][rR][iI][sS][0-9]{1,2})", RegexOptions.Compiled);
private Regex episodeReg = new Regex(@"([eE][0-9]{1,3})|([Ee][pP][iI][sS][oO][dD][eE][0-9]{1,3})", RegexOptions.Compiled);
public class ResourceInfo
{
public string? Name { get; set; }
public string? ChineseName { get; set; }
public string? EnglishName { get; set; }
public string? Year { get; set; }
public string? Resolution { get; set; }
public string? Seris { get; set; }
public string? Episode { get; set; }
public bool isSeris()
{
return Seris != null || Episode != null;
}
}
public ResourceInfo Match(string btFileName, ILogger _logger)
{
NameTrimmer trimmer = new NameTrimmer();
var trimmedName = trimmer.trimName(btFileName, _logger);
var btgroup = GetMatch(trimmedName, btGroupReg);
var year = GetMatch(trimmedName, yearReg);
var reso = GetMatch(trimmedName, resoReg);
var code = GetMatch(trimmedName, codeReg);
var chinese = GetMatch(trimmedName, chineseReg);
var seris = GetMatch(trimmedName, serisReg);
var episode = GetMatch(trimmedName, episodeReg);
ResourceInfo info = new ResourceInfo();
info.ChineseName = chinese?.MatchContent;
info.Year = year?.MatchContent;
info.Seris = seris?.MatchContent;
info.Episode = episode?.MatchContent;
info.Resolution = reso?.MatchContent;
info.Name = trimmedName;
info.EnglishName = ReplaceMatch(trimmedName, "", chineseReg).Trim();
return info;
}
private class MatchResult
{
public int Index { get; set; }
public string MatchContent { get; set; }
}
private MatchResult? GetMatch(string text, Regex reg)
{
var match = reg.Match(text);
if (match.Success && match.Groups.Count > 0)
{
return new MatchResult
{
Index = match.Groups[0].Index,
MatchContent = match.Groups[0].Value.Trim(),
};
}
return null;
}
private string ReplaceMatch(string text, string replacement, Regex reg)
{
return reg.Replace(text, replacement);
}
}
}

View File

@ -1,133 +0,0 @@

namespace Jellyfin.Plugin.MetaShark.Parser
{
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
class NameTrimmer
{
private static TreeNode node;
private const string path = "/Parser/dict.txt";
public static string AssemblyDirectory
{
get
{
string codeBase = Assembly.GetExecutingAssembly().CodeBase;
UriBuilder uri = new UriBuilder(codeBase);
string path = Uri.UnescapeDataString(uri.Path);
return Path.GetDirectoryName(path);
}
}
public string trimName(string name, ILogger _logger)
{
if (NameTrimmer.node == null)
{
_logger.LogInformation($"生成词库树");
List<string> dics = new List<string>();
List<string> lines = File.ReadAllLines(AssemblyDirectory + path).ToList();
lines.ForEach(line =>
{
string[] words = line.Split(" ");
if (words.Length == 2)
{
dics.Add(words[0]);
}
});
NameTrimmer.node = new TreeNode();
node.insertMany(dics);
_logger.LogInformation($"生成词库树完成");
}
_logger.LogInformation($"解析名称");
foreach (char c in "!\"#$%&()*+,-./:;<=>?@[\\]^_{|}~")
{
name = name.Replace(c, ' ');
}
string[] nameWords = name.Split(' ');
string empStr = "";
_logger.LogInformation($"开始比对" + name);
foreach (string word in nameWords)
{
if (string.IsNullOrEmpty(word))
{
continue;
}
if (NameTrimmer.node.search(word.ToLower()))
{
continue;
}
else
{
empStr += word + " ";
}
}
_logger.LogInformation($"比对结果" + empStr);
return empStr;
}
private class TreeNode
{
private bool IsLeaf = false;
private List<TreeNode> nodes = new List<TreeNode>();
private char value;
public void insert(string word)
{
var currentNode = this;
foreach (char c in word)
{
var findNode = currentNode.nodes.FirstOrDefault(a => a.value == c);
if (findNode == null)
{
TreeNode newNode = new TreeNode();
newNode.value = c;
currentNode.nodes.Add(newNode);
currentNode = newNode;
}
else
{
currentNode = findNode;
}
}
currentNode.IsLeaf = true;
}
public void insertMany(List<string> words)
{
foreach (var work in words)
{
this.insert(work);
}
}
public bool search(string word)
{
var current = this;
foreach (char c in word)
{
if (current != null)
{
current = current.nodes.FirstOrDefault(a => a.value == c);
}
else
{
break;
}
}
return current != null ? current.IsLeaf : false;
}
}
}
}

View File

@ -1,419 +0,0 @@
www 3268
org 685
mp4 16210
cc 620
the 10098
web 4943
dlrip 539
x264 7897
720p 7931
mkv 7680
tv 774
rip 252
flac 1199
sis001 1584
movie 298
1080p 14008
nf 320
dl 2478
x265 2381
10bit 867
hdr 350
ddp5 748
sexinsex 351
net 857
dark 217
of 3905
3d 389
real 251
x64 264
com 5469
rmvb 1159
no 1867
on 728
live 427
iso 542
by 1886
1280x720 487
hevc 1810
aac 3110
x 833
family 377
multi 247
subs 342
thz 610
la 1151
heyzo 510
xxx 7648
2160p 1209
ktr 2665
rarbg 4056
art 335
little 382
digital 421
portable 237
war 290
with 1084
bluray 3310
h264 2757
dts 1075
xyz 411
fc2ppv 632
ts 514
zip 3188
you 452
it 423
va 303
music 216
mp3 996
320kbps 236
hd 2964
cum 305
avi 5544
cd 251
webrip 2874
yts 427
mx 331
dvdrip 2255
for 1090
teen 377
and 3868
fuck 491
480p 1169
black 846
in 2108
vol 1066
internal 215
n1c 362
to 1537
7z 372
ass 434
b 435
xvid 2726
ac3 1482
a 2816
two 214
i 979
bdrip 1771
rose 250
me 1077
1pondo 311
dual 516
raws 574
girl 660
gb 571
pack 352
edition 428
avc 946
new 919
life 335
h265 409
hardcore 267
red 269
fc2 426
ppv 448
o 397
white 342
imageset 249
all 555
season 690
hdtv 2013
pl 417
pedo 597
baby 310
eztv 1192
re 730
fhd 574
bbqddq 216
man 380
tgx 708
dd5 348
h 798
rartv 368
tokyo 546
hot 961
xbay 208
my 1027
game 268
rar 1740
deep 264
ru 376
sex 986
stars 233
team 370
audio 378
repack 446
her 547
best 450
dvd 575
rus 816
lostfilm 246
e 346
brrip 461
fucked 240
theav 244
wa 324
is 491
english 984
m 248
ita 510
eng 1380
sub 398
ii 315
pthc 1820
lolita 324
xvx 475
t 225
69av 252
one 554
divx 207
house 259
hdrip 601
bd 657
american 349
ntb 353
tvboxnow 413
chs 754
v 391
pdf 1202
f 216
complete 524
collection 598
ukr 201
video 409
studio 268
ptsc 291
blue 229
angel 316
bbc 224
time 336
series 264
prt 400
olo 512
s 595
de 750
preteen 507
anal 863
r 409
jpg 284
wmv 1489
ni 255
last 240
amzn 597
part 521
full 417
remux 297
ma 466
w 212
fucking 250
aavv333 256
big 927
at 396
hjd2048 287
sd 733
fgt 240
child 237
uncensored 384
boy 284
aac2 252
4k 489
cock 382
ion10 268
pussy 321
tits 217
c 813
djvu 279
wild 220
club 411
high 258
d 487
brazzers 265
megusta 235
dead 277
gog 238
out 248
christmas 214
private 253
top 239
wife 222
3dmgame 292
green 352
french 391
extreme 231
mpg 788
gets 250
first 308
korean 236
star 622
japanese 267
av 293
torrenting 483
up 357
l 226
young 364
sky 273
story 241
chinese 256
hd1080p 344
kleenex 326
big5 479
1920x1080 340
carib 292
v1 441
hindi 208
exe 407
day 319
pro 307
atmos 291
sexy 279
love 927
final 282
epub 475
night 286
vs 296
next 421
msd 209
us 295
en 498
porn 289
k 233
wrb 262
girls 579
nike 255
blu 316
ray 400
j 207
from 357
alexis 207
mide 205
world 385
san 278
g 281
hard 278
good 217
p 397
blonde 206
castellano 278
rq 282
cap 205
mov 243
cht 234
your 301
raw 212
milf 240
snis 245
avistaz 1
cinemaz 1
exoticaz 1
privatehd 1
aidoru!online 1
bibliotik 1
bwt 1
concertos 1
xtr 1
space 1
torrents 1
teamhd 1
empornium 1
六维空间 1
skyeysnow 1
ab 1
btn 1
ggn 1
gfxpeers 1
jpopsuki 1
lztr 1
nebulance 1
ptp 1
alpharatio 1
anthelion 1
bemaniso 1
brks 1
dic 1
gpw 1
forever 1
oppaitime 1
ops 1
red 1
snakepop 1
sugoimusic 1
uhdbits 1
cgpeers 1
iptorrents 1
torrentstd 1
1ptbar 1
52pt 1
爱薇网 1
byrbt 1
chdbits 1
discfan 1
torrentccf 1
hdai 1
hdatmos 1
hdchina 1
hdcity 1
dolby 1
hdfans 1
hdhome 1
hdsky 1
hdtime 1
hdzone 1
海棠pt 1
hudbt 1
joyhd 1
mteam 1
lemonhd 1
南洋pt 1
nicept 1
npubits 1
opencd 1
ourbits 1
btschool 1
tlfbits 1
hd4fans 1
伊甸园 1
hdu 1
pt@keepfrds 1
ptmsg 1
麦田pt 1
葡萄 1
聆音club 1
溪涧草堂pt 1
pter 1
pthome 1
烧包 1
ssd 1
北洋园 1
u2 1
ultrahd 1
备胎 1
haidan 1
hdarea 1
百川pt 1
pttime 1
aither 1
asiancinema 1
beyondhd 1
blutopia 1
jptv 1
hdpost 1
animetorrents 1
bb 1
ccfbits 1
cinemageddon 1
filelist 1
hdb 1
hdroute 1
karagarga 1
pornbits 1
pussytorrents 1
sdbits 1
intheshadow 1
tg 1
ttg 1
cinematik 1
mtv 1
myanonamouse 1
torrentleech 1
torrentseeds 1
ptt 1

View File

@ -21,7 +21,6 @@ using System.Web;
using TMDbLib.Objects.General; using TMDbLib.Objects.General;
using Jellyfin.Plugin.MetaShark.Configuration; using Jellyfin.Plugin.MetaShark.Configuration;
using Jellyfin.Plugin.MetaShark.Core; using Jellyfin.Plugin.MetaShark.Core;
using Jellyfin.Plugin.MetaShark.Parser;
namespace Jellyfin.Plugin.MetaShark.Providers namespace Jellyfin.Plugin.MetaShark.Providers
{ {
@ -81,16 +80,13 @@ namespace Jellyfin.Plugin.MetaShark.Providers
{ {
// ParseName is required here. // ParseName is required here.
// Caller provides the filename with extension stripped and NOT the parsed filename // Caller provides the filename with extension stripped and NOT the parsed filename
BTNamePareser pareser = new BTNamePareser(); var fileName = Path.GetFileName(info.Path) ?? info.Name;
var search_info = pareser.Match(info.Name, this._logger); var parseResult = NameParser.Parse(fileName);
string searchName = search_info.ChineseName != null ? search_info.ChineseName : search_info.EnglishName; var searchName = !string.IsNullOrEmpty(parseResult.ChineseName) ? parseResult.ChineseName : parseResult.Name;
info.Year = parseResult.Year; // 默认parser对anime年份会解析出错以anitomy为准
if (info.Year == null && search_info.Year != null)
{
info.Year = int.Parse(search_info.Year);
}
this.Log($"GuessByDouban of [name]: {info.Name} year: {info.Year} search name: {searchName}"); this.Log($"GuessByDouban of [name]: {info.Name} [fileName]: {fileName} [year]: {info.Year} [search name]: {searchName}");
var result = await this._doubanApi.SearchAsync(searchName, cancellationToken).ConfigureAwait(false); var result = await this._doubanApi.SearchAsync(searchName, cancellationToken).ConfigureAwait(false);
var jw = new JaroWinkler(); var jw = new JaroWinkler();
foreach (var item in result) foreach (var item in result)
@ -104,24 +100,33 @@ namespace Jellyfin.Plugin.MetaShark.Providers
{ {
continue; continue;
} }
//英文关键词搜,结果是中文的情况,不适用相似匹配
if (jw.Similarity(searchName, item.Name) > 0.8
|| jw.Similarity(searchName, item.OriginalName) > 0.8) //英文关键词搜,结果是只有中文/繁体中文时不适用相似匹配如Who Am I
if (jw.Similarity(searchName, item.Name) < 0.8 && jw.Similarity(searchName, item.OriginalName) < 0.8)
{
if (!searchName.IsSameLanguage(item.Name) && !searchName.IsSameLanguage(item.OriginalName))
{
// 特殊处理下使用英文搜索,只有中文标题的情况
}
else
{
continue;
}
}
// 不存在年份需要比较时,直接返回
if (info.Year == null || info.Year == 0)
{ {
this.Log($"GuessByDouban of [name] found Sid: {item.Sid}"); this.Log($"GuessByDouban of [name] found Sid: {item.Sid}");
return item.Sid; return item.Sid;
} }
if (item.Name.Contains(searchName) && (info.Year != null && info.Year == item.Year))
if (info.Year == item.Year)
{ {
this.Log($"GuessByDouban of [name] found Sid: {item.Sid}"); this.Log($"GuessByDouban of [name] found Sid: {item.Sid}");
return item.Sid; return item.Sid;
} }
if (searchName.IsChinese() != item.Name.IsChinese()
&& searchName.IsChinese() != item.OriginalName.IsChinese())
{
this.Log($"GuessByDouban of [name] found tmdb id: \"{item.Sid}\"");
return item.Sid;
}
} }
@ -135,7 +140,7 @@ namespace Jellyfin.Plugin.MetaShark.Providers
return null; return null;
} }
this.Log($"GuestDoubanSeasonByYear of [name]: {name} year: {year}"); this.Log($"GuestDoubanSeasonByYear of [name]: {name} [year]: {year}");
var result = await this._doubanApi.SearchAsync(name, cancellationToken).ConfigureAwait(false); var result = await this._doubanApi.SearchAsync(name, cancellationToken).ConfigureAwait(false);
var jw = new JaroWinkler(); var jw = new JaroWinkler();
foreach (var item in result) foreach (var item in result)
@ -144,15 +149,22 @@ namespace Jellyfin.Plugin.MetaShark.Providers
{ {
continue; continue;
} }
// this.Log($"GuestDoubanSeasonByYear name: {name} douban_name: {item.Name} douban_sid: {item.Sid} douban_year: {item.Year} score: {score} "); // this.Log($"GuestDoubanSeasonByYear name: {name} douban_name: {item.Name} douban_sid: {item.Sid} douban_year: {item.Year} score: {score} ");
if (jw.Similarity(name, item.Name) > 0.8) //英文关键词搜,结果是只有中文/繁体中文时不适用相似匹配如Who Am I
if (jw.Similarity(name, item.Name) < 0.8 && jw.Similarity(name, item.OriginalName) < 0.8)
{ {
this.Log($"GuestDoubanSeasonByYear of [name] found Sid: {item.Sid}"); if (!name.IsSameLanguage(item.Name) && !name.IsSameLanguage(item.OriginalName))
return item.Sid; {
// 特殊处理下使用英文搜索,只有中文标题的情况
}
else
{
continue;
}
} }
if ((name.IsChinese() != item.Name.IsChinese() if (year == item.Year)
&& name.IsChinese() != item.OriginalName.IsChinese()) || year == item.Year)
{ {
this.Log($"GuestDoubanSeasonByYear of [name] found Sid: \"{item.Sid}\""); this.Log($"GuestDoubanSeasonByYear of [name] found Sid: \"{item.Sid}\"");
return item.Sid; return item.Sid;
@ -162,61 +174,17 @@ namespace Jellyfin.Plugin.MetaShark.Providers
return null; return null;
} }
// 通过季数,搜索结果按年份排序后,取对应季数索引项(不适合每季标题差异太大的,如葫芦兄弟和葫芦小金刚)
protected async Task<string?> GuestDoubanSeasonByNumberAsync(string name, int? seasonNumber, CancellationToken cancellationToken)
{
if (seasonNumber == null || seasonNumber == 0)
{
return null;
}
this.Log($"GuestDoubanSeasonByNumber of [name]: {name} seasonNumber: {seasonNumber}");
var result = await this._doubanApi.SearchAsync(name, cancellationToken).ConfigureAwait(false);
var jw = new JaroWinkler();
var matchList = new List<DoubanSubject>();
foreach (var item in result)
{
if (item.Category != "电视剧")
{
continue;
}
var score = jw.Similarity(name, item.Name);
if (score < 0.8)
{
continue;
}
// this.Log($"GuestDoubanSeasonByNumber name: {name} douban_name: {item.Name} douban_sid: {item.Sid} douban_year: {item.Year} score: {score} ");
matchList.Add(item);
}
matchList.Sort((x, y) => x.Year.CompareTo(y.Year));
if (matchList.Count >= seasonNumber)
{
var matchItem = matchList[seasonNumber.Value - 1];
var sid = matchItem.Sid;
this.Log($"GuestDoubanSeasonByNumber of [name] found Sid: {sid}");
return sid;
}
return null;
}
protected async Task<string?> GuestByTmdbAsync(ItemLookupInfo info, CancellationToken cancellationToken) protected async Task<string?> GuestByTmdbAsync(ItemLookupInfo info, CancellationToken cancellationToken)
{ {
// ParseName is required here. // ParseName is required here.
// Caller provides the filename with extension stripped and NOT the parsed filename // Caller provides the filename with extension stripped and NOT the parsed filename
BTNamePareser pareser = new BTNamePareser(); var fileName = Path.GetFileName(info.Path) ?? info.Name;
var search_info = pareser.Match(info.Name, this._logger); var parseResult = NameParser.Parse(fileName);
string searchName = search_info.ChineseName != null ? search_info.ChineseName : search_info.EnglishName; var searchName = !string.IsNullOrEmpty(parseResult.ChineseName) ? parseResult.ChineseName : parseResult.Name;
info.Year = parseResult.Year; // 默认parser对anime年份会解析出错以anitomy为准
if (info.Year == null && search_info.Year != null) this.Log($"GuestByTmdb of [name]: {info.Name} [fileName]: {fileName} [year]: {info.Year} [search name]: {searchName}");
{
info.Year = int.Parse(search_info.Year);
}
this.Log($"GuestByTmdb of [name]: {info.Name} search name: {searchName}");
var jw = new JaroWinkler(); var jw = new JaroWinkler();
switch (info) switch (info)
@ -231,8 +199,8 @@ namespace Jellyfin.Plugin.MetaShark.Providers
this.Log($"GuestByTmdb of [name] found tmdb id: \"{item.Id}\""); this.Log($"GuestByTmdb of [name] found tmdb id: \"{item.Id}\"");
return item.Id.ToString(CultureInfo.InvariantCulture); return item.Id.ToString(CultureInfo.InvariantCulture);
} }
if (searchName.IsChinese() != item.Title.IsChinese() // 特殊处理下使用英文搜索,只有中文标题的情况,当匹配成功
&& searchName.IsChinese() != item.OriginalTitle.IsChinese()) if (!searchName.IsSameLanguage(item.Title) && !searchName.IsSameLanguage(item.OriginalTitle))
{ {
this.Log($"GuestByTmdb of [name] found tmdb id: \"{item.Id}\""); this.Log($"GuestByTmdb of [name] found tmdb id: \"{item.Id}\"");
return item.Id.ToString(CultureInfo.InvariantCulture); return item.Id.ToString(CultureInfo.InvariantCulture);
@ -249,8 +217,8 @@ namespace Jellyfin.Plugin.MetaShark.Providers
this.Log($"GuestByTmdb of [name] found tmdb id: \"{item.Id}\""); this.Log($"GuestByTmdb of [name] found tmdb id: \"{item.Id}\"");
return item.Id.ToString(CultureInfo.InvariantCulture); return item.Id.ToString(CultureInfo.InvariantCulture);
} }
if (searchName.IsChinese() != item.Name.IsChinese() // 特殊处理下使用英文搜索,只有中文标题的情况,当匹配成功
&& searchName.IsChinese() != item.OriginalName.IsChinese()) if (!searchName.IsSameLanguage(item.Name) && !searchName.IsSameLanguage(item.OriginalName))
{ {
this.Log($"GuestByTmdb of [name] found tmdb id: \"{item.Id}\""); this.Log($"GuestByTmdb of [name] found tmdb id: \"{item.Id}\"");
return item.Id.ToString(CultureInfo.InvariantCulture); return item.Id.ToString(CultureInfo.InvariantCulture);

View File

@ -95,7 +95,7 @@ namespace Jellyfin.Plugin.MetaShark.Providers
var metaSource = info.GetProviderId(Plugin.ProviderId); // 刷新元数据时会有值 var metaSource = info.GetProviderId(Plugin.ProviderId); // 刷新元数据时会有值
if (string.IsNullOrEmpty(sid) && string.IsNullOrEmpty(tmdbId)) if (string.IsNullOrEmpty(sid) && string.IsNullOrEmpty(tmdbId))
{ {
// 自动扫描匹配搜索 // 自动扫描搜索匹配元数据
sid = await this.GuessByDoubanAsync(info, cancellationToken).ConfigureAwait(false); sid = await this.GuessByDoubanAsync(info, cancellationToken).ConfigureAwait(false);
if (string.IsNullOrEmpty(sid)) if (string.IsNullOrEmpty(sid))
{ {

View File

@ -88,7 +88,7 @@ namespace Jellyfin.Plugin.MetaShark.Providers
var metaSource = info.GetProviderId(Plugin.ProviderId); // 刷新元数据时会有值 var metaSource = info.GetProviderId(Plugin.ProviderId); // 刷新元数据时会有值
if (string.IsNullOrEmpty(sid) && string.IsNullOrEmpty(tmdbId)) if (string.IsNullOrEmpty(sid) && string.IsNullOrEmpty(tmdbId))
{ {
// 刷新元数据自动匹配搜索 // 自动扫描搜索匹配元数据
sid = await this.GuessByDoubanAsync(info, cancellationToken).ConfigureAwait(false); sid = await this.GuessByDoubanAsync(info, cancellationToken).ConfigureAwait(false);
if (string.IsNullOrEmpty(sid)) if (string.IsNullOrEmpty(sid))
{ {

View File

@ -57,5 +57,11 @@ rm -rf MediaBrowser*.dll Microsoft*.dll Newtonsoft*.dll System*.dll Emby*.dll Je
2. Create a folder, like `metashark` and copy `artifacts/*.dll` into it 2. Create a folder, like `metashark` and copy `artifacts/*.dll` into it
3. Move folder `metashark` to jellyfin `data/plugin` folder 3. Move folder `metashark` to jellyfin `data/plugins` folder
## QA
1. Plugin run in error: `System.BadImageFormatException: Bad IL format.`
> Remove all hidden file in `metashark` plugin folder