From 03ce0a0dda8852c576fa62315c3ee2564bb884dc Mon Sep 17 00:00:00 2001 From: Meiam <91270@qq.com> Date: Thu, 31 Mar 2022 21:08:24 +0800 Subject: [PATCH] =?UTF-8?q?=E9=80=82=E9=85=8D=20=20Jellyfin=20=E6=9C=80?= =?UTF-8?q?=E6=96=B0=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Configuration/PluginConfiguration.cs | 12 + .../Jellyfin.MeiamSub.Shooter.csproj | 11 + .../Model/DownloadSubInfo.cs | 15 + .../Model/SubtitleResponseRoot.cs | 19 ++ Jellyfin.MeiamSub.Shooter/Plugin.cs | 41 +++ Jellyfin.MeiamSub.Shooter/ShooterProvider.cs | 309 ++++++++++++++++++ .../Configuration/PluginConfiguration.cs | 12 + .../Jellyfin.MeiamSub.Thunder.csproj | 14 + .../Model/DownloadSubInfo.cs | 15 + .../Model/SubtitleResponseRoot.cs | 44 +++ Jellyfin.MeiamSub.Thunder/Plugin.cs | 42 +++ Jellyfin.MeiamSub.Thunder/ThunderProvider.cs | 262 +++++++++++++++ Emby.MeiamSub.sln => MeiamSubtitles.sln | 14 +- 13 files changed, 809 insertions(+), 1 deletion(-) create mode 100644 Jellyfin.MeiamSub.Shooter/Configuration/PluginConfiguration.cs create mode 100644 Jellyfin.MeiamSub.Shooter/Jellyfin.MeiamSub.Shooter.csproj create mode 100644 Jellyfin.MeiamSub.Shooter/Model/DownloadSubInfo.cs create mode 100644 Jellyfin.MeiamSub.Shooter/Model/SubtitleResponseRoot.cs create mode 100644 Jellyfin.MeiamSub.Shooter/Plugin.cs create mode 100644 Jellyfin.MeiamSub.Shooter/ShooterProvider.cs create mode 100644 Jellyfin.MeiamSub.Thunder/Configuration/PluginConfiguration.cs create mode 100644 Jellyfin.MeiamSub.Thunder/Jellyfin.MeiamSub.Thunder.csproj create mode 100644 Jellyfin.MeiamSub.Thunder/Model/DownloadSubInfo.cs create mode 100644 Jellyfin.MeiamSub.Thunder/Model/SubtitleResponseRoot.cs create mode 100644 Jellyfin.MeiamSub.Thunder/Plugin.cs create mode 100644 Jellyfin.MeiamSub.Thunder/ThunderProvider.cs rename Emby.MeiamSub.sln => MeiamSubtitles.sln (65%) 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