private IEnumerable <DownloadDecision> GetDecisions(List <ReleaseInfo> reports, SearchCriteriaBase searchCriteria = null) { if (reports.Any()) { _logger.ProgressInfo("Processing {0} releases", reports.Count); } else { _logger.ProgressInfo("No results found"); } var reportNumber = 1; foreach (var report in reports) { DownloadDecision decision = null; _logger.ProgressTrace("Processing release {0}/{1}", reportNumber, reports.Count); _logger.Debug("Processing release '{0}' from '{1}'", report.Title, report.Indexer); try { var parsedEpisodeInfo = Parser.Parser.ParseTitle(report.Title); if (parsedEpisodeInfo == null || parsedEpisodeInfo.IsPossibleSpecialEpisode) { var specialEpisodeInfo = _parsingService.ParseSpecialEpisodeTitle(parsedEpisodeInfo, report.Title, report.TvdbId, report.TvRageId, searchCriteria); if (specialEpisodeInfo != null) { parsedEpisodeInfo = specialEpisodeInfo; } } if (parsedEpisodeInfo != null && !parsedEpisodeInfo.SeriesTitle.IsNullOrWhiteSpace()) { var remoteEpisode = _parsingService.Map(parsedEpisodeInfo, report.TvdbId, report.TvRageId, searchCriteria); remoteEpisode.Release = report; if (remoteEpisode.Series == null) { var reason = "Unknown Series"; var matchingTvdbId = _sceneMappingService.FindTvdbId(parsedEpisodeInfo.SeriesTitle, parsedEpisodeInfo.ReleaseTitle); if (matchingTvdbId.HasValue) { reason = $"{parsedEpisodeInfo.SeriesTitle} matches an alias for series with TVDB ID: {matchingTvdbId}"; } decision = new DownloadDecision(remoteEpisode, new Rejection(reason)); } else if (remoteEpisode.Episodes.Empty()) { decision = new DownloadDecision(remoteEpisode, new Rejection("Unable to identify correct episode(s) using release name and scene mappings")); } else { _aggregationService.Augment(remoteEpisode); remoteEpisode.DownloadAllowed = remoteEpisode.Episodes.Any(); decision = GetDecisionForReport(remoteEpisode, searchCriteria); } } if (searchCriteria != null) { if (parsedEpisodeInfo == null) { parsedEpisodeInfo = new ParsedEpisodeInfo { Language = LanguageParser.ParseLanguage(report.Title), Quality = QualityParser.ParseQuality(report.Title) }; } if (parsedEpisodeInfo.SeriesTitle.IsNullOrWhiteSpace()) { var remoteEpisode = new RemoteEpisode { Release = report, ParsedEpisodeInfo = parsedEpisodeInfo }; decision = new DownloadDecision(remoteEpisode, new Rejection("Unable to parse release")); } } } catch (Exception e) { _logger.Error(e, "Couldn't process release."); var remoteEpisode = new RemoteEpisode { Release = report }; decision = new DownloadDecision(remoteEpisode, new Rejection("Unexpected error processing release")); } reportNumber++; if (decision != null) { if (decision.Rejections.Any()) { _logger.Debug("Release rejected for the following reasons: {0}", string.Join(", ", decision.Rejections)); } else { _logger.Debug("Release accepted"); } yield return(decision); } } }
public void Read(string path) { Logger.Debug($"Starting tag read for {path}"); IsValid = false; TagLib.File file = null; try { file = TagLib.File.Create(path); var tag = file.Tag; Title = tag.Title ?? tag.TitleSort; Performers = tag.Performers ?? tag.PerformersSort; AlbumArtists = tag.AlbumArtists ?? tag.AlbumArtistsSort; Track = tag.Track; TrackCount = tag.TrackCount; Album = tag.Album ?? tag.AlbumSort; Disc = tag.Disc; DiscCount = tag.DiscCount; Year = tag.Year; Publisher = tag.Publisher; Duration = file.Properties.Duration; Genres = tag.Genres; ImageSize = tag.Pictures.FirstOrDefault()?.Data.Count ?? 0; MusicBrainzReleaseCountry = tag.MusicBrainzReleaseCountry; MusicBrainzReleaseStatus = tag.MusicBrainzReleaseStatus; MusicBrainzReleaseType = tag.MusicBrainzReleaseType; MusicBrainzReleaseId = tag.MusicBrainzReleaseId; MusicBrainzArtistId = tag.MusicBrainzArtistId; MusicBrainzReleaseArtistId = tag.MusicBrainzReleaseArtistId; MusicBrainzReleaseGroupId = tag.MusicBrainzReleaseGroupId; MusicBrainzTrackId = tag.MusicBrainzTrackId; DateTime tempDate; // Do the ones that aren't handled by the generic taglib implementation if (file.TagTypesOnDisk.HasFlag(TagTypes.Id3v2)) { var id3tag = (TagLib.Id3v2.Tag)file.GetTag(TagTypes.Id3v2); Media = id3tag.GetTextAsString("TMED"); Date = ReadId3Date(id3tag, "TDRC"); OriginalReleaseDate = ReadId3Date(id3tag, "TDOR"); MusicBrainzAlbumComment = UserTextInformationFrame.Get(id3tag, "MusicBrainz Album Comment", false)?.Text.ExclusiveOrDefault(); MusicBrainzReleaseTrackId = UserTextInformationFrame.Get(id3tag, "MusicBrainz Release Track Id", false)?.Text.ExclusiveOrDefault(); } else if (file.TagTypesOnDisk.HasFlag(TagTypes.Xiph)) { // while publisher is handled by taglib, it seems to be mapped to 'ORGANIZATION' and not 'LABEL' like Picard is // https://picard.musicbrainz.org/docs/mappings/ var flactag = (TagLib.Ogg.XiphComment)file.GetTag(TagLib.TagTypes.Xiph); Media = flactag.GetField("MEDIA").ExclusiveOrDefault(); Date = DateTime.TryParse(flactag.GetField("DATE").ExclusiveOrDefault(), out tempDate) ? tempDate : default(DateTime?); OriginalReleaseDate = DateTime.TryParse(flactag.GetField("ORIGINALDATE").ExclusiveOrDefault(), out tempDate) ? tempDate : default(DateTime?); Publisher = flactag.GetField("LABEL").ExclusiveOrDefault(); MusicBrainzAlbumComment = flactag.GetField("MUSICBRAINZ_ALBUMCOMMENT").ExclusiveOrDefault(); MusicBrainzReleaseTrackId = flactag.GetField("MUSICBRAINZ_RELEASETRACKID").ExclusiveOrDefault(); // If we haven't managed to read status/type, try the alternate mapping if (MusicBrainzReleaseStatus.IsNullOrWhiteSpace()) { MusicBrainzReleaseStatus = flactag.GetField("RELEASESTATUS").ExclusiveOrDefault(); } if (MusicBrainzReleaseType.IsNullOrWhiteSpace()) { MusicBrainzReleaseType = flactag.GetField("RELEASETYPE").ExclusiveOrDefault(); } } else if (file.TagTypesOnDisk.HasFlag(TagTypes.Ape)) { var apetag = (TagLib.Ape.Tag)file.GetTag(TagTypes.Ape); Media = apetag.GetItem("Media")?.ToString(); Date = DateTime.TryParse(apetag.GetItem("Year")?.ToString(), out tempDate) ? tempDate : default(DateTime?); OriginalReleaseDate = DateTime.TryParse(apetag.GetItem("Original Date")?.ToString(), out tempDate) ? tempDate : default(DateTime?); Publisher = apetag.GetItem("Label")?.ToString(); MusicBrainzAlbumComment = apetag.GetItem("MUSICBRAINZ_ALBUMCOMMENT")?.ToString(); MusicBrainzReleaseTrackId = apetag.GetItem("MUSICBRAINZ_RELEASETRACKID")?.ToString(); } else if (file.TagTypesOnDisk.HasFlag(TagTypes.Asf)) { var asftag = (TagLib.Asf.Tag)file.GetTag(TagTypes.Asf); Media = asftag.GetDescriptorString("WM/Media"); Date = DateTime.TryParse(asftag.GetDescriptorString("WM/Year"), out tempDate) ? tempDate : default(DateTime?); OriginalReleaseDate = DateTime.TryParse(asftag.GetDescriptorString("WM/OriginalReleaseTime"), out tempDate) ? tempDate : default(DateTime?); Publisher = asftag.GetDescriptorString("WM/Publisher"); MusicBrainzAlbumComment = asftag.GetDescriptorString("MusicBrainz/Album Comment"); MusicBrainzReleaseTrackId = asftag.GetDescriptorString("MusicBrainz/Release Track Id"); } else if (file.TagTypesOnDisk.HasFlag(TagTypes.Apple)) { var appletag = (TagLib.Mpeg4.AppleTag)file.GetTag(TagTypes.Apple); Media = appletag.GetDashBox("com.apple.iTunes", "MEDIA"); Date = DateTime.TryParse(appletag.DataBoxes(FixAppleId("day")).FirstOrDefault()?.Text, out tempDate) ? tempDate : default(DateTime?); OriginalReleaseDate = DateTime.TryParse(appletag.GetDashBox("com.apple.iTunes", "Original Date"), out tempDate) ? tempDate : default(DateTime?); MusicBrainzAlbumComment = appletag.GetDashBox("com.apple.iTunes", "MusicBrainz Album Comment"); MusicBrainzReleaseTrackId = appletag.GetDashBox("com.apple.iTunes", "MusicBrainz Release Track Id"); } OriginalYear = OriginalReleaseDate.HasValue ? (uint)OriginalReleaseDate?.Year : 0; foreach (ICodec codec in file.Properties.Codecs) { IAudioCodec acodec = codec as IAudioCodec; if (acodec != null && (acodec.MediaTypes & MediaTypes.Audio) != MediaTypes.None) { int bitrate = acodec.AudioBitrate; if (bitrate == 0) { // Taglib can't read bitrate for Opus. bitrate = EstimateBitrate(file, path); } Logger.Debug("Audio Properties: " + acodec.Description + ", Bitrate: " + bitrate + ", Sample Size: " + file.Properties.BitsPerSample + ", SampleRate: " + acodec.AudioSampleRate + ", Channels: " + acodec.AudioChannels); Quality = QualityParser.ParseQuality(file.Name, acodec.Description, bitrate, file.Properties.BitsPerSample); Logger.Debug($"Quality parsed: {Quality}, Source: {Quality.QualityDetectionSource}"); MediaInfo = new MediaInfoModel { AudioFormat = acodec.Description, AudioBitrate = bitrate, AudioChannels = acodec.AudioChannels, AudioBits = file.Properties.BitsPerSample, AudioSampleRate = acodec.AudioSampleRate }; } } IsValid = true; } catch (Exception ex) { if (ex is CorruptFileException) { Logger.Warn(ex, $"Tag reading failed for {path}. File is corrupt"); } else { // Log as error so it goes to sentry with correct fingerprint Logger.Error(ex, "Tag reading failed for {0}", path); } } finally { file?.Dispose(); } // make sure these are initialized to avoid errors later on if (Quality == null) { Quality = QualityParser.ParseQuality(path, null, EstimateBitrate(file, path)); Logger.Debug($"Unable to parse qulity from tag, Quality parsed from file path: {Quality}, Source: {Quality.QualityDetectionSource}"); } MediaInfo = MediaInfo ?? new MediaInfoModel(); }
public void should_parse_reality_from_title(string title, int reality) { QualityParser.ParseQuality(title).Revision.Real.Should().Be(reality); }
public void should_parse_version_from_title(string title, int version) { QualityParser.ParseQuality(title).Revision.Version.Should().Be(version); }
public void should_parse_hardcoded_subs(string postTitle, string sub) { QualityParser.ParseQuality(postTitle).HardcodedSubs.Should().Be(sub); }