public List <ImportDecision> GetImportDecisions(List <string> videoFiles, Series series, DownloadClientItem downloadClientItem, ParsedEpisodeInfo folderInfo, bool sceneSource) { var newFiles = _mediaFileService.FilterExistingFiles(videoFiles.ToList(), series); _logger.Debug("Analyzing {0}/{1} files.", newFiles.Count, videoFiles.Count()); ParsedEpisodeInfo downloadClientItemInfo = null; if (downloadClientItem != null) { downloadClientItemInfo = Parser.Parser.ParseTitle(downloadClientItem.Title); } var nonSampleVideoFileCount = GetNonSampleVideoFileCount(newFiles, series, downloadClientItemInfo, folderInfo); var decisions = new List <ImportDecision>(); foreach (var file in newFiles) { var localEpisode = new LocalEpisode { Series = series, DownloadClientEpisodeInfo = downloadClientItemInfo, FolderEpisodeInfo = folderInfo, Path = file, SceneSource = sceneSource, ExistingFile = series.Path.IsParentPath(file) }; decisions.AddIfNotNull(GetDecision(localEpisode, downloadClientItem, nonSampleVideoFileCount > 1)); } return(decisions); }
public void Setup() { _series = Builder <Series> .CreateNew() .With(s => s.Title = "30 Stone") .With(s => s.CleanTitle = "stone") .Build(); _episodes = Builder <Episode> .CreateListOfSize(1) .All() .With(e => e.AirDate = DateTime.Today.ToString(Episode.AIR_DATE_FORMAT)) .Build() .ToList(); _parsedEpisodeInfo = new ParsedEpisodeInfo { SeriesTitle = _series.Title, SeasonNumber = 1, EpisodeNumbers = new[] { 1 }, AbsoluteEpisodeNumbers = new int[0] }; _singleEpisodeSearchCriteria = new SingleEpisodeSearchCriteria { Series = _series, EpisodeNumber = _episodes.First().EpisodeNumber, SeasonNumber = _episodes.First().SeasonNumber, Episodes = _episodes }; Mocker.GetMock <ISeriesService>() .Setup(s => s.FindByTitle(It.IsAny <string>())) .Returns(_series); }
private CachedSeedConfiguration FetchIndexer(string infoHash) { var historyItem = _downloadHistoryService.GetLatestGrab(infoHash); if (historyItem == null) { _logger.Debug("No download history item for infohash {0}, unable to provide seed configuration", infoHash); return(null); } ParsedEpisodeInfo parsedEpisodeInfo = null; if (historyItem.Release != null) { parsedEpisodeInfo = Parser.Parser.ParseTitle(historyItem.Release.Title); } if (parsedEpisodeInfo == null) { _logger.Debug("No parsed title in download history item for infohash {0}, unable to provide seed configuration", infoHash); return(null); } return(new CachedSeedConfiguration { IndexerId = historyItem.IndexerId, FullSeason = parsedEpisodeInfo.FullSeason }); }
private ParsedEpisodeInfo ParseSpecialEpisodeTitle(string releaseTitle, Series series) { // find special episode in series season 0 var episode = _episodeService.FindEpisodeByTitle(series.Id, 0, releaseTitle); if (episode != null) { // create parsed info from tv episode var info = new ParsedEpisodeInfo { ReleaseTitle = releaseTitle, SeriesTitle = series.Title, SeriesTitleInfo = new SeriesTitleInfo { Title = series.Title }, SeasonNumber = episode.SeasonNumber, EpisodeNumbers = new int[1] { episode.EpisodeNumber }, FullSeason = false, Quality = QualityParser.ParseQuality(releaseTitle), ReleaseGroup = Parser.ParseReleaseGroup(releaseTitle), Language = LanguageParser.ParseLanguage(releaseTitle), Special = true }; _logger.Debug("Found special episode {0} for title '{1}'", info, releaseTitle); return(info); } return(null); }
private bool ShouldUseFolderName(List <string> videoFiles, Series series, ParsedEpisodeInfo folderInfo) { if (folderInfo == null) { return(false); } if (folderInfo.FullSeason) { return(false); } return(videoFiles.Count(file => { var sample = _detectSample.IsSample(series, file, folderInfo.IsPossibleSpecialEpisode); if (sample == DetectSampleResult.Sample) { return false; } if (SceneChecker.IsSceneTitle(Path.GetFileName(file))) { return false; } return true; }) == 1); }
private List<Episode> GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series series, int mappedSeasonNumber, bool sceneSource, SearchCriteriaBase searchCriteria) { if (parsedEpisodeInfo.FullSeason) { return _episodeService.GetEpisodesBySeason(series.Id, mappedSeasonNumber); } if (parsedEpisodeInfo.IsDaily) { var episodeInfo = GetDailyEpisode(series, parsedEpisodeInfo.AirDate, parsedEpisodeInfo.DailyPart, searchCriteria); if (episodeInfo != null) { return new List<Episode> { episodeInfo }; } return new List<Episode>(); } if (parsedEpisodeInfo.IsAbsoluteNumbering) { return GetAnimeEpisodes(series, parsedEpisodeInfo, mappedSeasonNumber, sceneSource, searchCriteria); } return GetStandardEpisodes(series, parsedEpisodeInfo, mappedSeasonNumber, sceneSource, searchCriteria); }
public void Setup() { _series = Builder <Series> .CreateNew() .With(s => s.Title = "30 Rock") .With(s => s.CleanTitle = "rock") .Build(); _episodes = Builder <Episode> .CreateListOfSize(1) .All() .With(e => e.AirDate = DateTime.Today.ToString(Episode.AIR_DATE_FORMAT)) .Build() .ToList(); _parsedEpisodeInfo = new ParsedEpisodeInfo { SeriesTitle = _series.Title, SeriesTitleInfo = new SeriesTitleInfo(), SeasonNumber = 1, EpisodeNumbers = new[] { 1 } }; _singleEpisodeSearchCriteria = new SingleEpisodeSearchCriteria { Series = _series, EpisodeNumber = _episodes.First().EpisodeNumber, SeasonNumber = _episodes.First().SeasonNumber, Episodes = _episodes }; Mocker.GetMock <ISceneMappingService>() .Setup(v => v.GetTvdbSeasonNumber(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <int>())) .Returns <string, string, int>((s, r, i) => i); }
private List <Episode> GetAnimeEpisodes(Series series, ParsedEpisodeInfo parsedEpisodeInfo, bool sceneSource) { var result = new List <Episode>(); var sceneSeasonNumber = _sceneMappingService.GetSceneSeasonNumber(parsedEpisodeInfo.SeriesTitle); foreach (var absoluteEpisodeNumber in parsedEpisodeInfo.AbsoluteEpisodeNumbers) { Episode episode = null; if (parsedEpisodeInfo.Special) { episode = _episodeService.FindEpisode(series.Id, 0, absoluteEpisodeNumber); } else if (sceneSource) { // Is there a reason why we excluded season 1 from this handling before? // Might have something to do with the scene name to season number check // If this needs to be reverted tests will need to be added if (sceneSeasonNumber.HasValue) { var episodes = _episodeService.FindEpisodesBySceneNumbering(series.Id, sceneSeasonNumber.Value, absoluteEpisodeNumber); if (episodes.Count == 1) { episode = episodes.First(); } if (episode == null) { episode = _episodeService.FindEpisode(series.Id, sceneSeasonNumber.Value, absoluteEpisodeNumber); } } else { episode = _episodeService.FindEpisodeBySceneNumbering(series.Id, absoluteEpisodeNumber); } } if (episode == null) { episode = _episodeService.FindEpisode(series.Id, absoluteEpisodeNumber); } if (episode != null) { _logger.Debug("Using absolute episode number {0} for: {1} - TVDB: {2}x{3:00}", absoluteEpisodeNumber, series.Title, episode.SeasonNumber, episode.EpisodeNumber); result.Add(episode); } } return(result); }
public void Setup() { _series = Builder <Series> .CreateNew() .With(s => s.Title = "30 Rock") .With(s => s.CleanTitle = "rock") .Build(); _episodes = Builder <Episode> .CreateListOfSize(1) .All() .With(e => e.AirDate = DateTime.Today.ToString(Episode.AIR_DATE_FORMAT)) .Build() .ToList(); _parsedEpisodeInfo = new ParsedEpisodeInfo { SeriesTitle = _series.Title, SeriesTitleInfo = new SeriesTitleInfo(), SeasonNumber = 1, EpisodeNumbers = new[] { 1 } }; _singleEpisodeSearchCriteria = new SingleEpisodeSearchCriteria { Series = _series, EpisodeNumber = _episodes.First().EpisodeNumber, SeasonNumber = _episodes.First().SeasonNumber, Episodes = _episodes }; }
public List <Episode> GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series series, bool sceneSource, SearchCriteriaBase searchCriteria = null) { if (parsedEpisodeInfo.FullSeason) { return(_episodeService.GetEpisodesBySeason(series.Id, parsedEpisodeInfo.SeasonNumber)); } if (parsedEpisodeInfo.IsDaily) { if (series.SeriesType == SeriesTypes.Standard) { _logger.Warn("Found daily-style episode for non-daily series: {0}.", series); return(new List <Episode>()); } var episodeInfo = GetDailyEpisode(series, parsedEpisodeInfo.AirDate, searchCriteria); if (episodeInfo != null) { return(new List <Episode> { episodeInfo }); } return(new List <Episode>()); } if (parsedEpisodeInfo.IsAbsoluteNumbering) { return(GetAnimeEpisodes(series, parsedEpisodeInfo, sceneSource)); } return(GetStandardEpisodes(series, parsedEpisodeInfo, sceneSource, searchCriteria)); }
private ParsedEpisodeInfo ParseSpecialEpisodeTitle(string title, Series series) { // find special episode in series season 0 var episode = _episodeService.FindEpisodeByTitle(series.Id, 0, title); if (episode != null) { // create parsed info from tv episode var info = new ParsedEpisodeInfo(); info.SeriesTitle = series.Title; info.SeriesTitleInfo = new SeriesTitleInfo(); info.SeriesTitleInfo.Title = info.SeriesTitle; info.SeasonNumber = episode.SeasonNumber; info.EpisodeNumbers = new int[1] { episode.EpisodeNumber }; info.FullSeason = false; info.Quality = QualityParser.ParseQuality(title); info.ReleaseGroup = Parser.ParseReleaseGroup(title); info.Language = Parser.ParseLanguage(title); info.Special = true; _logger.Info("Found special episode {0} for title '{1}'", info, title); return(info); } return(null); }
private ParsedEpisodeInfo GetSpecialEpisodeInfo(LocalEpisode localEpisode, ParsedEpisodeInfo parsedEpisodeInfo) { var title = Path.GetFileNameWithoutExtension(localEpisode.Path); var specialEpisodeInfo = _parsingService.ParseSpecialEpisodeTitle(parsedEpisodeInfo, title, localEpisode.Series); return(specialEpisodeInfo); }
private bool ShouldUseFolderName(List <string> videoFiles, Series series, ParsedEpisodeInfo folderInfo) { if (folderInfo == null) { return(false); } if (folderInfo.FullSeason) { return(false); } return(videoFiles.Count(file => { var size = _diskProvider.GetFileSize(file); var fileQuality = QualityParser.ParseQuality(file); var sample = _detectSample.IsSample(series, GetQuality(folderInfo, fileQuality, series), file, size, folderInfo.SeasonNumber); if (sample) { return false; } if (SceneChecker.IsSceneTitle(Path.GetFileName(file))) { return false; } return true; }) == 1); }
public List <Episode> GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series series, bool sceneSource, SearchCriteriaBase searchCriteria = null) { if (parsedEpisodeInfo.FullSeason) { return(_episodeService.GetEpisodesBySeason(series.Id, parsedEpisodeInfo.SeasonNumber)); } if (parsedEpisodeInfo.IsDaily) { var episodeInfo = GetDailyEpisode(series, parsedEpisodeInfo.AirDate, parsedEpisodeInfo.DailyPart, searchCriteria); if (episodeInfo != null) { return(new List <Episode> { episodeInfo }); } return(new List <Episode>()); } if (parsedEpisodeInfo.IsAbsoluteNumbering) { return(GetAnimeEpisodes(series, parsedEpisodeInfo, sceneSource)); } return(GetStandardEpisodes(series, parsedEpisodeInfo, sceneSource, searchCriteria)); }
public RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int tvdbId, int tvRageId, SearchCriteriaBase searchCriteria = null) { var remoteEpisode = new RemoteEpisode { ParsedEpisodeInfo = parsedEpisodeInfo, }; var series = GetSeries(parsedEpisodeInfo, tvdbId, tvRageId, searchCriteria); if (series == null) { return(remoteEpisode); } remoteEpisode.Series = series; if (ValidateParsedEpisodeInfo.ValidateForSeriesType(parsedEpisodeInfo, series)) { remoteEpisode.Episodes = GetEpisodes(parsedEpisodeInfo, series, true, searchCriteria); } else { remoteEpisode.Episodes = new List <Episode>(); } return(remoteEpisode); }
private Series GetSeries(ParsedEpisodeInfo parsedEpisodeInfo, int tvRageId, SearchCriteriaBase searchCriteria) { var tvdbId = _sceneMappingService.FindTvDbId(parsedEpisodeInfo.SeriesTitle); if (tvdbId.HasValue) { if (searchCriteria.Series.TvdbId == tvdbId) { return(searchCriteria.Series); } } if (parsedEpisodeInfo.SeriesTitle.CleanSeriesTitle() == searchCriteria.Series.CleanTitle) { return(searchCriteria.Series); } if (tvRageId > 0 && tvRageId == searchCriteria.Series.TvRageId) { //TODO: If series is found by TvRageId, we should report it as a scene naming exception, since it will fail to import return(searchCriteria.Series); } return(GetSeries(parsedEpisodeInfo, tvRageId)); }
private Language GetLanguage(ParsedEpisodeInfo parsedEpisodeInfo, List <Episode> episodes) { if (parsedEpisodeInfo == null) { return(Language.English); } var normalizedReleaseTitle = Parser.Parser.NormalizeEpisodeTitle(parsedEpisodeInfo.ReleaseTitle); foreach (var episode in episodes) { var episodeTitleLanguage = LanguageParser.ParseLanguage(episode.Title, false); if (episodeTitleLanguage != Language.Unknown && episodeTitleLanguage == parsedEpisodeInfo.Language) { // Release title contains the episode title, return english instead of the parsed language. if (normalizedReleaseTitle.ContainsIgnoreCase(Parser.Parser.NormalizeEpisodeTitle(episode.Title))) { return(Language.English); } } } return(parsedEpisodeInfo.Language); }
public RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int seriesId, IEnumerable <int> episodeIds) { return(new RemoteEpisode { ParsedEpisodeInfo = parsedEpisodeInfo, Series = _seriesService.GetSeries(seriesId), Episodes = _episodeService.GetEpisodes(episodeIds) }); }
private string GetReleaseGroup(ParsedEpisodeInfo episodeInfo, bool skipFullSeason) { if (episodeInfo == null || episodeInfo.FullSeason && skipFullSeason) { return(null); } return(episodeInfo.ReleaseGroup); }
private ImportDecision GetDecision(string file, Series series, ParsedEpisodeInfo folderInfo, bool sceneSource, bool shouldUseFolderName) { ImportDecision decision = null; try { var localEpisode = _parsingService.GetLocalEpisode(file, series, shouldUseFolderName ? folderInfo : null, sceneSource); if (localEpisode != null) { localEpisode.Quality = GetQuality(folderInfo, localEpisode.Quality, series); localEpisode.Size = _diskProvider.GetFileSize(file); _logger.Debug("Size: {0}", localEpisode.Size); //TODO: make it so media info doesn't ruin the import process of a new series if (sceneSource) { localEpisode.MediaInfo = _videoFileInfoReader.GetMediaInfo(file); } if (localEpisode.Episodes.Empty()) { decision = new ImportDecision(localEpisode, new Rejection("Invalid season or episode")); } else { decision = GetDecision(localEpisode); } } else { localEpisode = new LocalEpisode(); localEpisode.Path = file; decision = new ImportDecision(localEpisode, new Rejection("Unable to parse file")); } } catch (Exception e) { _logger.Error(e, "Couldn't import file. " + file); var localEpisode = new LocalEpisode { Path = file }; decision = new ImportDecision(localEpisode, new Rejection("Unexpected error processing file")); } if (decision == null) { _logger.Error("Unable to make a decision on {0}", file); } return(decision); }
private RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int tvdbId, int tvRageId, Series series, SearchCriteriaBase searchCriteria) { var sceneMapping = _sceneMappingService.FindSceneMapping(parsedEpisodeInfo.SeriesTitle, parsedEpisodeInfo.ReleaseTitle, parsedEpisodeInfo.SeasonNumber); var remoteEpisode = new RemoteEpisode { ParsedEpisodeInfo = parsedEpisodeInfo, SceneMapping = sceneMapping, MappedSeasonNumber = parsedEpisodeInfo.SeasonNumber }; // For now we just detect tvdb vs scene, but we can do multiple 'origins' in the future. var sceneSource = true; if (sceneMapping != null) { if (sceneMapping.SeasonNumber.HasValue && sceneMapping.SeasonNumber.Value >= 0 && sceneMapping.SceneSeasonNumber <= parsedEpisodeInfo.SeasonNumber) { remoteEpisode.MappedSeasonNumber += sceneMapping.SeasonNumber.Value - sceneMapping.SceneSeasonNumber.Value; } if (sceneMapping.SceneOrigin == "tvdb") { sceneSource = false; } } if (series == null) { series = GetSeries(parsedEpisodeInfo, tvdbId, tvRageId, sceneMapping, searchCriteria); } if (series != null) { remoteEpisode.Series = series; if (ValidateParsedEpisodeInfo.ValidateForSeriesType(parsedEpisodeInfo, series)) { remoteEpisode.Episodes = GetEpisodes(parsedEpisodeInfo, series, remoteEpisode.MappedSeasonNumber, sceneSource, searchCriteria); } } if (remoteEpisode.Episodes == null) { remoteEpisode.Episodes = new List <Episode>(); } if (searchCriteria != null) { var requestedEpisodes = searchCriteria.Episodes.ToDictionaryIgnoreDuplicates(v => v.Id); remoteEpisode.EpisodeRequested = remoteEpisode.Episodes.Any(v => requestedEpisodes.ContainsKey(v.Id)); } return(remoteEpisode); }
private QualityModel GetQuality(ParsedEpisodeInfo folderInfo, QualityModel fileQuality, Series series) { if (UseFolderQuality(folderInfo, fileQuality, series)) { _logger.Debug("Using quality from folder: {0}", folderInfo.Quality); return(folderInfo.Quality); } return(fileQuality); }
private bool ValidateSeasonAndEpisodeNumbers(List <Episode> episodes, ParsedEpisodeInfo parsedEpisodeInfo) { if (parsedEpisodeInfo.SeasonNumber != episodes.First().SeasonNumber || !parsedEpisodeInfo.EpisodeNumbers.OrderBy(e => e).SequenceEqual(episodes.Select(e => e.EpisodeNumber).OrderBy(e => e))) { return(false); } return(true); }
public void should_return_true_when_episode_numbers_is_empty() { var parsedEpisodeInfo = new ParsedEpisodeInfo { SeasonNumber = 1, SeriesTitle = "" }; parsedEpisodeInfo.IsPossibleSpecialEpisode.Should().BeTrue(); }
public void Setup() { _parsedEpisodeInfo = Builder <ParsedEpisodeInfo> .CreateNew() .With(p => p.AirDate = null) .Build(); _series = Builder <Series> .CreateNew() .With(s => s.SeriesType = SeriesTypes.Standard) .Build(); }
private QualityModel GetQuality(ParsedEpisodeInfo folderInfo, QualityModel fileQuality, Series series) { if (folderInfo != null && folderInfo.Quality.Quality != Quality.Unknown && fileQuality.QualitySource == QualitySource.Extension) { _logger.Debug("Using quality from folder: {0}", folderInfo.Quality); return folderInfo.Quality; } return fileQuality; }
private Language GetLanguage(ParsedEpisodeInfo parsedEpisodeInfo) { if (parsedEpisodeInfo == null) { // English is the default language when otherwise unknown return(Language.English); } return(parsedEpisodeInfo.Language); }
public RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, Int32 seriesId, IEnumerable <Int32> episodeIds) { var remoteEpisode = new RemoteEpisode { ParsedEpisodeInfo = parsedEpisodeInfo, Series = _seriesService.GetSeries(seriesId), Episodes = _episodeService.GetEpisodes(episodeIds) }; return(remoteEpisode); }
public List <Episode> GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series series, bool sceneSource, SearchCriteriaBase searchCriteria = null) { if (sceneSource) { var remoteEpisode = Map(parsedEpisodeInfo, 0, 0, series, searchCriteria); return(remoteEpisode.Episodes); } return(GetEpisodes(parsedEpisodeInfo, series, parsedEpisodeInfo.SeasonNumber, sceneSource, searchCriteria)); }
public void should_not_treat_files_without_a_series_title_as_a_special() { var parsedEpisodeInfo = new ParsedEpisodeInfo { EpisodeNumbers = new[] { 7 }, SeasonNumber = 1, SeriesTitle = "" }; parsedEpisodeInfo.IsPossibleSpecialEpisode.Should().BeFalse(); }