diff --git a/Jellyfin.MeiamSub.Shooter/Configuration/PluginConfiguration.cs b/Jellyfin.MeiamSub.Shooter/Configuration/PluginConfiguration.cs
new file mode 100644
index 0000000..59bbbf7
--- /dev/null
+++ b/Jellyfin.MeiamSub.Shooter/Configuration/PluginConfiguration.cs
@@ -0,0 +1,12 @@
+using MediaBrowser.Model.Plugins;
+
+namespace Jellyfin.MeiamSub.Shooter.Configuration
+{
+ ///
+ /// Plugin configuration.
+ ///
+ public class PluginConfiguration : BasePluginConfiguration
+ {
+
+ }
+}
diff --git a/Jellyfin.MeiamSub.Shooter/Jellyfin.MeiamSub.Shooter.csproj b/Jellyfin.MeiamSub.Shooter/Jellyfin.MeiamSub.Shooter.csproj
new file mode 100644
index 0000000..dd14ac0
--- /dev/null
+++ b/Jellyfin.MeiamSub.Shooter/Jellyfin.MeiamSub.Shooter.csproj
@@ -0,0 +1,11 @@
+
+
+
+ net5.0
+
+
+
+
+
+
+
diff --git a/Jellyfin.MeiamSub.Shooter/Model/DownloadSubInfo.cs b/Jellyfin.MeiamSub.Shooter/Model/DownloadSubInfo.cs
new file mode 100644
index 0000000..55b5f22
--- /dev/null
+++ b/Jellyfin.MeiamSub.Shooter/Model/DownloadSubInfo.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Jellyfin.MeiamSub.Shooter.Model
+{
+ public class DownloadSubInfo
+ {
+ public string Url { get; set; }
+ public string Format { get; set; }
+ public string Language { get; set; }
+ public string TwoLetterISOLanguageName { get; set; }
+ public bool? IsForced { get; set; }
+ }
+}
diff --git a/Jellyfin.MeiamSub.Shooter/Model/SubtitleResponseRoot.cs b/Jellyfin.MeiamSub.Shooter/Model/SubtitleResponseRoot.cs
new file mode 100644
index 0000000..6a312b5
--- /dev/null
+++ b/Jellyfin.MeiamSub.Shooter/Model/SubtitleResponseRoot.cs
@@ -0,0 +1,19 @@
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Jellyfin.MeiamSub.Shooter.Model
+{
+ public class SubtitleResponseRoot
+ {
+ public string Desc { get; set; }
+ public int Delay { get; set; }
+ public SubFileInfo[] Files { get; set; }
+ }
+
+ public class SubFileInfo
+ {
+ public string Ext { get; set; }
+ public string Link { get; set; }
+ }
+
+}
diff --git a/Jellyfin.MeiamSub.Shooter/Plugin.cs b/Jellyfin.MeiamSub.Shooter/Plugin.cs
new file mode 100644
index 0000000..bf84473
--- /dev/null
+++ b/Jellyfin.MeiamSub.Shooter/Plugin.cs
@@ -0,0 +1,41 @@
+using Jellyfin.MeiamSub.Shooter.Configuration;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Plugins;
+using MediaBrowser.Model.Drawing;
+using MediaBrowser.Model.Serialization;
+using System;
+using System.IO;
+
+namespace Jellyfin.MeiamSub.Shooter
+{
+
+ ///
+ /// 插件入口
+ ///
+ public class Plugin : BasePlugin
+ {
+ ///
+ /// 插件ID
+ ///
+ public override Guid Id => new Guid("038D37A2-7A1E-4C01-9B6D-AA215D29AB4C");
+
+ ///
+ /// 插件名称
+ ///
+ public override string Name => "MeiamSub.Shooter";
+
+ ///
+ /// 插件描述
+ ///
+ public override string Description => "Download subtitles from Shooter";
+
+ public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer)
+ : base(applicationPaths, xmlSerializer)
+ {
+ Instance = this;
+ }
+
+ public static Plugin Instance { get; private set; }
+
+ }
+}
diff --git a/Jellyfin.MeiamSub.Shooter/ShooterProvider.cs b/Jellyfin.MeiamSub.Shooter/ShooterProvider.cs
new file mode 100644
index 0000000..aac9b3b
--- /dev/null
+++ b/Jellyfin.MeiamSub.Shooter/ShooterProvider.cs
@@ -0,0 +1,309 @@
+using Jellyfin.MeiamSub.Shooter.Model;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Controller.Subtitles;
+using MediaBrowser.Model.Providers;
+using MediaBrowser.Model.Serialization;
+using Microsoft.Extensions.Logging;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Net.Http.Json;
+using System.Security.Cryptography;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Web;
+
+namespace Jellyfin.MeiamSub.Shooter
+{
+ ///
+ /// 迅雷字幕组件
+ ///
+ public class ShooterProvider : ISubtitleProvider, IHasOrder
+ {
+ #region 变量声明
+ public const string ASS = "ass";
+ public const string SSA = "ssa";
+ public const string SRT = "srt";
+
+ private readonly ILogger _logger;
+ private readonly IJsonSerializer _jsonSerializer;
+ private static readonly HttpClient Client = new HttpClient();
+
+ public int Order => 0;
+ public string Name => "MeiamSub.Shooter";
+
+ ///
+ /// 支持电影、剧集
+ ///
+ public IEnumerable SupportedMediaTypes => new List() { VideoContentType.Movie, VideoContentType.Episode };
+ #endregion
+
+ #region 构造函数
+ public ShooterProvider(ILogger logger, IJsonSerializer jsonSerializer)
+ {
+ _logger = logger;
+ _jsonSerializer = jsonSerializer;
+ }
+ #endregion
+
+ #region 查询字幕
+
+ ///
+ /// 查询请求
+ ///
+ ///
+ ///
+ ///
+ public async Task> Search(SubtitleSearchRequest request, CancellationToken cancellationToken)
+ {
+ _logger.LogInformation($"MeiamSub.Shooter Search | Request -> { _jsonSerializer.SerializeToString(request) }");
+
+ var subtitles = await SearchSubtitlesAsync(request);
+
+ return subtitles;
+ }
+
+ ///
+ /// 查询字幕
+ ///
+ ///
+ ///
+ private async Task> SearchSubtitlesAsync(SubtitleSearchRequest request)
+ {
+ if (request.Language != "chi" && request.Language != "eng")
+ {
+ return Array.Empty();
+ }
+
+ FileInfo fileInfo = new FileInfo(request.MediaPath);
+
+ var hash = ComputeFileHash(fileInfo);
+
+ using (var _httpClient = new HttpClient())
+ {
+ var options = new {
+
+ filehash = HttpUtility.UrlEncode(hash),
+ pathinfo = HttpUtility.UrlEncode(request.MediaPath),
+ format = "json",
+ lang = request.Language == "chi" ? "chn" : "eng"
+ };
+
+ _logger.LogInformation($"MeiamSub.Shooter Search | Request -> { _jsonSerializer.SerializeToString(options) }");
+
+ var response = await PostAsync($"http://www.shooter.cn/api/subapi.php", options);
+
+ _logger.LogInformation($"MeiamSub.Shooter Search | Response -> { _jsonSerializer.SerializeToString(response) }");
+
+ if (response.StatusCode == HttpStatusCode.OK && response.Headers.Any(m => m.Value.Contains("application/json")))
+ {
+ var subtitleResponse = _jsonSerializer.DeserializeFromStream>(await response.Content.ReadAsStreamAsync());
+
+ if (subtitleResponse != null)
+ {
+ _logger.LogInformation($"MeiamSub.Shooter Search | Response -> { _jsonSerializer.SerializeToString(subtitleResponse) }");
+
+ var remoteSubtitleInfos = new List();
+
+ foreach (var subFileInfo in subtitleResponse)
+ {
+ foreach (var subFile in subFileInfo.Files)
+ {
+ remoteSubtitleInfos.Add(new RemoteSubtitleInfo()
+ {
+ Id = Base64Encode(_jsonSerializer.SerializeToString(new DownloadSubInfo
+ {
+ Url = subFile.Link,
+ Format = subFile.Ext,
+ Language = request.Language,
+ TwoLetterISOLanguageName = request.TwoLetterISOLanguageName,
+ })),
+ Name = $"[MEIAMSUB] { Path.GetFileName(request.MediaPath) } | {request.TwoLetterISOLanguageName} | 射手",
+ Author = "Meiam ",
+ ProviderName = "MeiamSub.Shooter",
+ Format = subFile.Ext,
+ Comment = $"Format : { ExtractFormat(subFile.Ext)}"
+ });
+ }
+ }
+
+ _logger.LogInformation($"MeiamSub.Shooter Search | Summary -> Get { remoteSubtitleInfos.Count } Subtitles");
+
+ return remoteSubtitleInfos;
+ }
+ }
+
+ _logger.LogInformation($"MeiamSub.Shooter Search | Summary -> Get 0 Subtitles");
+
+ }
+ return Array.Empty();
+ }
+ #endregion
+
+ #region 下载字幕
+ ///
+ /// 下载请求
+ ///
+ ///
+ ///
+ ///
+ public async Task GetSubtitles(string id, CancellationToken cancellationToken)
+ {
+ await Task.Run(() =>
+ {
+ _logger.LogInformation($"MeiamSub.Shooter DownloadSub | Request -> {id}");
+ });
+
+ return await DownloadSubAsync(id);
+ }
+
+ ///
+ /// 下载字幕
+ ///
+ ///
+ ///
+ private async Task DownloadSubAsync(string info)
+ {
+ var downloadSub = _jsonSerializer.DeserializeFromString(Base64Decode(info));
+
+ downloadSub.Url = downloadSub.Url.Replace("https://www.shooter.cn", "http://www.shooter.cn");
+
+ _logger.LogInformation($"MeiamSub.Shooter DownloadSub | Url -> { downloadSub.Url } | Format -> { downloadSub.Format } | Language -> { downloadSub.Language } ");
+
+ using (var _httpClient = new HttpClient())
+ {
+
+ var response = await _httpClient.GetAsync(downloadSub.Url);
+
+ _logger.LogInformation($"MeiamSub.Shooter DownloadSub | Response -> { response.StatusCode }");
+
+ if (response.StatusCode == HttpStatusCode.OK)
+ {
+
+ return new SubtitleResponse()
+ {
+ Language = downloadSub.Language,
+ IsForced = false,
+ Format = downloadSub.Format,
+ Stream = await response.Content.ReadAsStreamAsync(),
+ };
+ }
+ }
+ return new SubtitleResponse();
+
+ }
+ #endregion
+
+ #region 内部方法
+
+ ///
+ /// Base64 加密
+ ///
+ /// 明文
+ ///
+ public static string Base64Encode(string plainText)
+ {
+ var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
+ return Convert.ToBase64String(plainTextBytes);
+ }
+ ///
+ /// Base64 解密
+ ///
+ ///
+ ///
+ public static string Base64Decode(string base64EncodedData)
+ {
+ var base64EncodedBytes = Convert.FromBase64String(base64EncodedData);
+ return Encoding.UTF8.GetString(base64EncodedBytes);
+ }
+
+ ///
+ /// 提取格式化字幕类型
+ ///
+ ///
+ ///
+ protected string ExtractFormat(string text)
+ {
+
+ string result = null;
+
+ if (text != null)
+ {
+ text = text.ToLower();
+ if (text.Contains(ASS)) result = ASS;
+ else if (text.Contains(SSA)) result = SSA;
+ else if (text.Contains(SRT)) result = SRT;
+ else result = null;
+ }
+ return result;
+ }
+
+ ///
+ /// 获取文件 Hash (射手)
+ ///
+ ///
+ ///
+ public static string ComputeFileHash(FileInfo fileInfo)
+ {
+ string ret = "";
+
+ if (!fileInfo.Exists || fileInfo.Length < 8 * 1024)
+ {
+ return ret;
+ }
+
+ FileStream fs = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read);
+
+ long[] offset = new long[4];
+ offset[3] = fileInfo.Length - 8 * 1024;
+ offset[2] = fileInfo.Length / 3;
+ offset[1] = fileInfo.Length / 3 * 2;
+ offset[0] = 4 * 1024;
+
+ byte[] bBuf = new byte[1024 * 4];
+
+ for (int i = 0; i < 4; ++i)
+ {
+ fs.Seek(offset[i], 0);
+ fs.Read(bBuf, 0, 4 * 1024);
+
+ MD5 md5Hash = MD5.Create();
+ byte[] data = md5Hash.ComputeHash(bBuf);
+ StringBuilder sBuilder = new StringBuilder();
+
+ for (int j = 0; j < data.Length; j++)
+ {
+ sBuilder.Append(data[j].ToString("x2"));
+ }
+
+ if (!string.IsNullOrEmpty(ret))
+ {
+ ret += ";";
+ }
+
+ ret += sBuilder.ToString();
+ }
+
+ fs.Close();
+
+ return ret;
+ }
+ #endregion
+
+ #region HTTP
+ public async Task PostAsync(string url, object data)
+ {
+ string content = _jsonSerializer.SerializeToString(data);
+ var buffer = Encoding.UTF8.GetBytes(content);
+ var byteContent = new ByteArrayContent(buffer);
+ byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
+ return await Client.PostAsync(url, byteContent);
+ }
+ #endregion
+ }
+}
diff --git a/Jellyfin.MeiamSub.Thunder/Configuration/PluginConfiguration.cs b/Jellyfin.MeiamSub.Thunder/Configuration/PluginConfiguration.cs
new file mode 100644
index 0000000..761ff4e
--- /dev/null
+++ b/Jellyfin.MeiamSub.Thunder/Configuration/PluginConfiguration.cs
@@ -0,0 +1,12 @@
+using MediaBrowser.Model.Plugins;
+
+namespace Jellyfin.MeiamSub.Thunder.Configuration
+{
+ ///
+ /// Plugin configuration.
+ ///
+ public class PluginConfiguration : BasePluginConfiguration
+ {
+
+ }
+}
diff --git a/Jellyfin.MeiamSub.Thunder/Jellyfin.MeiamSub.Thunder.csproj b/Jellyfin.MeiamSub.Thunder/Jellyfin.MeiamSub.Thunder.csproj
new file mode 100644
index 0000000..1a74402
--- /dev/null
+++ b/Jellyfin.MeiamSub.Thunder/Jellyfin.MeiamSub.Thunder.csproj
@@ -0,0 +1,14 @@
+
+
+
+ Library
+ net5.0
+
+
+
+
+
+
+
+
+
diff --git a/Jellyfin.MeiamSub.Thunder/Model/DownloadSubInfo.cs b/Jellyfin.MeiamSub.Thunder/Model/DownloadSubInfo.cs
new file mode 100644
index 0000000..86b3d53
--- /dev/null
+++ b/Jellyfin.MeiamSub.Thunder/Model/DownloadSubInfo.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Jellyfin.MeiamSub.Thunder.Model
+{
+ public class DownloadSubInfo
+ {
+ public string Url { get; set; }
+ public string Format { get; set; }
+ public string Language { get; set; }
+ public string TwoLetterISOLanguageName { get; set; }
+ public bool? IsForced { get; set; }
+ }
+}
diff --git a/Jellyfin.MeiamSub.Thunder/Model/SubtitleResponseRoot.cs b/Jellyfin.MeiamSub.Thunder/Model/SubtitleResponseRoot.cs
new file mode 100644
index 0000000..4c86b99
--- /dev/null
+++ b/Jellyfin.MeiamSub.Thunder/Model/SubtitleResponseRoot.cs
@@ -0,0 +1,44 @@
+using System.Collections.Generic;
+
+namespace Jellyfin.MeiamSub.Thunder.Model
+{
+ public class SublistItem
+ {
+ ///
+ ///
+ ///
+ public string scid { get; set; }
+ ///
+ ///
+ ///
+ public string sname { get; set; }
+ ///
+ /// 未知语言
+ ///
+ public string language { get; set; }
+ ///
+ ///
+ ///
+ public string rate { get; set; }
+ ///
+ ///
+ ///
+ public string surl { get; set; }
+ ///
+ ///
+ ///
+ public int svote { get; set; }
+ ///
+ ///
+ ///
+ public int roffset { get; set; }
+ }
+
+ public class SubtitleResponseRoot
+ {
+ ///
+ ///
+ ///
+ public List sublist { get; set; }
+ }
+}
diff --git a/Jellyfin.MeiamSub.Thunder/Plugin.cs b/Jellyfin.MeiamSub.Thunder/Plugin.cs
new file mode 100644
index 0000000..55abc31
--- /dev/null
+++ b/Jellyfin.MeiamSub.Thunder/Plugin.cs
@@ -0,0 +1,42 @@
+using Jellyfin.MeiamSub.Thunder.Configuration;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Plugins;
+using MediaBrowser.Model.Drawing;
+using MediaBrowser.Model.Serialization;
+using System;
+
+namespace Jellyfin.MeiamSub.Thunder
+{
+ ///
+ /// 插件入口
+ ///
+ public class Plugin : BasePlugin
+ {
+ ///
+ /// 插件ID
+ ///
+ public override Guid Id => new Guid("E4CE9DA9-EF00-417C-96F2-861C512D45EB");
+
+ ///
+ /// 插件名称
+ ///
+ public override string Name => "MeiamSub.Thunder";
+
+ ///
+ /// 插件描述
+ ///
+ public override string Description => "Download subtitles from Thunder XMP";
+
+
+ p
+
+
+ public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer)
+ : base(applicationPaths, xmlSerializer)
+ {
+ Instance = this;
+ }
+
+ public static Plugin Instance { get; private set; }
+ }
+}
diff --git a/Jellyfin.MeiamSub.Thunder/ThunderProvider.cs b/Jellyfin.MeiamSub.Thunder/ThunderProvider.cs
new file mode 100644
index 0000000..f6b1e05
--- /dev/null
+++ b/Jellyfin.MeiamSub.Thunder/ThunderProvider.cs
@@ -0,0 +1,262 @@
+using Jellyfin.MeiamSub.Thunder.Model;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Controller.Subtitles;
+using MediaBrowser.Model.Providers;
+using MediaBrowser.Model.Serialization;
+using Microsoft.Extensions.Logging;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Security.Cryptography;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Jellyfin.MeiamSub.Thunder
+{
+ ///
+ /// 迅雷字幕组件
+ ///
+ public class ThunderProvider : ISubtitleProvider
+ {
+ #region 变量声明
+ public const string ASS = "ass";
+ public const string SSA = "ssa";
+ public const string SRT = "srt";
+
+ private readonly ILogger _logger;
+ private readonly IJsonSerializer _jsonSerializer;
+
+ public int Order => 0;
+
+ public string Name => "MeiamSub.Thunder";
+
+ ///
+ /// 支持电影、剧集
+ ///
+ public IEnumerable SupportedMediaTypes => new List() { VideoContentType.Movie, VideoContentType.Episode };
+ #endregion
+
+ #region 构造函数
+ public ThunderProvider(ILogger logger, IJsonSerializer jsonSerializer)
+ {
+ _logger = logger;
+ _jsonSerializer = jsonSerializer;
+ }
+ #endregion
+
+ #region 查询字幕
+
+ ///
+ /// 查询请求
+ ///
+ ///
+ ///
+ ///
+ public async Task> Search(SubtitleSearchRequest request, CancellationToken cancellationToken)
+ {
+ _logger.LogDebug($"MeiamSub.Thunder Search | Request -> { _jsonSerializer.SerializeToString(request) }");
+
+ var subtitles = await SearchSubtitlesAsync(request);
+
+ return subtitles;
+ }
+
+ ///
+ /// 查询字幕
+ ///
+ ///
+ ///
+ private async Task> SearchSubtitlesAsync(SubtitleSearchRequest request)
+ {
+ if (request.Language != "chi")
+ {
+ return Array.Empty();
+ }
+
+ var cid = GetCidByFile(request.MediaPath);
+
+
+ using (var _httpClient = new HttpClient())
+ {
+ var response = await _httpClient.GetAsync($"http://sub.xmp.sandai.net:8000/subxl/{cid}.json");
+
+ _logger.LogDebug($"MeiamSub.Thunder Search | Response -> { _jsonSerializer.SerializeToString(response) }");
+
+ if (response.StatusCode == HttpStatusCode.OK)
+ {
+ var subtitleResponse = _jsonSerializer.DeserializeFromStream(await response.Content.ReadAsStreamAsync());
+
+ if (subtitleResponse != null)
+ {
+ _logger.LogDebug($"MeiamSub.Thunder Search | Response -> { _jsonSerializer.SerializeToString(subtitleResponse) }");
+
+ var subtitles = subtitleResponse.sublist.Where(m => !string.IsNullOrEmpty(m.sname));
+
+ if (subtitles.Count() > 0)
+ {
+ _logger.LogDebug($"MeiamSub.Thunder Search | Summary -> Get { subtitles.Count() } Subtitles");
+
+ return subtitles.Select(m => new RemoteSubtitleInfo()
+ {
+ Id = Base64Encode(_jsonSerializer.SerializeToString(new DownloadSubInfo
+ {
+ Url = m.surl,
+ Format = ExtractFormat(m.sname),
+ Language = request.Language,
+ TwoLetterISOLanguageName = request.TwoLetterISOLanguageName,
+ })),
+ Name = $"[MEIAMSUB] { Path.GetFileName(request.MediaPath) } | {request.TwoLetterISOLanguageName} | 迅雷",
+ Author = "Meiam ",
+ CommunityRating = Convert.ToSingle(m.rate),
+ ProviderName = "MeiamSub.Thunder",
+ Format = ExtractFormat(m.sname),
+ Comment = $"Format : { ExtractFormat(m.sname)} - Rate : { m.rate }"
+ }).OrderByDescending(m => m.CommunityRating);
+ }
+ }
+ }
+
+ }
+ _logger.LogDebug($"MeiamSub.Thunder Search | Summary -> Get 0 Subtitles");
+
+ return Array.Empty();
+ }
+ #endregion
+
+ #region 下载字幕
+ ///
+ /// 下载请求
+ ///
+ ///
+ ///
+ ///
+ public async Task GetSubtitles(string id, CancellationToken cancellationToken)
+ {
+ await Task.Run(() =>
+ {
+ _logger.LogDebug($"MeiamSub.Thunder DownloadSub | Request -> {id}");
+ });
+
+ return await DownloadSubAsync(id);
+ }
+
+ ///
+ /// 下载字幕
+ ///
+ ///
+ ///
+ private async Task DownloadSubAsync(string info)
+ {
+ var downloadSub = _jsonSerializer.DeserializeFromString(Base64Decode(info));
+
+ _logger.LogDebug($"MeiamSub.Thunder DownloadSub | Url -> { downloadSub.Url } | Format -> { downloadSub.Format } | Language -> { downloadSub.Language } ");
+
+ using (var _httpClient = new HttpClient())
+ {
+
+ var response = await _httpClient.GetAsync(downloadSub.Url);
+
+ _logger.LogDebug($"MeiamSub.Thunder DownloadSub | Response -> { response.StatusCode }");
+
+ if (response.StatusCode == HttpStatusCode.OK)
+ {
+
+ return new SubtitleResponse()
+ {
+ Language = downloadSub.Language,
+ IsForced = false,
+ Format = downloadSub.Format,
+ Stream = await response.Content.ReadAsStreamAsync(),
+ };
+ }
+ }
+ return new SubtitleResponse();
+
+ }
+ #endregion
+
+ #region 内部方法
+
+ ///
+ /// Base64 加密
+ ///
+ /// 明文
+ ///
+ public static string Base64Encode(string plainText)
+ {
+ var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
+ return Convert.ToBase64String(plainTextBytes);
+ }
+ ///
+ /// Base64 解密
+ ///
+ ///
+ ///
+ public static string Base64Decode(string base64EncodedData)
+ {
+ var base64EncodedBytes = Convert.FromBase64String(base64EncodedData);
+ return Encoding.UTF8.GetString(base64EncodedBytes);
+ }
+
+ ///
+ /// 提取格式化字幕类型
+ ///
+ ///
+ ///
+ protected string ExtractFormat(string text)
+ {
+
+ string result = null;
+
+ if (text != null)
+ {
+ text = text.ToLower();
+ if (text.Contains(ASS)) result = ASS;
+ else if (text.Contains(SSA)) result = SSA;
+ else if (text.Contains(SRT)) result = SRT;
+ else result = null;
+ }
+ return result;
+ }
+
+ ///
+ /// 获取文件 CID (迅雷)
+ ///
+ ///
+ ///
+ private string GetCidByFile(string filePath)
+ {
+ var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
+ var reader = new BinaryReader(stream);
+ var fileSize = new FileInfo(filePath).Length;
+ var SHA1 = new SHA1CryptoServiceProvider();
+ var buffer = new byte[0xf000];
+ if (fileSize < 0xf000)
+ {
+ reader.Read(buffer, 0, (int)fileSize);
+ buffer = SHA1.ComputeHash(buffer, 0, (int)fileSize);
+ }
+ else
+ {
+ reader.Read(buffer, 0, 0x5000);
+ stream.Seek(fileSize / 3, SeekOrigin.Begin);
+ reader.Read(buffer, 0x5000, 0x5000);
+ stream.Seek(fileSize - 0x5000, SeekOrigin.Begin);
+ reader.Read(buffer, 0xa000, 0x5000);
+
+ buffer = SHA1.ComputeHash(buffer, 0, 0xf000);
+ }
+ var result = "";
+ foreach (var i in buffer)
+ {
+ result += string.Format("{0:X2}", i);
+ }
+ return result;
+ }
+ #endregion
+ }
+}
diff --git a/Emby.MeiamSub.sln b/MeiamSubtitles.sln
similarity index 65%
rename from Emby.MeiamSub.sln
rename to MeiamSubtitles.sln
index 9c28627..bdea4dd 100644
--- a/Emby.MeiamSub.sln
+++ b/MeiamSubtitles.sln
@@ -7,7 +7,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.MeiamSub.Thunder", "Em
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.MeiamSub.DevTool", "Emby.MeiamSub.DevTool\Emby.MeiamSub.DevTool.csproj", "{6B0C23EA-EC24-4FB0-948E-094E84AEBF21}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.MeiamSub.Shooter", "Emby.MeiamSub.Shooter\Emby.MeiamSub.Shooter.csproj", "{0F502AEB-0FF4-44FA-8391-13AD61FC5490}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.MeiamSub.Shooter", "Emby.MeiamSub.Shooter\Emby.MeiamSub.Shooter.csproj", "{0F502AEB-0FF4-44FA-8391-13AD61FC5490}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.MeiamSub.Thunder", "Jellyfin.MeiamSub.Thunder\Jellyfin.MeiamSub.Thunder.csproj", "{4676AA1B-CC6C-42DC-BD69-6A293BAE8823}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.MeiamSub.Shooter", "Jellyfin.MeiamSub.Shooter\Jellyfin.MeiamSub.Shooter.csproj", "{8F77E155-9A91-4882-82E8-E8D69FECD246}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -27,6 +31,14 @@ Global
{0F502AEB-0FF4-44FA-8391-13AD61FC5490}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0F502AEB-0FF4-44FA-8391-13AD61FC5490}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0F502AEB-0FF4-44FA-8391-13AD61FC5490}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4676AA1B-CC6C-42DC-BD69-6A293BAE8823}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4676AA1B-CC6C-42DC-BD69-6A293BAE8823}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4676AA1B-CC6C-42DC-BD69-6A293BAE8823}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4676AA1B-CC6C-42DC-BD69-6A293BAE8823}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8F77E155-9A91-4882-82E8-E8D69FECD246}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8F77E155-9A91-4882-82E8-E8D69FECD246}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8F77E155-9A91-4882-82E8-E8D69FECD246}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8F77E155-9A91-4882-82E8-E8D69FECD246}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE