public Issue Add(string message, Severity severity = Severity.Error, IssueTags tag = IssueTags.None, string prompt = null, Func <bool, string> repairer = null, bool isFinalRepairer = false) { System.Diagnostics.Debug.Assert((prompt == null) == (repairer == null)); if (repairer != null) { // Force Warning as minimum for repairables. if (severity < Severity.Warning) { severity = Severity.Warning; } ++Data.RepairableCount; } var issue = new Issue(this, message, severity, tag, prompt, repairer, isFinalRepairer); Data.items.Add(issue); Severity level = issue.Level; if (Data.MaxSeverity < level) { Data.MaxSeverity = level; Data.Severest = issue; } Data.NotifyPropertyChanged(nameof(FixedMessage)); return(issue); }
private Vector(IssueTags warnEscalator = IssueTags.None, IssueTags errEscalator = IssueTags.None) { this.items = new ObservableCollection <Issue>(); this.Items = new ReadOnlyObservableCollection <Issue> (this.items); this.MaxSeverity = Severity.NoIssue; this.WarnEscalator = warnEscalator; this.ErrEscalator = errEscalator; }
public Model(string root, string filter = null, string exclusion = null, Interaction action = Interaction.None, Granularity scope = Granularity.Summary, IssueTags warnEscalator = IssueTags.None, IssueTags errEscalator = IssueTags.None) : this() { this._data = new Diags(this); Data.Root = root; Data.Filter = filter; Data.Exclusion = exclusion; Data.Response = action; Data.Scope = scope; Data.WarnEscalator = warnEscalator; Data.ErrEscalator = errEscalator; }
public void Escalate(IssueTags warnEscalator, IssueTags errEscalator) { // Accumulate escalations. Data.WarnEscalator |= warnEscalator; Data.ErrEscalator |= errEscalator; foreach (var issue in Data.items) { Severity level = Data.GetLevel(issue.BaseLevel, issue.Tag); if (Data.MaxSeverity < level) { Data.MaxSeverity = level; } } }
public void Escalate(IssueTags warnEscalator, IssueTags errEscalator) { // Accumulate escalations. Data.WarnEscalator |= warnEscalator; Data.ErrEscalator |= errEscalator; foreach (var issue in Data.items) { Severity level = issue.Level; if (Data.MaxSeverity < level) { Data.MaxSeverity = level; Data.Severest = issue; } } }
public Severity MaxLevelWhereAny(IssueTags tags) { var result = Severity.NoIssue; foreach (Issue issue in items) { if ((issue.Tag & tags) != 0) { Severity level = GetLevel(issue.BaseLevel, issue.Tag); if (result < level) { result = level; } } } return(result); }
public Severity GetLevel(Severity baseLevel, IssueTags tags) { if (baseLevel < Severity.Warning) { if ((tags & ErrEscalator) != 0) { return(Severity.Error); } if ((tags & WarnEscalator) != 0) { return(Severity.Warning); } } else if (baseLevel == Severity.Warning && (tags & (ErrEscalator)) != 0) { return(Severity.Error); } return(baseLevel); }
private void GetDiagnostics() { if (Data.Header.BitRate == null) { IssueModel.Add("Invalid bit rate."); } if (Data.Header.ChannelMode != Mp3ChannelMode.JointStereo) { IssueTags tags = Data.Header.ChannelMode == Mp3ChannelMode.Stereo? IssueTags.Overstandard : IssueTags.Substandard; IssueModel.Add($"Channel mode is {Data.Header.ChannelMode}.", Severity.Advisory, tags); } if (Data.Header.SampleRate < 44100) { IssueModel.Add($"Frequency is {Data.Header.SampleRate} Hz (expecting 44100 or better)", Severity.Advisory, IssueTags.Substandard); } else if (Data.Header.SampleRate > 44100) { IssueModel.Add($"Frequency is {Data.Header.SampleRate}", Severity.Advisory, IssueTags.Overstandard); } if (Data.Xing != null) { if (Data.Header.CrcProtectedBit == 0) { IssueModel.Add("Header not flagged for CRC protection.", Severity.Noise); } if (!Data.Xing.HasFrameCount) { IssueModel.Add("Missing XING frame count."); } if (!Data.Xing.HasSize) { IssueModel.Add("Missing XING file size."); } if (!Data.Xing.HasTableOfContents) { IssueModel.Add("Missing XING table of contents."); } else if (Data.Xing.IsTocCorrupt()) { IssueModel.Add("XING table of contents is corrupt."); } if (Data.Xing.HasQualityIndicator) { var qi = Data.Xing.QualityIndicator; if (qi < 0 || qi > 100) { IssueModel.Add($"Quality indicator of {qi} is out of range."); } else if (Data.Lame != null && Data.Lame.IsVbr && qi < 78) { IssueModel.Add($"VBR quality of {qi} is substandard.", Severity.Advisory, IssueTags.Substandard); } } } if (Data.Lame == null) { IssueModel.Add("Not a LAME encoding.", Severity.Advisory, IssueTags.Substandard); } else { var isBlessed = blessedLames.Any(item => item == Data.Lame.LameVersion); if (!isBlessed) { IssueModel.Add("LAME version is not favored.", Severity.Advisory, IssueTags.Substandard); } if (Data.Lame.LameSize != Data.MediaCount) { IssueModel.Add("Indicated LAME audio size incorrect or unrecognized tag block.", Severity.Warning); } if (Data.Lame.TagRevision == 0xF) { IssueModel.Add($"Tag revision {Data.Lame.TagRevision} invalid."); } if (Data.Lame.BitrateMethod == 0 || Data.Lame.BitrateMethod == 0xF) { IssueModel.Add($"Bitrate method {Data.Lame.BitrateMethod} invalid."); } if (Data.Lame.IsAbr) { IssueModel.Add("ABR encoding method is obsolete.", Severity.Advisory, IssueTags.Substandard); } if (Data.Lame.AudiophileReplayGain != 0) { IssueModel.Add($"Audiophile ReplayGain ({Data.Lame.AudiophileReplayGain}) usage is obsolete.", Severity.Advisory, IssueTags.Substandard); } if (Data.Lame.IsCbr && Mp3Header.IsBadCbr(Data.Header.MpegVersionBits, Data.Lame.MinBitRate)) { IssueModel.Add($"Minimum bit rate of {Data.Lame.MinBitRate} not valid.", Severity.Advisory, IssueTags.Substandard); } } if (Data.HasId3v1) { if (Data.HasId3v1Phantom) { IssueModel.Add("Has phantom ID3v1 tag block.", Severity.Warning, IssueTags.Fussy | IssueTags.HasId3v1, "Remove phantom ID3v1 tag block", RepairPhantomTag); } else { var minor = GetMinorOfV1(Data.id3v1Block); Severity sev = minor == 0? Severity.Warning : Severity.Noise; IssueModel.Add($"Has ID3v1.{minor} tags.", sev, IssueTags.HasId3v1); } } if (!Data.HasId3v2) { IssueModel.Add("Missing ID3v2 tags.", Severity.Trivia, IssueTags.Fussy | IssueTags.Substandard); } else { switch (Data.Id3v2Major) { case 2: IssueModel.Add("Has obsolete ID3v2.2 tags.", Severity.Warning, IssueTags.Fussy | IssueTags.Substandard); break; case 3: // Hunky dory! break; case 4: IssueModel.Add("Has jumped-the-shark ID3v2.4 tags.", Severity.Trivia); break; default: IssueModel.Add("Has ID3 tags of unknown version 2." + Data.Id3v2Major); break; } if (Data.Id3v2TagRepair != null) { IssueModel.Add($"ID3v2 tag size over by 1 (repair with {Data.Id3v2TagRepair}).", Severity.Warning, IssueTags.Fussy, "Patch EAC induced ID3v2 tag size error", RepairId3v2OffBy1); } } if (Data.HasApe) { IssueModel.Add("Has APE tags.", Severity.Trivia, IssueTags.HasApe); } if (Data.HasLyrics3) { IssueModel.Add("Has obsolete Lyrics3v2 block.", Severity.Advisory); } if (Data.DeadBytes != 0) { IssueModel.Add($"Dead space preceeds audio, size={Data.DeadBytes}", Severity.Warning, IssueTags.Substandard); } }
private void ValidateMp3s(Hashes fileHash) { Bind.mp3Models = new List <Mp3Format.Model>(); int id3v1Count = 0, id3v23Count = 0, id3v24Count = 0; foreach (FileInfo mp3Info in Bind.mp3Infos) { Owner.SetCurrentFile(mp3Info.Name, Granularity.Verbose); using (var ffs = new FileStream(mp3Info.FullName, FileMode.Open, FileAccess.Read)) { var buf = new byte[0x2C]; int got = ffs.Read(buf, 0, buf.Length); Mp3Format.Model mp3Model = Mp3Format.CreateModel(ffs, buf, mp3Info.FullName); if (mp3Model == null) { Bind.MaxTrackSeverity = Severity.Error; Owner.ReportLine("Doesn't seem to be a MP3.", Severity.Error, Bind.Signature != null); } else { Mp3Format mp3 = mp3Model.Data; mp3Model.CalcHashes(Hashes.Intrinsic | Owner.Data.HashFlags | fileHash, Owner.Data.ValidationFlags); if (mp3.IsBadHeader) { ++Owner.Data.Mp3Format.TotalHeaderErrors; } if (mp3.IsBadData) { ++Owner.Data.Mp3Format.TotalDataErrors; } if (mp3.Lame != null) { if (Bind.RipProfile == null) { Bind.RipProfile = mp3.Lame.Profile; } else if (Bind.RipProfile != mp3.Lame.Profile) { mp3Model.IssueModel.Add($"Profile {mp3.Lame.Profile} inconsistent with rip profile of {Bind.RipProfile}."); } } if (mp3.HasId3v1) { ++id3v1Count; } if (mp3.HasId3v2) { if (mp3.Id3v2Major == 3) { ++id3v23Count; } else if (mp3.Id3v2Major == 4) { ++id3v24Count; } } IssueTags tags = Owner.Data.IsFussy? IssueTags.Fussy : IssueTags.None; mp3Model.IssueModel.Escalate(IssueTags.HasApe, tags | IssueTags.Substandard | IssueTags.Overstandard); if (Bind.MaxTrackSeverity < mp3.Issues.MaxSeverity) { Bind.MaxTrackSeverity = mp3.Issues.MaxSeverity; } Bind.mp3Models.Add(mp3Model); Owner.ReportFormat(mp3, Bind.Signature != null); } ++Owner.Data.Mp3Format.TrueTotal; ++Owner.Data.TotalFiles; if (mp3Model != null) { mp3Model.ClearFile(); } if (Bind.MaxTrackSeverity >= Severity.Fatal) { return; } } } if (new string[] { "V2", "V0", "C320" }.Any(x => x == Bind.RipProfile)) { LogModel.IssueModel.Add($"All tracks profile {Bind.RipProfile}.", Severity.Noise); } else { LogModel.IssueModel.Add($"Profile {Bind.RipProfile} is substandard.", Severity.Error, IssueTags.Substandard); } if (id3v1Count > 0 && id3v1Count != Bind.mp3Infos.Length) { LogModel.IssueModel.Add("Tracks have incomplete ID3v1 tagging."); } if (id3v23Count > 0 && id3v24Count > 0) { LogModel.IssueModel.Add("Tracks inconsistently tagged both ID3v2.3 and ID3v2.4."); } }
private void ValidateDirectory() { Owner.SetCurrentDirectory(Bind.DirPath); try { Bind.logInfos = Bind.Dir.GetFiles("*.log").Where(lf => lf.Name.EndsWith(".log")).ToArray(); Bind.mp3Infos = Bind.Dir.GetFiles("*.mp3").Where(lf => lf.Name.EndsWith(".mp3")).ToArray(); Bind.digInfos = Bind.Dir.GetFiles("*.sha1x"); Bind.m3uInfos = Bind.Dir.GetFiles("*.m3u").Where(lf => lf.Name.EndsWith(".m3u")).ToArray(); Bind.m3u8Infos = Bind.Dir.GetFiles("*.m3u8"); if (!String.IsNullOrEmpty(Owner.Data.Bypass)) { Bind.bypassInfos = Bind.Dir.GetFiles("*" + Owner.Data.Bypass); } } catch (IOException ex) { Owner.ReportLine(ex.Message.Trim(null), Severity.Fatal); Bind.Status = Severity.Fatal; return; } if (Bind.bypassInfos != null && Bind.bypassInfos.Length > 0) { Owner.ReportLine($"Ignoring directory containing file ending with '{Owner.Data.Bypass}'.", Severity.Advisory); return; } if (Bind.logInfos.Length == 0) { if (Bind.mp3Infos.Length > 0) { ++Owner.Data.Mp3Format.TrueTotal; ++Owner.Data.TotalFiles; ++Owner.Data.TotalErrors; ++Owner.Data.LogFormat.TotalMissing; Owner.ReportLine("Found .mp3 file(s) without a .log file in same directory.", Severity.Error, Bind.Signature != null); Bind.Status = Severity.Error; } return; } if (Bind.logInfos.Length > 1) { Owner.Data.LogFormat.TrueTotal += Bind.logInfos.Length; Owner.Data.TotalFiles += Bind.logInfos.Length; Owner.Data.TotalErrors += Bind.logInfos.Length - 1; Owner.ReportLine("Directory has more than 1 .log file.", Severity.Error, Bind.Signature != null); Bind.Status = Severity.Error; return; } if (Bind.mp3Infos.Length == 0) { ++Owner.Data.LogFormat.TrueTotal; ++Owner.Data.TotalFiles; ++Owner.Data.TotalErrors; ++Owner.Data.Mp3Format.TotalMissing; Owner.ReportLine("Directory has .log file yet has no .mp3 files.", Severity.Error, Bind.Signature != null); Bind.Status = Severity.Error; return; } Array.Sort(Bind.mp3Infos, (f1, f2) => f1.Name.CompareTo(f2.Name)); Owner.Data.ExpectedFiles = 1 + Bind.mp3Infos.Length; Bind.LogName = Bind.logInfos[0].Name; if (Bind.digInfos.Length == 0) { Bind.WorkName = Path.GetFileNameWithoutExtension(Bind.LogName); if (Bind.LogTagEnabled) { if (Bind.WorkName.EndsWith("." + Bind.RipProfile)) { Bind.WorkName = Bind.WorkName.Substring(0, Bind.WorkName.Length - 1 - Bind.RipProfile.Length); } Bind.DigName = Bind.WorkName + "." + Bind.RipProfile; } Bind.DigName = ".LAME." + Bind.Signature + ".sha1x"; Bind.DigPath = Bind.DirPath + Bind.DigName; } else { ++Owner.Data.ExpectedFiles; var digRE = new Regex(@"(.+)\.LAME\.(.+)\.sha1x"); Bind.DigName = Bind.digInfos[0].Name; Bind.DigPath = Bind.digInfos[0].FullName; Owner.SetCurrentFile(Bind.DigName); MatchCollection digMat = digRE.Matches(Bind.DigName); if (digMat.Count != 1) { Bind.WorkName = Path.GetFileNameWithoutExtension(Bind.LogName); } else { Match m1 = digMat[0]; if (m1.Groups.Count != 3) { ++Owner.Data.LogFormat.TrueTotal; ++Owner.Data.TotalFiles; Owner.ReportLine("Too confused by digest name, bailing out.", Severity.Error, Bind.Signature != null); Bind.Status = Severity.Error; return; } else { Bind.WorkName = m1.Groups[1].ToString(); Bind.Ripper = m1.Groups[2].ToString(); } } } using (var logfs = new FileStream(Bind.logInfos[0].FullName, FileMode.Open, FileAccess.Read, FileShare.Read)) { var hdr = new byte[0x26]; int got = logfs.Read(hdr, 0, hdr.Length); LogModel = LogEacFormat.CreateModel(logfs, hdr, Bind.logInfos[0].FullName); } if (LogModel == null) { ++Owner.Data.LogFormat.TrueTotal; ++Owner.Data.TotalFiles; Owner.ReportLine("Invalid EAC log file or unknown layout.", Severity.Error, Bind.Signature != null); Bind.Status = Severity.Error; return; } LogModel.ClearFile(); Bind.Log = LogModel.Data; if (Bind.Ripper == null && (Owner.Data.HashFlags & Hashes.WebCheck) != 0) { LogModel.CalcHashWebCheck(); } if (Bind.mp3Infos.Length != Bind.Log.Tracks.Items.Count) { var sb = new StringBuilder("Directory has "); sb.Append(Bind.mp3Infos.Length); sb.Append(" MP3"); if (Bind.mp3Infos.Length != 1) { sb.Append("s"); } sb.Append(", EAC log has "); sb.Append(Bind.Log.Tracks.Items.Count); sb.Append(" track"); sb.Append(Bind.Log.Tracks.Items.Count == 1? "." : "s."); LogModel.TkIssue = LogModel.IssueModel.Add(sb.ToString(), Severity.Error, IssueTags.Failure); } IssueTags errEscalator = Owner.Data.ErrEscalator; if (Owner.Data.WillProve && Bind.Ripper == null) { errEscalator |= IssueTags.MissingHash; } LogModel.IssueModel.Escalate(Owner.Data.WarnEscalator, errEscalator); }
private int ParseArgs(string[] Args) { if (args.Length == 0 || args[0] == "/?" || args[0] == "/help" || args[0] == "-help") { ShowUsage(); return(1); } for (int an = 0; an < args.Length - 1; ++an) { bool argOk = false; if (args[an] == @"/R") { action = Interaction.PromptToRepair; argOk = true; } else if (args[an].StartsWith("/f:")) { filter = args[an].Substring(3); argOk = true; } else if (args[an].StartsWith("/g:")) { var arg = Granularity.Summary; argOk = Enum.TryParse <Granularity> (args[an].Substring(3), true, out arg); argOk = argOk && Enum.IsDefined(typeof(Granularity), arg); if (argOk) { scope = arg; } } else if (args[an].StartsWith("/h:")) { argOk = Enum.TryParse <Hashes> (args[an].Substring(3), true, out Hashes arg); argOk = argOk && arg == (arg & (Hashes.WebCheck - 1)); if (argOk) { hashes = arg; } } else if (args[an].StartsWith("/out:")) { mirrorName = args[an].Substring(5).Trim(null); argOk = mirrorName.Length > 0; } else if (args[an].StartsWith("/v:")) { var arg = Validations.None; argOk = Enum.TryParse <Validations> (args[an].Substring(3), true, out arg); if (argOk) { validations = arg; } } else if (args[an].StartsWith("/w:")) { var arg = IssueTags.None; argOk = Enum.TryParse <IssueTags> (args[an].Substring(3), true, out arg); if (argOk) { warnEscalator = arg; } } else if (args[an].StartsWith("/e:")) { var arg = IssueTags.None; argOk = Enum.TryParse <IssueTags> (args[an].Substring(3), true, out arg); if (argOk) { errEscalator = arg; } } else if (args[an].StartsWith("/p:")) { argOk = int.TryParse(args[an].Substring(3), out int arg); if (argOk) { notifyEvery = arg; } } else if (args[an].StartsWith("/x:")) { var arg = args[an].Substring(3); argOk = !String.IsNullOrWhiteSpace(arg); if (argOk) { exclusion = arg; } } else if (args[an] == "/k") { waitForKeyPress = true; argOk = true; } if (!argOk) { Console.Error.WriteLine("Invalid argument: " + args[an]); return(1); } } return(0); }
public Issue Add(string message, Severity baseLevel = Severity.Error, IssueTags tags = IssueTags.None, string prompt = null, Func <bool, string> repairer = null, bool isFinalRepairer = false) { System.Diagnostics.Debug.Assert((prompt == null) == (repairer == null)); if (repairer != null) { // Force Warning as minimum for repairables. if (baseLevel < Severity.Warning) { baseLevel = Severity.Warning; } ++Data.RepairableCount; } var issue = new Issue(Data, message, baseLevel, tags, prompt, repairer, isFinalRepairer); Data.items.Add(issue); Severity level = Data.GetLevel(issue.BaseLevel, issue.Tag); if (Data.MaxSeverity < level) { Data.MaxSeverity = level; } Data.RaisePropertyChanged(nameof(LongMessage)); return(issue); }
public Model(IssueTags warnEscalator = IssueTags.None, IssueTags errEscalator = IssueTags.None) => Data = new Issue.Vector(warnEscalator, errEscalator);
private Issue(Vector owner, string message, Severity level = Severity.Advisory, IssueTags tag = IssueTags.None, string prompt = null, Func <bool, string> repairer = null, bool isFinalRepairer = false) { this.vector = owner; this.Index = owner.Items.Count; this.Message = message; this.BaseLevel = level; this.Tag = tag; this.RepairPrompt = prompt; this.Repairer = repairer; this.IsFinalRepairer = isFinalRepairer; }
private Issue(Vector.Model owner, string message, Severity level = Severity.Advisory, IssueTags tag = IssueTags.None, string prompt = null, Func <bool, string> repairer = null, bool isFinalRepairer = false) { this.owner = owner; this.Index = owner.Data.Items.Count; this.Message = message; this.BaseLevel = level; this.Tag = tag; this.RepairPrompt = prompt; this.Repairer = repairer; this.IsFinalRepairer = isFinalRepairer; #if MVVM DoRepair = repairer == null ? null : new RelayCommand(() => owner.Repair(Index)); #endif }
protected override void GetDiagnostics() { base.GetDiagnostics(); string OkErr = TracksModel.GetOkDiagnostics(); if (OkErr != null) { Data.OkIssue = IssueModel.Add(OkErr, Severity.Error, IssueTags.Failure); } string QualErr = TracksModel.GetQualDiagnostics(); if (QualErr != null) { Data.QiIssue = IssueModel.Add(QualErr, Severity.Error, IssueTags.Failure); } if (String.IsNullOrEmpty(Data.RipArtist)) { IssueModel.Add("Missing artist", Severity.Warning, IssueTags.Substandard); } if (String.IsNullOrEmpty(Data.RipAlbum)) { IssueModel.Add("Missing album", Severity.Warning, IssueTags.Substandard); } if (String.IsNullOrEmpty(Data.Drive)) { IssueModel.Add("Missing 'Used drive'."); } if (String.IsNullOrEmpty(Data.ReadMode)) { IssueModel.Add("Missing 'Read mode'."); } else if (Data.ReadMode != "Secure with NO C2, accurate stream, disable cache" && Data.ReadMode != "Secure with NO C2, accurate stream, disable cache") { if (Data.ReadMode != "Secure") { Data.DsIssue = IssueModel.Add("Nonpreferred drive setting: Read mode: " + Data.ReadMode, Severity.Warning, IssueTags.Substandard); } if (Data.AccurateStream == null || Data.AccurateStream != "Yes") { Data.DsIssue = IssueModel.Add("Missing drive setting: 'Utilize accurate stream: Yes'." + Data.AccurateStream, Severity.Warning, IssueTags.StrictErr); } if (Data.DefeatCache == null || Data.DefeatCache != "Yes") { Data.DsIssue = IssueModel.Add("Missing drive setting: 'Defeat audio cache: Yes'.", Severity.Warning, IssueTags.StrictErr); } if (Data.UseC2 == null || Data.UseC2 != "No") { Data.DsIssue = IssueModel.Add("Missing drive setting: 'Make use of C2 pointers: No'.", Severity.Warning, IssueTags.Substandard); } } if (String.IsNullOrEmpty(Data.ReadOffset)) { IssueModel.Add("Missing 'Read offset correction'.", Severity.Trivia, IssueTags.StrictWarn); } if (Data.FillWithSilence != null && Data.FillWithSilence != "Yes") { IssueModel.Add("Missing 'Fill up missing offset samples with silence: Yes'.", Severity.Trivia, IssueTags.StrictWarn); } if (Data.Quality != null && Data.Quality != "High") { IssueModel.Add("Missing 'Quality: High'.", Severity.Advisory, IssueTags.Substandard); } if (Data.TrimSilence == null || Data.TrimSilence != "No") { Data.TsIssue = IssueModel.Add("Missing 'Delete leading and trailing silent blocks: No'.", Severity.Warning, IssueTags.StrictErr); } if (Data.CalcWithNulls != null && Data.CalcWithNulls != "Yes") { IssueModel.Add("Missing 'Null samples used in CRC calculations: Yes'."); } if (Data.GapHandling != null) { if (Data.GapHandling != "Appended to previous track") { IssueTags gapTag = IssueTags.StrictErr; if (Data.GapHandling != "Not detected, thus appended to previous track") { gapTag |= IssueTags.StrictErr; } Data.GpIssue = IssueModel.Add("Gap handling preferred setting is 'Appended to previous track'.", Severity.Advisory, gapTag); } } if (Data.Id3Tag == "Yes") { IssueModel.Add("Append ID3 tags preferred setting is 'No'.", Severity.NoIssue, IssueTags.StrictErr); } if (Data.ReadOffset == "0" && Data.Drive.Contains("not found in database")) { IssueModel.Add("Unknown drive with offset '0'.", Severity.Advisory, IssueTags.StrictErr); } if (Data.NormalizeTo != null) { Data.NzIssue = IssueModel.Add("Use of normalization considered harmful.", Severity.Warning, IssueTags.StrictErr); } if (Data.SampleFormat != null && Data.SampleFormat != "44.100 Hz; 16 Bit; Stereo") { IssueModel.Add("Missing 'Sample format: 44.100 Hz; 16 Bit; Stereo'.", Severity.Warning, IssueTags.Substandard); } if (Data.IsRangeRip) { IssueModel.Add("Range rip detected.", Severity.Advisory, IssueTags.StrictWarn); } else { if (Data.TocTrackCount != null) { int diff = Data.TocTrackCount.Value - Data.Tracks.Items.Count; if (diff != 0) { Severity sev = diff == 1? Severity.Advisory : Severity.Error; IssueModel.Add("Found " + Data.Tracks.Items.Count + " of " + Data.TocTrackCount.Value + " tracks.", sev); } } } var arTag = IssueTags.None; var arSev = Severity.Trivia; if (Data.AccurateRipConfidence != null) { if (Data.AccurateRipConfidence.Value > 0) { arTag = IssueTags.Success; } else { arSev = Severity.Advisory; if (Data.AccurateRipConfidence.Value < 0) { arTag = IssueTags.Failure; } } } Data.ArIssue = IssueModel.Add($"AccurateRip verification {Data.AccurateRipText}.", arSev, arTag); var ctSev = Severity.Trivia; var ctTag = IssueTags.None; if (Data.CueToolsConfidence == null) { ctTag = IssueTags.StrictErr; } else if (Data.CueToolsConfidence.Value < 0) { ctSev = Severity.Error; } else if (Data.CueToolsConfidence.Value == 0) { ctSev = Severity.Advisory; } else { ctTag = IssueTags.Success; } Data.CtIssue = IssueModel.Add($"CUETools DB verification {Data.CueToolsText}.", ctSev, ctTag); }