aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Providers
diff options
context:
space:
mode:
authorLuke Pulverenti <luke.pulverenti@gmail.com>2014-05-06 22:28:19 -0400
committerLuke Pulverenti <luke.pulverenti@gmail.com>2014-05-06 22:28:19 -0400
commit0d025f7fb620bf2a24ca9aa4e5994f132e02e7c0 (patch)
tree76137f5f868ed81725d29bbdc6ac0e3b57c304d8 /MediaBrowser.Providers
parente1dd361c7bf05af49d9210ab679e85fa00870990 (diff)
beginning remote subtitle downloading
Diffstat (limited to 'MediaBrowser.Providers')
-rw-r--r--MediaBrowser.Providers/MediaBrowser.Providers.csproj2
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs14
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs57
-rw-r--r--MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs140
-rw-r--r--MediaBrowser.Providers/Subtitles/OpenSubtitleDownloader.cs138
-rw-r--r--MediaBrowser.Providers/Subtitles/SubtitleManager.cs141
6 files changed, 421 insertions, 71 deletions
diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
index b19966718..43402123c 100644
--- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj
+++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
@@ -108,6 +108,7 @@
<Compile Include="MediaInfo\FFProbeHelpers.cs" />
<Compile Include="MediaInfo\FFProbeProvider.cs" />
<Compile Include="MediaInfo\FFProbeVideoInfo.cs" />
+ <Compile Include="MediaInfo\SubtitleDownloader.cs" />
<Compile Include="Movies\MovieDbTrailerProvider.cs" />
<Compile Include="Movies\MovieExternalIds.cs" />
<Compile Include="Movies\TrailerMetadataService.cs" />
@@ -187,6 +188,7 @@
<Compile Include="Studios\StudiosImageProvider.cs" />
<Compile Include="Studios\StudioMetadataService.cs" />
<Compile Include="Subtitles\OpenSubtitleDownloader.cs" />
+ <Compile Include="Subtitles\SubtitleManager.cs" />
<Compile Include="TV\EpisodeLocalImageProvider.cs" />
<Compile Include="TV\EpisodeMetadataService.cs" />
<Compile Include="TV\EpisodeXmlProvider.cs" />
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
index 7a71a7551..3c03e11b3 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
@@ -1,5 +1,6 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
@@ -10,15 +11,16 @@ using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
+using MediaBrowser.Controller.Subtitles;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Serialization;
using System;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using System.Linq;
namespace MediaBrowser.Providers.MediaInfo
{
@@ -45,6 +47,8 @@ namespace MediaBrowser.Providers.MediaInfo
private readonly IJsonSerializer _json;
private readonly IEncodingManager _encodingManager;
private readonly IFileSystem _fileSystem;
+ private readonly IServerConfigurationManager _config;
+ private readonly ISubtitleManager _subtitleManager;
public string Name
{
@@ -96,7 +100,7 @@ namespace MediaBrowser.Providers.MediaInfo
return FetchAudioInfo(item, cancellationToken);
}
- public FFProbeProvider(ILogger logger, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IBlurayExaminer blurayExaminer, ILocalizationManager localization, IApplicationPaths appPaths, IJsonSerializer json, IEncodingManager encodingManager, IFileSystem fileSystem)
+ public FFProbeProvider(ILogger logger, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IBlurayExaminer blurayExaminer, ILocalizationManager localization, IApplicationPaths appPaths, IJsonSerializer json, IEncodingManager encodingManager, IFileSystem fileSystem, IServerConfigurationManager config, ISubtitleManager subtitleManager)
{
_logger = logger;
_isoManager = isoManager;
@@ -108,6 +112,8 @@ namespace MediaBrowser.Providers.MediaInfo
_json = json;
_encodingManager = encodingManager;
_fileSystem = fileSystem;
+ _config = config;
+ _subtitleManager = subtitleManager;
}
private readonly Task<ItemUpdateType> _cachedTask = Task.FromResult(ItemUpdateType.None);
@@ -134,7 +140,7 @@ namespace MediaBrowser.Providers.MediaInfo
return _cachedTask;
}
- var prober = new FFProbeVideoInfo(_logger, _isoManager, _mediaEncoder, _itemRepo, _blurayExaminer, _localization, _appPaths, _json, _encodingManager, _fileSystem);
+ var prober = new FFProbeVideoInfo(_logger, _isoManager, _mediaEncoder, _itemRepo, _blurayExaminer, _localization, _appPaths, _json, _encodingManager, _fileSystem, _config, _subtitleManager);
return prober.ProbeVideo(item, directoryService, cancellationToken);
}
@@ -165,7 +171,7 @@ namespace MediaBrowser.Providers.MediaInfo
if (video != null && !video.IsPlaceHolder)
{
- var prober = new FFProbeVideoInfo(_logger, _isoManager, _mediaEncoder, _itemRepo, _blurayExaminer, _localization, _appPaths, _json, _encodingManager, _fileSystem);
+ var prober = new FFProbeVideoInfo(_logger, _isoManager, _mediaEncoder, _itemRepo, _blurayExaminer, _localization, _appPaths, _json, _encodingManager, _fileSystem, _config, _subtitleManager);
return !video.SubtitleFiles.SequenceEqual(prober.GetSubtitleFiles(video, directoryService).Select(i => i.FullName).OrderBy(i => i), StringComparer.OrdinalIgnoreCase);
}
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
index a7d4a480e..a2897ef9c 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
@@ -2,12 +2,16 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
+using MediaBrowser.Controller.Subtitles;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
@@ -35,10 +39,12 @@ namespace MediaBrowser.Providers.MediaInfo
private readonly IJsonSerializer _json;
private readonly IEncodingManager _encodingManager;
private readonly IFileSystem _fileSystem;
+ private readonly IServerConfigurationManager _config;
+ private readonly ISubtitleManager _subtitleManager;
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
- public FFProbeVideoInfo(ILogger logger, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IBlurayExaminer blurayExaminer, ILocalizationManager localization, IApplicationPaths appPaths, IJsonSerializer json, IEncodingManager encodingManager, IFileSystem fileSystem)
+ public FFProbeVideoInfo(ILogger logger, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IBlurayExaminer blurayExaminer, ILocalizationManager localization, IApplicationPaths appPaths, IJsonSerializer json, IEncodingManager encodingManager, IFileSystem fileSystem, IServerConfigurationManager config, ISubtitleManager subtitleManager)
{
_logger = logger;
_isoManager = isoManager;
@@ -50,6 +56,8 @@ namespace MediaBrowser.Providers.MediaInfo
_json = json;
_encodingManager = encodingManager;
_fileSystem = fileSystem;
+ _config = config;
+ _subtitleManager = subtitleManager;
}
public async Task<ItemUpdateType> ProbeVideo<T>(T item, IDirectoryService directoryService, CancellationToken cancellationToken)
@@ -118,7 +126,7 @@ namespace MediaBrowser.Providers.MediaInfo
cancellationToken.ThrowIfCancellationRequested();
var idString = item.Id.ToString("N");
- var cachePath = Path.Combine(_appPaths.CachePath,
+ var cachePath = Path.Combine(_appPaths.CachePath,
"ffprobe-video",
idString.Substring(0, 2), idString, "v" + SchemaVersion + _mediaEncoder.Version + item.DateModified.Ticks.ToString(_usCulture) + ".json");
@@ -200,7 +208,7 @@ namespace MediaBrowser.Providers.MediaInfo
FetchBdInfo(video, chapters, mediaStreams, blurayInfo);
}
- AddExternalSubtitles(video, mediaStreams, directoryService);
+ await AddExternalSubtitles(video, mediaStreams, directoryService, cancellationToken).ConfigureAwait(false);
FetchWtvInfo(video, data);
@@ -247,7 +255,7 @@ namespace MediaBrowser.Providers.MediaInfo
}
}
- info.StartPositionTicks = chapter.start/100;
+ info.StartPositionTicks = chapter.start / 100;
return info;
}
@@ -450,11 +458,42 @@ namespace MediaBrowser.Providers.MediaInfo
/// </summary>
/// <param name="video">The video.</param>
/// <param name="currentStreams">The current streams.</param>
- private void AddExternalSubtitles(Video video, List<MediaStream> currentStreams, IDirectoryService directoryService)
+ private async Task AddExternalSubtitles(Video video, List<MediaStream> currentStreams, IDirectoryService directoryService, CancellationToken cancellationToken)
+ {
+ var externalSubtitleStreams = GetExternalSubtitleStreams(video, currentStreams.Count, directoryService).ToList();
+
+ if ((_config.Configuration.SubtitleOptions.DownloadEpisodeSubtitles &&
+ video is Episode) ||
+ (_config.Configuration.SubtitleOptions.DownloadMovieSubtitles &&
+ video is Movie))
+ {
+ var downloadedLanguages = await new SubtitleDownloader(_logger,
+ _subtitleManager)
+ .DownloadSubtitles(video,
+ currentStreams,
+ externalSubtitleStreams,
+ _config.Configuration.SubtitleOptions.RequireExternalSubtitles,
+ _config.Configuration.SubtitleOptions.SubtitleDownloadLanguages,
+ cancellationToken).ConfigureAwait(false);
+
+ // Rescan
+ if (downloadedLanguages.Count > 0)
+ {
+ externalSubtitleStreams = GetExternalSubtitleStreams(video, currentStreams.Count, directoryService).ToList();
+ }
+ }
+
+ video.SubtitleFiles = externalSubtitleStreams.Select(i => i.Path).OrderBy(i => i).ToList();
+
+ currentStreams.AddRange(externalSubtitleStreams);
+ }
+
+ private IEnumerable<MediaStream> GetExternalSubtitleStreams(Video video,
+ int startIndex,
+ IDirectoryService directoryService)
{
var files = GetSubtitleFiles(video, directoryService);
- var startIndex = currentStreams.Count;
var streams = new List<MediaStream>();
var videoFileNameWithoutExtension = Path.GetFileNameWithoutExtension(video.Path);
@@ -504,9 +543,7 @@ namespace MediaBrowser.Providers.MediaInfo
}
}
- video.SubtitleFiles = streams.Select(i => i.Path).OrderBy(i => i).ToList();
-
- currentStreams.AddRange(streams);
+ return streams;
}
/// <summary>
@@ -627,7 +664,7 @@ namespace MediaBrowser.Providers.MediaInfo
{
var path = mount == null ? item.Path : mount.MountedPath;
var dvd = new Dvd(path);
-
+
var primaryTitle = dvd.Titles.OrderByDescending(GetRuntime).FirstOrDefault();
byte? titleNumber = null;
diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs b/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs
new file mode 100644
index 000000000..7f7ccda19
--- /dev/null
+++ b/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs
@@ -0,0 +1,140 @@
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Subtitles;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Logging;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Providers.MediaInfo
+{
+ public class SubtitleDownloader
+ {
+ private readonly ILogger _logger;
+ private readonly ISubtitleManager _subtitleManager;
+
+ public SubtitleDownloader(ILogger logger, ISubtitleManager subtitleManager)
+ {
+ _logger = logger;
+ _subtitleManager = subtitleManager;
+ }
+
+ public async Task<List<string>> DownloadSubtitles(Video video,
+ List<MediaStream> internalSubtitleStreams,
+ List<MediaStream> externalSubtitleStreams,
+ bool forceExternal,
+ IEnumerable<string> languages,
+ CancellationToken cancellationToken)
+ {
+ if (video.LocationType != LocationType.FileSystem ||
+ video.VideoType != VideoType.VideoFile)
+ {
+ return new List<string>();
+ }
+
+ SubtitleMediaType mediaType;
+
+ if (video is Episode)
+ {
+ mediaType = SubtitleMediaType.Episode;
+ }
+ else if (video is Movie)
+ {
+ mediaType = SubtitleMediaType.Movie;
+ }
+ else
+ {
+ // These are the only supported types
+ return new List<string>();
+ }
+
+ var downloadedLanguages = new List<string>();
+
+ foreach (var lang in languages)
+ {
+ try
+ {
+ var downloaded = await DownloadSubtitles(video, internalSubtitleStreams, externalSubtitleStreams, forceExternal, lang, mediaType, cancellationToken)
+ .ConfigureAwait(false);
+
+ if (downloaded)
+ {
+ downloadedLanguages.Add(lang);
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error downloading subtitles", ex);
+ }
+ }
+
+ return downloadedLanguages;
+ }
+
+ private async Task<bool> DownloadSubtitles(Video video,
+ IEnumerable<MediaStream> internalSubtitleStreams,
+ IEnumerable<MediaStream> externalSubtitleStreams,
+ bool forceExternal,
+ string language,
+ SubtitleMediaType mediaType,
+ CancellationToken cancellationToken)
+ {
+ // There's already subtitles for this language
+ if (externalSubtitleStreams.Any(i => string.Equals(i.Language, language, StringComparison.OrdinalIgnoreCase)))
+ {
+ return false;
+ }
+
+ // There's an internal subtitle stream for this language
+ if (!forceExternal && internalSubtitleStreams.Any(i => string.Equals(i.Language, language, StringComparison.OrdinalIgnoreCase)))
+ {
+ return false;
+ }
+
+ var request = new SubtitleSearchRequest
+ {
+ ContentType = mediaType,
+ IndexNumber = video.IndexNumber,
+ Language = language,
+ MediaPath = video.Path,
+ Name = video.Name,
+ ParentIndexNumber = video.ParentIndexNumber,
+ ProductionYear = video.ProductionYear,
+ ProviderIds = video.ProviderIds
+ };
+
+ var episode = video as Episode;
+
+ if (episode != null)
+ {
+ request.IndexNumberEnd = episode.IndexNumberEnd;
+ request.SeriesName = episode.SeriesName;
+ }
+
+ try
+ {
+ var searchResults = await _subtitleManager.SearchSubtitles(request, cancellationToken).ConfigureAwait(false);
+
+ var result = searchResults.FirstOrDefault();
+
+ if (result != null)
+ {
+ await _subtitleManager.DownloadSubtitles(video, result.Id, result.ProviderName, cancellationToken)
+ .ConfigureAwait(false);
+
+ return true;
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error downloading subtitles", ex);
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/MediaBrowser.Providers/Subtitles/OpenSubtitleDownloader.cs b/MediaBrowser.Providers/Subtitles/OpenSubtitleDownloader.cs
index 7309513d6..929cccd5a 100644
--- a/MediaBrowser.Providers/Subtitles/OpenSubtitleDownloader.cs
+++ b/MediaBrowser.Providers/Subtitles/OpenSubtitleDownloader.cs
@@ -1,8 +1,10 @@
-using MediaBrowser.Common.Net;
+using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Providers;
+using MediaBrowser.Controller.Subtitles;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.MediaInfo;
+using MediaBrowser.Model.Providers;
using OpenSubtitlesHandler;
using System;
using System.Collections.Generic;
@@ -20,9 +22,9 @@ namespace MediaBrowser.Providers.Subtitles
private readonly IHttpClient _httpClient;
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
- public OpenSubtitleDownloader(ILogger logger, IHttpClient httpClient)
+ public OpenSubtitleDownloader(ILogManager logManager, IHttpClient httpClient)
{
- _logger = logger;
+ _logger = logManager.GetLogger(GetType().Name);
_httpClient = httpClient;
}
@@ -36,39 +38,71 @@ namespace MediaBrowser.Providers.Subtitles
get { return new[] { SubtitleMediaType.Episode, SubtitleMediaType.Movie }; }
}
- public Task<SubtitleResponse> GetSubtitles(SubtitleRequest request, CancellationToken cancellationToken)
+ public Task<SubtitleResponse> GetSubtitles(string id, CancellationToken cancellationToken)
{
- return GetSubtitlesInternal(request, cancellationToken);
+ return GetSubtitlesInternal(id, cancellationToken);
}
- private async Task<SubtitleResponse> GetSubtitlesInternal(SubtitleRequest request,
+ private async Task<SubtitleResponse> GetSubtitlesInternal(string id,
CancellationToken cancellationToken)
{
- var response = new SubtitleResponse();
+ 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) };
+
+ var resultDownLoad = OpenSubtitles.DownloadSubtitles(downloadsList);
+ if (!(resultDownLoad is MethodResponseSubtitleDownload))
+ {
+ throw new ApplicationException("Invalid response type");
+ }
+ var res = ((MethodResponseSubtitleDownload)resultDownLoad).Results.First();
+ var data = Convert.FromBase64String(res.Data);
+
+ return new SubtitleResponse
+ {
+ Format = format,
+ Language = language,
+
+ Stream = new MemoryStream(Utilities.Decompress(new MemoryStream(data)))
+ };
+ }
+
+ public async Task<IEnumerable<RemoteSubtitleInfo>> SearchSubtitles(SubtitleSearchRequest request, CancellationToken cancellationToken)
+ {
var imdbIdText = request.GetProviderId(MetadataProviders.Imdb);
long imdbId;
if (string.IsNullOrWhiteSpace(imdbIdText) ||
- long.TryParse(imdbIdText.TrimStart('t'), NumberStyles.Any, _usCulture, out imdbId))
+ !long.TryParse(imdbIdText.TrimStart('t'), NumberStyles.Any, _usCulture, out imdbId))
{
- return response;
+ _logger.Debug("Imdb id missing");
+ return new List<RemoteSubtitleInfo>();
}
-
+
switch (request.ContentType)
{
case SubtitleMediaType.Episode:
if (!request.IndexNumber.HasValue || !request.ParentIndexNumber.HasValue || string.IsNullOrEmpty(request.SeriesName))
{
- _logger.Debug("Information Missing");
- return response;
+ _logger.Debug("Episode information missing");
+ return new List<RemoteSubtitleInfo>();
}
break;
case SubtitleMediaType.Movie:
if (string.IsNullOrEmpty(request.Name))
{
- _logger.Debug("Information Missing");
- return response;
+ _logger.Debug("Movie name missing");
+ return new List<RemoteSubtitleInfo>();
}
break;
}
@@ -76,16 +110,18 @@ namespace MediaBrowser.Providers.Subtitles
if (string.IsNullOrEmpty(request.MediaPath))
{
_logger.Debug("Path Missing");
- return response;
+ return new List<RemoteSubtitleInfo>();
}
Utilities.HttpClient = _httpClient;
OpenSubtitles.SetUserAgent("OS Test User Agent");
- var loginResponse = OpenSubtitles.LogIn("", "", "en");
+
+ var loginResponse = await OpenSubtitles.LogInAsync("", "", "en", cancellationToken).ConfigureAwait(false);
+
if (!(loginResponse is MethodResponseLogIn))
{
_logger.Debug("Login error");
- return response;
+ return new List<RemoteSubtitleInfo>();
}
var subLanguageId = request.Language;
@@ -105,54 +141,42 @@ namespace MediaBrowser.Providers.Subtitles
var result = OpenSubtitles.SearchSubtitles(parms.ToArray());
if (!(result is MethodResponseSubtitleSearch))
{
- _logger.Debug("invalid response type");
- return null;
+ _logger.Debug("Invalid response type");
+ return new List<RemoteSubtitleInfo>();
}
Predicate<SubtitleSearchResult> mediaFilter =
x =>
request.ContentType == SubtitleMediaType.Episode
- ? int.Parse(x.SeriesSeason) == request.ParentIndexNumber && int.Parse(x.SeriesEpisode) == request.IndexNumber
- : long.Parse(x.IDMovieImdb) == imdbId;
+ ? int.Parse(x.SeriesSeason, _usCulture) == request.ParentIndexNumber && int.Parse(x.SeriesEpisode, _usCulture) == request.IndexNumber
+ : long.Parse(x.IDMovieImdb, _usCulture) == imdbId;
var results = ((MethodResponseSubtitleSearch)result).Results;
- var bestResult = results.Where(x => x.SubBad == "0" && mediaFilter(x))
- .OrderBy(x => x.MovieHash == hash)
- .ThenBy(x => Math.Abs(long.Parse(x.MovieByteSize) - movieByteSize))
- .ThenByDescending(x => int.Parse(x.SubDownloadsCnt))
- .ThenByDescending(x => double.Parse(x.SubRating))
- .ToList();
-
- if (!bestResult.Any())
- {
- _logger.Debug("No Subtitles");
- return response;
- }
-
- _logger.Debug("Found " + bestResult.Count + " subtitles.");
- var subtitle = bestResult.First();
- var downloadsList = new[] { int.Parse(subtitle.IDSubtitleFile) };
+ // Avoid implicitly captured closure
+ var hasCopy = hash;
- var resultDownLoad = OpenSubtitles.DownloadSubtitles(downloadsList);
- if (!(resultDownLoad is MethodResponseSubtitleDownload))
- {
- _logger.Debug("invalid response type");
- return response;
- }
- if (!((MethodResponseSubtitleDownload)resultDownLoad).Results.Any())
- {
- _logger.Debug("No Subtitle Downloads");
- return response;
- }
-
- var res = ((MethodResponseSubtitleDownload)resultDownLoad).Results.First();
- var data = Convert.FromBase64String(res.Data);
-
- response.HasContent = true;
- response.Format = subtitle.SubFormat.ToUpper();
- response.Stream = new MemoryStream(Utilities.Decompress(new MemoryStream(data)));
- return response;
+ return results.Where(x => x.SubBad == "0" && mediaFilter(x))
+ .OrderBy(x => x.MovieHash == hash)
+ .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,
+ Language = i.SubLanguageID,
+
+ Id = i.SubFormat + "-" + i.SubLanguageID + "-" + i.IDSubtitle,
+
+ Name = i.SubFileName,
+ DateCreated = DateTime.Parse(i.SubAddDate, _usCulture),
+ IsHashMatch = i.MovieHash == hasCopy
+ });
}
}
}
diff --git a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs
new file mode 100644
index 000000000..6951e8bd0
--- /dev/null
+++ b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs
@@ -0,0 +1,141 @@
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Subtitles;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Providers;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Providers.Subtitles
+{
+ public class SubtitleManager : ISubtitleManager
+ {
+ private ISubtitleProvider[] _subtitleProviders;
+ private readonly ILogger _logger;
+ private readonly IFileSystem _fileSystem;
+ private readonly ILibraryMonitor _monitor;
+
+ public SubtitleManager(ILogger logger, IFileSystem fileSystem, ILibraryMonitor monitor)
+ {
+ _logger = logger;
+ _fileSystem = fileSystem;
+ _monitor = monitor;
+ }
+
+ public void AddParts(IEnumerable<ISubtitleProvider> subtitleProviders)
+ {
+ _subtitleProviders = subtitleProviders.ToArray();
+ }
+
+ public async Task<IEnumerable<RemoteSubtitleInfo>> SearchSubtitles(SubtitleSearchRequest request, CancellationToken cancellationToken)
+ {
+ var providers = _subtitleProviders
+ .Where(i => i.SupportedMediaTypes.Contains(request.ContentType))
+ .ToList();
+
+ var tasks = providers.Select(async i =>
+ {
+ try
+ {
+ return await i.SearchSubtitles(request, cancellationToken).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error downloading subtitles from {0}", ex, i.Name);
+ return new List<RemoteSubtitleInfo>();
+ }
+ });
+
+ var results = await Task.WhenAll(tasks).ConfigureAwait(false);
+
+ return results.SelectMany(i => i);
+ }
+
+ public async Task DownloadSubtitles(Video video,
+ string subtitleId,
+ string providerName,
+ CancellationToken cancellationToken)
+ {
+ var provider = _subtitleProviders.First(i => string.Equals(i.Name, providerName, StringComparison.OrdinalIgnoreCase));
+
+ var response = await provider.GetSubtitles(subtitleId, cancellationToken).ConfigureAwait(false);
+
+ using (var stream = response.Stream)
+ {
+ var savePath = Path.Combine(Path.GetDirectoryName(video.Path),
+ Path.GetFileNameWithoutExtension(video.Path) + "." + response.Language.ToLower() + "." + response.Format.ToLower());
+
+ _logger.Info("Saving subtitles to {0}", savePath);
+
+ _monitor.ReportFileSystemChangeBeginning(savePath);
+
+ try
+ {
+ using (var fs = _fileSystem.GetFileStream(savePath, FileMode.Create, FileAccess.Write, FileShare.Read, true))
+ {
+ await stream.CopyToAsync(fs).ConfigureAwait(false);
+ }
+ }
+ finally
+ {
+ _monitor.ReportFileSystemChangeComplete(savePath, false);
+ }
+ }
+ }
+
+ public Task<IEnumerable<RemoteSubtitleInfo>> SearchSubtitles(Video video, string language, CancellationToken cancellationToken)
+ {
+ if (video.LocationType != LocationType.FileSystem ||
+ video.VideoType != VideoType.VideoFile)
+ {
+ return Task.FromResult<IEnumerable<RemoteSubtitleInfo>>(new List<RemoteSubtitleInfo>());
+ }
+
+ SubtitleMediaType mediaType;
+
+ if (video is Episode)
+ {
+ mediaType = SubtitleMediaType.Episode;
+ }
+ else if (video is Movie)
+ {
+ mediaType = SubtitleMediaType.Movie;
+ }
+ else
+ {
+ // These are the only supported types
+ return Task.FromResult<IEnumerable<RemoteSubtitleInfo>>(new List<RemoteSubtitleInfo>());
+ }
+
+ var request = new SubtitleSearchRequest
+ {
+ ContentType = mediaType,
+ IndexNumber = video.IndexNumber,
+ Language = language,
+ MediaPath = video.Path,
+ Name = video.Name,
+ ParentIndexNumber = video.ParentIndexNumber,
+ ProductionYear = video.ProductionYear,
+ ProviderIds = video.ProviderIds
+ };
+
+ var episode = video as Episode;
+
+ if (episode != null)
+ {
+ request.IndexNumberEnd = episode.IndexNumberEnd;
+ request.SeriesName = episode.SeriesName;
+ }
+
+ return SearchSubtitles(request, cancellationToken);
+ }
+ }
+}