mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-06-30 11:22:53 +01:00
move provider project towards portability
This commit is contained in:
@@ -55,10 +55,6 @@
|
||||
<Reference Include="System.Net" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="taglib-sharp, Version=2.1.0.0, Culture=neutral, PublicKeyToken=db62eba44689b5b0, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\ThirdParty\taglib\taglib-sharp.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\SharedVersion.cs">
|
||||
@@ -131,16 +127,12 @@
|
||||
<Compile Include="People\PersonMetadataService.cs" />
|
||||
<Compile Include="People\MovieDbPersonProvider.cs" />
|
||||
<Compile Include="Photos\PhotoAlbumMetadataService.cs" />
|
||||
<Compile Include="Photos\PhotoHelper.cs" />
|
||||
<Compile Include="Photos\PhotoMetadataService.cs" />
|
||||
<Compile Include="Photos\PhotoProvider.cs" />
|
||||
<Compile Include="Playlists\PlaylistMetadataService.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Manager\ProviderUtils.cs" />
|
||||
<Compile Include="Studios\StudiosImageProvider.cs" />
|
||||
<Compile Include="Studios\StudioMetadataService.cs" />
|
||||
<Compile Include="Subtitles\ConfigurationExtension.cs" />
|
||||
<Compile Include="Subtitles\OpenSubtitleDownloader.cs" />
|
||||
<Compile Include="Subtitles\SubtitleManager.cs" />
|
||||
<Compile Include="TV\DummySeasonProvider.cs" />
|
||||
<Compile Include="TV\EpisodeMetadataService.cs" />
|
||||
@@ -182,10 +174,6 @@
|
||||
<Project>{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}</Project>
|
||||
<Name>MediaBrowser.Model</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\OpenSubtitlesHandler\OpenSubtitlesHandler.csproj">
|
||||
<Project>{4a4402d4-e910-443b-b8fc-2c18286a2ca0}</Project>
|
||||
<Name>OpenSubtitlesHandler</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace MediaBrowser.Providers.Photos
|
||||
{
|
||||
public static class PhotoHelper
|
||||
{
|
||||
public static string Dec2Frac(double dbl)
|
||||
{
|
||||
char neg = ' ';
|
||||
double dblDecimal = dbl;
|
||||
if (dblDecimal == (int)dblDecimal) return dblDecimal.ToString(); //return no if it's not a decimal
|
||||
if (dblDecimal < 0)
|
||||
{
|
||||
dblDecimal = Math.Abs(dblDecimal);
|
||||
neg = '-';
|
||||
}
|
||||
var whole = (int)Math.Truncate(dblDecimal);
|
||||
string decpart = dblDecimal.ToString().Replace(Math.Truncate(dblDecimal) + ".", "");
|
||||
double rN = Convert.ToDouble(decpart);
|
||||
double rD = Math.Pow(10, decpart.Length);
|
||||
|
||||
string rd = Recur(decpart);
|
||||
int rel = Convert.ToInt32(rd);
|
||||
if (rel != 0)
|
||||
{
|
||||
rN = rel;
|
||||
rD = (int)Math.Pow(10, rd.Length) - 1;
|
||||
}
|
||||
//just a few prime factors for testing purposes
|
||||
var primes = new[] { 47, 43, 37, 31, 29, 23, 19, 17, 13, 11, 7, 5, 3, 2 };
|
||||
foreach (int i in primes) ReduceNo(i, ref rD, ref rN);
|
||||
|
||||
rN = rN + (whole * rD);
|
||||
return string.Format("{0}{1}/{2}", neg, rN, rD);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds out the recurring decimal in a specified number
|
||||
/// </summary>
|
||||
/// <param name="db">Number to check</param>
|
||||
/// <returns></returns>
|
||||
private static string Recur(string db)
|
||||
{
|
||||
if (db.Length < 13) return "0";
|
||||
var sb = new StringBuilder();
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
sb.Append(db[i]);
|
||||
int dlength = (db.Length / sb.ToString().Length);
|
||||
int occur = Occurence(sb.ToString(), db);
|
||||
if (dlength == occur || dlength == occur - sb.ToString().Length)
|
||||
{
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
return "0";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks for number of occurence of specified no in a number
|
||||
/// </summary>
|
||||
/// <param name="s">The no to check occurence times</param>
|
||||
/// <param name="check">The number where to check this</param>
|
||||
/// <returns></returns>
|
||||
private static int Occurence(string s, string check)
|
||||
{
|
||||
int i = 0;
|
||||
int d = s.Length;
|
||||
string ds = check;
|
||||
for (int n = (ds.Length / d); n > 0; n--)
|
||||
{
|
||||
if (ds.Contains(s))
|
||||
{
|
||||
i++;
|
||||
ds = ds.Remove(ds.IndexOf(s, System.StringComparison.Ordinal), d);
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reduces a fraction given the numerator and denominator
|
||||
/// </summary>
|
||||
/// <param name="i">Number to use in an attempt to reduce fraction</param>
|
||||
/// <param name="rD">the Denominator</param>
|
||||
/// <param name="rN">the Numerator</param>
|
||||
private static void ReduceNo(int i, ref double rD, ref double rN)
|
||||
{
|
||||
//keep reducing until divisibility ends
|
||||
while ((rD % i) < 1e-10 && (rN % i) < 1e-10)
|
||||
{
|
||||
rN = rN / i;
|
||||
rD = rD / i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using TagLib;
|
||||
using TagLib.IFD;
|
||||
using TagLib.IFD.Entries;
|
||||
using TagLib.IFD.Tags;
|
||||
|
||||
namespace MediaBrowser.Providers.Photos
|
||||
{
|
||||
public class PhotoProvider : ICustomMetadataProvider<Photo>, IHasItemChangeMonitor, IForcedProvider
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public PhotoProvider(ILogger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Task<ItemUpdateType> FetchAsync(Photo item, MetadataRefreshOptions options, CancellationToken cancellationToken)
|
||||
{
|
||||
item.SetImagePath(ImageType.Primary, item.Path);
|
||||
|
||||
// Examples: https://github.com/mono/taglib-sharp/blob/a5f6949a53d09ce63ee7495580d6802921a21f14/tests/fixtures/TagLib.Tests.Images/NullOrientationTest.cs
|
||||
|
||||
try
|
||||
{
|
||||
using (var file = TagLib.File.Create(item.Path))
|
||||
{
|
||||
var image = file as TagLib.Image.File;
|
||||
|
||||
var tag = file.GetTag(TagTypes.TiffIFD) as IFDTag;
|
||||
|
||||
if (tag != null)
|
||||
{
|
||||
var structure = tag.Structure;
|
||||
|
||||
if (structure != null)
|
||||
{
|
||||
var exif = structure.GetEntry(0, (ushort)IFDEntryTag.ExifIFD) as SubIFDEntry;
|
||||
|
||||
if (exif != null)
|
||||
{
|
||||
var exifStructure = exif.Structure;
|
||||
|
||||
if (exifStructure != null)
|
||||
{
|
||||
var entry = exifStructure.GetEntry(0, (ushort)ExifEntryTag.ApertureValue) as RationalIFDEntry;
|
||||
|
||||
if (entry != null)
|
||||
{
|
||||
double val = entry.Value.Numerator;
|
||||
val /= entry.Value.Denominator;
|
||||
item.Aperture = val;
|
||||
}
|
||||
|
||||
entry = exifStructure.GetEntry(0, (ushort)ExifEntryTag.ShutterSpeedValue) as RationalIFDEntry;
|
||||
|
||||
if (entry != null)
|
||||
{
|
||||
double val = entry.Value.Numerator;
|
||||
val /= entry.Value.Denominator;
|
||||
item.ShutterSpeed = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
item.CameraMake = image.ImageTag.Make;
|
||||
item.CameraModel = image.ImageTag.Model;
|
||||
|
||||
item.Width = image.Properties.PhotoWidth;
|
||||
item.Height = image.Properties.PhotoHeight;
|
||||
|
||||
var rating = image.ImageTag.Rating;
|
||||
if (rating.HasValue)
|
||||
{
|
||||
item.CommunityRating = rating;
|
||||
}
|
||||
else
|
||||
{
|
||||
item.CommunityRating = null;
|
||||
}
|
||||
|
||||
item.Overview = image.ImageTag.Comment;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(image.ImageTag.Title))
|
||||
{
|
||||
item.Name = image.ImageTag.Title;
|
||||
}
|
||||
|
||||
var dateTaken = image.ImageTag.DateTime;
|
||||
if (dateTaken.HasValue)
|
||||
{
|
||||
item.DateCreated = dateTaken.Value;
|
||||
item.PremiereDate = dateTaken.Value;
|
||||
item.ProductionYear = dateTaken.Value.Year;
|
||||
}
|
||||
|
||||
item.Genres = image.ImageTag.Genres.ToList();
|
||||
item.Tags = image.ImageTag.Keywords.ToList();
|
||||
item.Software = image.ImageTag.Software;
|
||||
|
||||
if (image.ImageTag.Orientation == TagLib.Image.ImageOrientation.None)
|
||||
{
|
||||
item.Orientation = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
Model.Drawing.ImageOrientation orientation;
|
||||
if (Enum.TryParse(image.ImageTag.Orientation.ToString(), true, out orientation))
|
||||
{
|
||||
item.Orientation = orientation;
|
||||
}
|
||||
}
|
||||
|
||||
item.ExposureTime = image.ImageTag.ExposureTime;
|
||||
item.FocalLength = image.ImageTag.FocalLength;
|
||||
|
||||
item.Latitude = image.ImageTag.Latitude;
|
||||
item.Longitude = image.ImageTag.Longitude;
|
||||
item.Altitude = image.ImageTag.Altitude;
|
||||
|
||||
if (image.ImageTag.ISOSpeedRatings.HasValue)
|
||||
{
|
||||
item.IsoSpeedRating = Convert.ToInt32(image.ImageTag.ISOSpeedRatings.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
item.IsoSpeedRating = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.ErrorException("Image Provider - Error reading image tag for {0}", e, item.Path);
|
||||
}
|
||||
|
||||
const ItemUpdateType result = ItemUpdateType.ImageUpdate | ItemUpdateType.MetadataImport;
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return "Embedded Information"; }
|
||||
}
|
||||
|
||||
public bool HasChanged(IHasMetadata item, IDirectoryService directoryService)
|
||||
{
|
||||
if (item.EnableRefreshOnDateModifiedChange && !string.IsNullOrWhiteSpace(item.Path) && item.LocationType == LocationType.FileSystem)
|
||||
{
|
||||
var file = directoryService.GetFile(item.Path);
|
||||
if (file != null && file.LastWriteTimeUtc != item.DateModified)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Model.Providers;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MediaBrowser.Providers.Subtitles
|
||||
{
|
||||
public static class ConfigurationExtension
|
||||
{
|
||||
public static SubtitleOptions GetSubtitleConfiguration(this IConfigurationManager manager)
|
||||
{
|
||||
return manager.GetConfiguration<SubtitleOptions>("subtitles");
|
||||
}
|
||||
}
|
||||
|
||||
public class SubtitleConfigurationFactory : IConfigurationFactory
|
||||
{
|
||||
public IEnumerable<ConfigurationStore> GetConfigurations()
|
||||
{
|
||||
return new List<ConfigurationStore>
|
||||
{
|
||||
new ConfigurationStore
|
||||
{
|
||||
Key = "subtitles",
|
||||
ConfigurationType = typeof (SubtitleOptions)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,340 +0,0 @@
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Controller.Security;
|
||||
using MediaBrowser.Controller.Subtitles;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Providers;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using OpenSubtitlesHandler;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Providers.Subtitles
|
||||
{
|
||||
public class OpenSubtitleDownloader : ISubtitleProvider, IDisposable
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
|
||||
|
||||
private readonly IServerConfigurationManager _config;
|
||||
private readonly IEncryptionManager _encryption;
|
||||
|
||||
private readonly IJsonSerializer _json;
|
||||
|
||||
public OpenSubtitleDownloader(ILogManager logManager, IHttpClient httpClient, IServerConfigurationManager config, IEncryptionManager encryption, IJsonSerializer json)
|
||||
{
|
||||
_logger = logManager.GetLogger(GetType().Name);
|
||||
_httpClient = httpClient;
|
||||
_config = config;
|
||||
_encryption = encryption;
|
||||
_json = json;
|
||||
|
||||
_config.NamedConfigurationUpdating += _config_NamedConfigurationUpdating;
|
||||
|
||||
Utilities.HttpClient = httpClient;
|
||||
OpenSubtitles.SetUserAgent("mediabrowser.tv");
|
||||
}
|
||||
|
||||
private const string PasswordHashPrefix = "h:";
|
||||
void _config_NamedConfigurationUpdating(object sender, ConfigurationUpdateEventArgs e)
|
||||
{
|
||||
if (!string.Equals(e.Key, "subtitles", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var options = (SubtitleOptions)e.NewConfiguration;
|
||||
|
||||
if (options != null &&
|
||||
!string.IsNullOrWhiteSpace(options.OpenSubtitlesPasswordHash) &&
|
||||
!options.OpenSubtitlesPasswordHash.StartsWith(PasswordHashPrefix, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
options.OpenSubtitlesPasswordHash = EncryptPassword(options.OpenSubtitlesPasswordHash);
|
||||
}
|
||||
}
|
||||
|
||||
private string EncryptPassword(string password)
|
||||
{
|
||||
return PasswordHashPrefix + _encryption.EncryptString(password);
|
||||
}
|
||||
|
||||
private string DecryptPassword(string password)
|
||||
{
|
||||
if (password == null ||
|
||||
!password.StartsWith(PasswordHashPrefix, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return _encryption.DecryptString(password.Substring(2));
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return "Open Subtitles"; }
|
||||
}
|
||||
|
||||
private SubtitleOptions GetOptions()
|
||||
{
|
||||
return _config.GetSubtitleConfiguration();
|
||||
}
|
||||
|
||||
public IEnumerable<VideoContentType> SupportedMediaTypes
|
||||
{
|
||||
get
|
||||
{
|
||||
var options = GetOptions();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(options.OpenSubtitlesUsername) ||
|
||||
string.IsNullOrWhiteSpace(options.OpenSubtitlesPasswordHash))
|
||||
{
|
||||
return new VideoContentType[] { };
|
||||
}
|
||||
|
||||
return new[] { VideoContentType.Episode, VideoContentType.Movie };
|
||||
}
|
||||
}
|
||||
|
||||
public Task<SubtitleResponse> GetSubtitles(string id, CancellationToken cancellationToken)
|
||||
{
|
||||
return GetSubtitlesInternal(id, GetOptions(), cancellationToken);
|
||||
}
|
||||
|
||||
private DateTime _lastRateLimitException;
|
||||
private async Task<SubtitleResponse> GetSubtitlesInternal(string id,
|
||||
SubtitleOptions options,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(id))
|
||||
{
|
||||
throw new ArgumentNullException("id");
|
||||
}
|
||||
|
||||
var idParts = id.Split(new[] { '-' }, 3);
|
||||
|
||||
var format = idParts[0];
|
||||
var language = idParts[1];
|
||||
var ossId = idParts[2];
|
||||
|
||||
var downloadsList = new[] { int.Parse(ossId, _usCulture) };
|
||||
|
||||
await Login(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if ((DateTime.UtcNow - _lastRateLimitException).TotalHours < 1)
|
||||
{
|
||||
throw new ApplicationException("OpenSubtitles rate limit reached");
|
||||
}
|
||||
|
||||
var resultDownLoad = await OpenSubtitles.DownloadSubtitlesAsync(downloadsList, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if ((resultDownLoad.Status ?? string.Empty).IndexOf("407", StringComparison.OrdinalIgnoreCase) != -1)
|
||||
{
|
||||
_lastRateLimitException = DateTime.UtcNow;
|
||||
throw new ApplicationException("OpenSubtitles rate limit reached");
|
||||
}
|
||||
|
||||
if (!(resultDownLoad is MethodResponseSubtitleDownload))
|
||||
{
|
||||
throw new ApplicationException("Invalid response type");
|
||||
}
|
||||
|
||||
var results = ((MethodResponseSubtitleDownload)resultDownLoad).Results;
|
||||
|
||||
_lastRateLimitException = DateTime.MinValue;
|
||||
|
||||
if (results.Count == 0)
|
||||
{
|
||||
var msg = string.Format("Subtitle with Id {0} was not found. Name: {1}. Status: {2}. Message: {3}",
|
||||
ossId,
|
||||
resultDownLoad.Name ?? string.Empty,
|
||||
resultDownLoad.Status ?? string.Empty,
|
||||
resultDownLoad.Message ?? string.Empty);
|
||||
|
||||
throw new ResourceNotFoundException(msg);
|
||||
}
|
||||
|
||||
var data = Convert.FromBase64String(results.First().Data);
|
||||
|
||||
return new SubtitleResponse
|
||||
{
|
||||
Format = format,
|
||||
Language = language,
|
||||
|
||||
Stream = new MemoryStream(Utilities.Decompress(new MemoryStream(data)))
|
||||
};
|
||||
}
|
||||
|
||||
private DateTime _lastLogin;
|
||||
private async Task Login(CancellationToken cancellationToken)
|
||||
{
|
||||
if ((DateTime.UtcNow - _lastLogin).TotalSeconds < 60)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var options = GetOptions();
|
||||
|
||||
var user = options.OpenSubtitlesUsername ?? string.Empty;
|
||||
var password = DecryptPassword(options.OpenSubtitlesPasswordHash);
|
||||
|
||||
var loginResponse = await OpenSubtitles.LogInAsync(user, password, "en", cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (!(loginResponse is MethodResponseLogIn))
|
||||
{
|
||||
throw new Exception("Authentication to OpenSubtitles failed.");
|
||||
}
|
||||
|
||||
_lastLogin = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<NameIdPair>> GetSupportedLanguages(CancellationToken cancellationToken)
|
||||
{
|
||||
await Login(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var result = OpenSubtitles.GetSubLanguages("en");
|
||||
if (!(result is MethodResponseGetSubLanguages))
|
||||
{
|
||||
_logger.Error("Invalid response type");
|
||||
return new List<NameIdPair>();
|
||||
}
|
||||
|
||||
var results = ((MethodResponseGetSubLanguages)result).Languages;
|
||||
|
||||
return results.Select(i => new NameIdPair
|
||||
{
|
||||
Name = i.LanguageName,
|
||||
Id = i.SubLanguageID
|
||||
});
|
||||
}
|
||||
|
||||
private string NormalizeLanguage(string language)
|
||||
{
|
||||
// Problem with Greek subtitle download #1349
|
||||
if (string.Equals (language, "gre", StringComparison.OrdinalIgnoreCase)) {
|
||||
|
||||
return "ell";
|
||||
}
|
||||
|
||||
return language;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<RemoteSubtitleInfo>> Search(SubtitleSearchRequest request, CancellationToken cancellationToken)
|
||||
{
|
||||
var imdbIdText = request.GetProviderId(MetadataProviders.Imdb);
|
||||
long imdbId = 0;
|
||||
|
||||
switch (request.ContentType)
|
||||
{
|
||||
case VideoContentType.Episode:
|
||||
if (!request.IndexNumber.HasValue || !request.ParentIndexNumber.HasValue || string.IsNullOrEmpty(request.SeriesName))
|
||||
{
|
||||
_logger.Debug("Episode information missing");
|
||||
return new List<RemoteSubtitleInfo>();
|
||||
}
|
||||
break;
|
||||
case VideoContentType.Movie:
|
||||
if (string.IsNullOrEmpty(request.Name))
|
||||
{
|
||||
_logger.Debug("Movie name missing");
|
||||
return new List<RemoteSubtitleInfo>();
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(imdbIdText) || !long.TryParse(imdbIdText.TrimStart('t'), NumberStyles.Any, _usCulture, out imdbId))
|
||||
{
|
||||
_logger.Debug("Imdb id missing");
|
||||
return new List<RemoteSubtitleInfo>();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(request.MediaPath))
|
||||
{
|
||||
_logger.Debug("Path Missing");
|
||||
return new List<RemoteSubtitleInfo>();
|
||||
}
|
||||
|
||||
await Login(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var subLanguageId = NormalizeLanguage(request.Language);
|
||||
var hash = Utilities.ComputeHash(request.MediaPath);
|
||||
var fileInfo = new FileInfo(request.MediaPath);
|
||||
var movieByteSize = fileInfo.Length;
|
||||
var searchImdbId = request.ContentType == VideoContentType.Movie ? imdbId.ToString(_usCulture) : "";
|
||||
var subtitleSearchParameters = request.ContentType == VideoContentType.Episode
|
||||
? new List<SubtitleSearchParameters> {
|
||||
new SubtitleSearchParameters(subLanguageId,
|
||||
query: request.SeriesName,
|
||||
season: request.ParentIndexNumber.Value.ToString(_usCulture),
|
||||
episode: request.IndexNumber.Value.ToString(_usCulture))
|
||||
}
|
||||
: new List<SubtitleSearchParameters> {
|
||||
new SubtitleSearchParameters(subLanguageId, imdbid: searchImdbId),
|
||||
new SubtitleSearchParameters(subLanguageId, query: request.Name, imdbid: searchImdbId)
|
||||
};
|
||||
var parms = new List<SubtitleSearchParameters> {
|
||||
new SubtitleSearchParameters( subLanguageId,
|
||||
movieHash: hash,
|
||||
movieByteSize: movieByteSize,
|
||||
imdbid: searchImdbId ),
|
||||
};
|
||||
parms.AddRange(subtitleSearchParameters);
|
||||
var result = await OpenSubtitles.SearchSubtitlesAsync(parms.ToArray(), cancellationToken).ConfigureAwait(false);
|
||||
if (!(result is MethodResponseSubtitleSearch))
|
||||
{
|
||||
_logger.Error("Invalid response type");
|
||||
return new List<RemoteSubtitleInfo>();
|
||||
}
|
||||
|
||||
Predicate<SubtitleSearchResult> mediaFilter =
|
||||
x =>
|
||||
request.ContentType == VideoContentType.Episode
|
||||
? !string.IsNullOrEmpty(x.SeriesSeason) && !string.IsNullOrEmpty(x.SeriesEpisode) &&
|
||||
int.Parse(x.SeriesSeason, _usCulture) == request.ParentIndexNumber &&
|
||||
int.Parse(x.SeriesEpisode, _usCulture) == request.IndexNumber
|
||||
: !string.IsNullOrEmpty(x.IDMovieImdb) && long.Parse(x.IDMovieImdb, _usCulture) == imdbId;
|
||||
|
||||
var results = ((MethodResponseSubtitleSearch)result).Results;
|
||||
|
||||
// Avoid implicitly captured closure
|
||||
var hasCopy = hash;
|
||||
|
||||
return results.Where(x => x.SubBad == "0" && mediaFilter(x) && (!request.IsPerfectMatch || string.Equals(x.MovieHash, hash, StringComparison.OrdinalIgnoreCase)))
|
||||
.OrderBy(x => (string.Equals(x.MovieHash, hash, StringComparison.OrdinalIgnoreCase) ? 0 : 1))
|
||||
.ThenBy(x => Math.Abs(long.Parse(x.MovieByteSize, _usCulture) - movieByteSize))
|
||||
.ThenByDescending(x => int.Parse(x.SubDownloadsCnt, _usCulture))
|
||||
.ThenByDescending(x => double.Parse(x.SubRating, _usCulture))
|
||||
.Select(i => new RemoteSubtitleInfo
|
||||
{
|
||||
Author = i.UserNickName,
|
||||
Comment = i.SubAuthorComment,
|
||||
CommunityRating = float.Parse(i.SubRating, _usCulture),
|
||||
DownloadCount = int.Parse(i.SubDownloadsCnt, _usCulture),
|
||||
Format = i.SubFormat,
|
||||
ProviderName = Name,
|
||||
ThreeLetterISOLanguageName = i.SubLanguageID,
|
||||
|
||||
Id = i.SubFormat + "-" + i.SubLanguageID + "-" + i.IDSubtitleFile,
|
||||
|
||||
Name = i.SubFileName,
|
||||
DateCreated = DateTime.Parse(i.SubAddDate, _usCulture),
|
||||
IsHashMatch = i.MovieHash == hasCopy
|
||||
|
||||
}).Where(i => !string.Equals(i.Format, "sub", StringComparison.OrdinalIgnoreCase) && !string.Equals(i.Format, "idx", StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_config.NamedConfigurationUpdating -= _config_NamedConfigurationUpdating;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user