public async Task<IEnumerable<RemoteSubtitleInfo>> SearchSubtitles(SubtitleSearchRequest request, CancellationToken cancellationToken) { var contentType = request.ContentType; var providers = _subtitleProviders .Where(i => i.SupportedMediaTypes.Contains(contentType)) .ToList(); // If not searching all, search one at a time until something is found if (!request.SearchAllProviders) { foreach (var provider in providers) { try { var searchResults = await provider.Search(request, cancellationToken).ConfigureAwait(false); var list = searchResults.ToList(); if (list.Count > 0) { Normalize(list); return list; } } catch (Exception ex) { _logger.ErrorException("Error downloading subtitles from {0}", ex, provider.Name); } } return new List<RemoteSubtitleInfo>(); } var tasks = providers.Select(async i => { try { var searchResults = await i.Search(request, cancellationToken).ConfigureAwait(false); var list = searchResults.ToList(); Normalize(list); return list; } 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); }
private async Task<bool> DownloadSubtitles(Video video, List<MediaStream> mediaStreams, bool skipIfEmbeddedSubtitlesPresent, bool skipIfAudioTrackMatches, bool requirePerfectMatch, string language, VideoContentType mediaType, CancellationToken cancellationToken) { // There's already subtitles for this language if (mediaStreams.Any(i => i.Type == MediaStreamType.Subtitle && i.IsTextSubtitleStream && string.Equals(i.Language, language, StringComparison.OrdinalIgnoreCase))) { return false; } var audioStreams = mediaStreams.Where(i => i.Type == MediaStreamType.Audio).ToList(); var defaultAudioStreams = audioStreams.Where(i => i.IsDefault).ToList(); // If none are marked as default, just take a guess if (defaultAudioStreams.Count == 0) { defaultAudioStreams = audioStreams.Take(1).ToList(); } // There's already a default audio stream for this language if (skipIfAudioTrackMatches && defaultAudioStreams.Any(i => string.Equals(i.Language, language, StringComparison.OrdinalIgnoreCase))) { return false; } // There's an internal subtitle stream for this language if (skipIfEmbeddedSubtitlesPresent && mediaStreams.Any(i => i.Type == MediaStreamType.Subtitle && !i.IsExternal && 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, // Stop as soon as we find something SearchAllProviders = false, IsPerfectMatch = requirePerfectMatch }; 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, cancellationToken) .ConfigureAwait(false); return true; } } catch (Exception ex) { _logger.ErrorException("Error downloading subtitles", ex); } return false; }
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)) .OrderBy(x => (x.MovieHash == hash ? 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 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>()); } VideoContentType mediaType; if (video is Episode) { mediaType = VideoContentType.Episode; } else if (video is Movie) { mediaType = VideoContentType.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, RuntimeTicks = video.RunTimeTicks }; var episode = video as Episode; if (episode != null) { request.IndexNumberEnd = episode.IndexNumberEnd; request.SeriesName = episode.SeriesName; } return SearchSubtitles(request, cancellationToken); }