private async Task <SubtitleResponse> GetSubtitlesInternal(string id, SubtitleOptions options, CancellationToken cancellationToken) { if (string.IsNullOrWhiteSpace(id)) { throw new ArgumentNullException("id"); } if (_dailyDownloadCount >= MaxDownloadsPerDay && !options.IsOpenSubtitleVipAccount) { throw new InvalidOperationException("Open Subtitle's daily download limit has been exceeded. Please try again tomorrow."); } 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); var resultDownLoad = await OpenSubtitles.DownloadSubtitlesAsync(downloadsList, cancellationToken).ConfigureAwait(false); if (!(resultDownLoad is MethodResponseSubtitleDownload)) { throw new ApplicationException("Invalid response type"); } var results = ((MethodResponseSubtitleDownload)resultDownLoad).Results; 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.Message ?? string.Empty, resultDownLoad.Status ?? 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))) }); }
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"); }
public OpenSubtitleDownloader(ILoggerFactory loggerFactory, IHttpClient httpClient, IServerConfigurationManager config, IJsonSerializer json, IFileSystem fileSystem) { _logger = loggerFactory.CreateLogger(GetType().Name); _httpClient = httpClient; _config = config; _json = json; _fileSystem = fileSystem; _config.NamedConfigurationUpdating += _config_NamedConfigurationUpdating; Utilities.HttpClient = httpClient; OpenSubtitles.SetUserAgent("jellyfin"); }
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; // Reset the count every 24 hours _dailyTimer = new Timer(state => _dailyDownloadCount = 0, null, TimeSpan.FromHours(24), TimeSpan.FromHours(24)); Utilities.HttpClient = httpClient; OpenSubtitles.SetUserAgent("mediabrowser.tv"); }
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 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 <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))); }
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))) }); }
public async Task <ItemUpdateType> GetSubtitle(Episode item, CancellationToken cancellationToken) { _logger.Debug("Start with: " + item.Name); if (!Supports(item)) { _logger.Debug("Not Supported"); return(ItemUpdateType.None); } _logger.Debug("Supported"); if (!item.IndexNumber.HasValue || !item.ParentIndexNumber.HasValue) { _logger.Debug("Information Missing"); return(ItemUpdateType.None); } if (string.IsNullOrEmpty(item.Path)) { _logger.Debug("Path Missing"); return(ItemUpdateType.None); } OpenSubtitles.SetUserAgent("OS Test User Agent"); var loginResponse = OpenSubtitles.LogIn("", "", "en"); if (!(loginResponse is MethodResponseLogIn)) { _logger.Debug("Login error"); return(ItemUpdateType.None); } var user = _userManager.Users.Where(u => { var subtitleUser = UserHelper.GetUser(u); return(subtitleUser != null && subtitleUser.SubtitleLocations != null && subtitleUser.SubtitleLocations.Length > 0); }).Select(UserHelper.GetUser).First(); var subLanguageId = user.SubtitleLanguage; _logger.Debug("User language: " + subLanguageId); var hash = Utilities.ComputeHash(item.Path); var fileInfo = new FileInfo(item.Path); var movieByteSize = fileInfo.Length; _logger.Debug(string.Format("{0} - {1} - {2}", subLanguageId, hash, movieByteSize)); _logger.Debug(string.Format("{0} - {1} - {2} - {3}", subLanguageId, item.SeriesName, item.ParentIndexNumber.Value.ToString(), item.IndexNumber.Value.ToString())); var parms = new List <SubtitleSearchParameters> { new SubtitleSearchParameters(subLanguageId, hash, movieByteSize), new SubtitleSearchParameters(subLanguageId, item.SeriesName, item.ParentIndexNumber.Value.ToString(), item.IndexNumber.Value.ToString()), }; var result = OpenSubtitles.SearchSubtitles(parms.ToArray()); if (!(result is MethodResponseSubtitleSearch)) { _logger.Debug("invalid response type"); return(ItemUpdateType.None); } var downloadedSubtitles = new List <Subtitle>(Plugin.Instance.PluginConfiguration.DownloadedSubtitles); var results = ((MethodResponseSubtitleSearch)result).Results; var bestResult = results.Where(x => x.SubBad == "0" && int.Parse(x.SeriesSeason) == item.ParentIndexNumber && int.Parse(x.SeriesEpisode) == item.IndexNumber) .Where(x => downloadedSubtitles.All(y => y.IdSubtitle != x.IDSubtitle)) .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(ItemUpdateType.None); } _logger.Debug("Found " + bestResult.Count + " subtitles."); var subtitle = bestResult.First(); var downloadsList = new[] { int.Parse(subtitle.IDSubtitleFile) }; var resultDownLoad = OpenSubtitles.DownloadSubtitles(downloadsList); if (!(resultDownLoad is MethodResponseSubtitleDownload)) { _logger.Debug("invalid response type"); return(ItemUpdateType.None); } if (!((MethodResponseSubtitleDownload)resultDownLoad).Results.Any()) { _logger.Debug("No Subtitle Downloads"); return(ItemUpdateType.None); } var res = ((MethodResponseSubtitleDownload)resultDownLoad).Results.First(); var data = Convert.FromBase64String(res.Data); var target = Utilities.Decompress(new MemoryStream(data)); // now save the subtitle var fileName = Path.Combine(fileInfo.DirectoryName, string.Format("{0}.{1}.srt", Path.GetFileNameWithoutExtension(fileInfo.Name), subtitle.SubLanguageID)); Stream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write); stream.Write(target, 0, target.Length); stream.Close(); _logger.Debug("Subtitles downloaded: " + subtitle.SubFileName); var configuration = Plugin.Instance.Configuration; downloadedSubtitles.Add(new Subtitle { IdSubtitle = subtitle.IDSubtitle, Date = DateTime.Now, SubtitleFileName = subtitle.SubFileName }); configuration.DownloadedSubtitles = downloadedSubtitles.ToArray(); Plugin.Instance.UpdateConfiguration(configuration); return(ItemUpdateType.MetadataEdit); }