diff options
| author | Luke Pulverenti <luke.pulverenti@gmail.com> | 2014-05-06 22:28:19 -0400 |
|---|---|---|
| committer | Luke Pulverenti <luke.pulverenti@gmail.com> | 2014-05-06 22:28:19 -0400 |
| commit | 0d025f7fb620bf2a24ca9aa4e5994f132e02e7c0 (patch) | |
| tree | 76137f5f868ed81725d29bbdc6ac0e3b57c304d8 /MediaBrowser.Providers | |
| parent | e1dd361c7bf05af49d9210ab679e85fa00870990 (diff) | |
beginning remote subtitle downloading
Diffstat (limited to 'MediaBrowser.Providers')
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); + } + } +} |
