public override IEnumerable <Issue> GetIssues(BeatmapSet beatmapSet)
        {
            foreach (string hsFile in beatmapSet.hitSoundFiles)
            {
                string hsPath = Path.Combine(beatmapSet.songPath, hsFile);

                double    duration  = 0;
                Exception exception = null;
                try
                { duration = AudioBASS.GetDuration(hsPath); }
                catch (Exception ex)
                { exception = ex; }

                if (exception == null)
                {
                    // Greater than 0 since 44-byte muted hit sounds are fine.
                    if (duration < 25 && duration > 0)
                    {
                        yield return(new Issue(GetTemplate("Length"), null,
                                               hsFile, $"{duration:0.##}"));
                    }
                }
                else
                {
                    yield return(new Issue(GetTemplate("Unable to check"), null,
                                           hsFile, Common.ExceptionTag(exception)));
                }
            }
        }
Esempio n. 2
0
        public override IEnumerable <Issue> GetIssues(BeatmapSet beatmapSet)
        {
            if (beatmapSet.GetAudioFilePath() != null)
            {
                foreach (Issue issue in GetIssue(beatmapSet, beatmapSet.GetAudioFilePath()))
                {
                    yield return(issue);
                }
            }

            foreach (string hitSoundFile in beatmapSet.hitSoundFiles)
            {
                string hitSoundPath = Path.Combine(beatmapSet.songPath, hitSoundFile);
                ManagedBass.ChannelType hitSoundFormat = AudioBASS.GetFormat(hitSoundPath);
                if ((hitSoundFormat & ManagedBass.ChannelType.OGG) != 0 &&
                    (hitSoundFormat & ManagedBass.ChannelType.MP3) != 0)
                {
                    continue;
                }

                foreach (Issue issue in GetIssue(beatmapSet, hitSoundPath, true))
                {
                    yield return(issue);
                }
            }
        }
Esempio n. 3
0
        public IEnumerable <Issue> GetIssue(BeatmapSet beatmapSet, string audioPath, bool isHitSound = false)
        {
            // `Audio.GetBitrate` has a < 0.1 kbps error margin, so we should round this.
            double bitrate = Math.Round(AudioBASS.GetBitrate(audioPath));

            // Hit sounds only need to follow the lower limit for quality requirements, as Wave
            // (which is the most used hit sound format currently) is otherwise uncompressed anyway.
            if (bitrate >= 128 && (bitrate <= 192 || isHitSound))
            {
                yield break;
            }

            string audioRelPath = PathStatic.RelativePath(audioPath, beatmapSet.songPath);

            if (!isHitSound)
            {
                yield return(new Issue(GetTemplate("Bitrate"), null,
                                       audioRelPath, $"{bitrate:0.##}",
                                       (bitrate < 128 ? "low" : "high")));
            }
            else
            {
                yield return(new Issue(GetTemplate("Hit Sound"), null,
                                       audioRelPath, $"{bitrate:0.##}"));
            }
        }
Esempio n. 4
0
        public override IEnumerable <Issue> GetIssues(BeatmapSet beatmapSet)
        {
            if (beatmapSet.hitSoundFiles == null)
            {
                yield break;
            }

            foreach (string hitSoundFile in beatmapSet.hitSoundFiles)
            {
                string fullPath = Path.Combine(beatmapSet.songPath, hitSoundFile);

                ManagedBass.ChannelType actualFormat = 0;
                Exception exception = null;
                try
                {
                    actualFormat = AudioBASS.GetFormat(fullPath);
                }
                catch (Exception ex)
                {
                    exception = ex;
                }

                if (exception != null)
                {
                    yield return(new Issue(GetTemplate("Exception"), null,
                                           hitSoundFile, Common.ExceptionTag(exception)));

                    continue;
                }

                // The .mp3 format includes inherent delays and are as such not fit for active hit sounding.
                if (actualFormat == ManagedBass.ChannelType.MP3)
                {
                    HitObject hitObjectActiveAt = GetHitObjectActiveAt(beatmapSet, hitSoundFile);
                    if (hitObjectActiveAt != null)
                    {
                        yield return(new Issue(GetTemplate("mp3"), null,
                                               hitSoundFile, Timestamp.Get(hitObjectActiveAt), hitObjectActiveAt.beatmap));
                    }
                }
                else
                {
                    if ((ManagedBass.ChannelType.Wave & actualFormat) == 0 &&
                        (ManagedBass.ChannelType.OGG & actualFormat) == 0)
                    {
                        yield return(new Issue(GetTemplate("Unexpected Format"), null,
                                               hitSoundFile, AudioBASS.EnumToString(actualFormat)));
                    }
                    else if (!hitSoundFile.ToLower().EndsWith(".wav") && !hitSoundFile.ToLower().EndsWith(".ogg"))
                    {
                        yield return(new Issue(GetTemplate("Incorrect Extension"), null,
                                               hitSoundFile, AudioBASS.EnumToString(actualFormat)));
                    }
                }
            }
        }
Esempio n. 5
0
        public override IEnumerable <Issue> GetIssues(BeatmapSet beatmapSet)
        {
            string audioPath = beatmapSet.GetAudioFilePath();
            string audioName = beatmapSet.GetAudioFileName();

            if (audioPath == null)
            {
                yield break;
            }

            ManagedBass.ChannelType actualFormat = 0;
            Exception exception = null;

            try
            {
                actualFormat = AudioBASS.GetFormat(audioPath);
            }
            catch (Exception ex)
            {
                exception = ex;
            }

            if (exception != null)
            {
                yield return(new Issue(GetTemplate("Exception"), null,
                                       audioName, Common.ExceptionTag(exception)));
            }

            else if ((ManagedBass.ChannelType.MP3 & actualFormat) != ManagedBass.ChannelType.MP3 &&
                     (ManagedBass.ChannelType.OGG & actualFormat) != ManagedBass.ChannelType.OGG)
            {
                yield return(new Issue(GetTemplate("Incorrect Format"), null,
                                       audioName, AudioBASS.EnumToString(actualFormat)));
            }

            else if (!audioName.ToLower().EndsWith(".mp3") && (ManagedBass.ChannelType.MP3 & actualFormat) == ManagedBass.ChannelType.MP3)
            {
                yield return(new Issue(GetTemplate("Incorrect Extension"), null,
                                       audioName, AudioBASS.EnumToString(actualFormat), ".mp3"));
            }

            else if (!audioName.ToLower().EndsWith(".ogg") && (ManagedBass.ChannelType.OGG & actualFormat) == ManagedBass.ChannelType.OGG)
            {
                yield return(new Issue(GetTemplate("Incorrect Extension"), null,
                                       audioName, AudioBASS.EnumToString(actualFormat), ".ogg"));
            }
        }
Esempio n. 6
0
        public override IEnumerable <Issue> GetIssues(BeatmapSet beatmapSet)
        {
            if (beatmapSet.GetAudioFilePath() != null)
            {
                foreach (var issue in GetIssue(beatmapSet, beatmapSet.GetAudioFilePath()))
                {
                    yield return(issue);
                }
            }

            foreach (string hitSoundFile in beatmapSet.hitSoundFiles)
            {
                string hitSoundPath = Path.Combine(beatmapSet.songPath, hitSoundFile);

                ManagedBass.ChannelType hitSoundFormat = 0;
                Issue errorIssue = null;
                try
                {
                    hitSoundFormat = AudioBASS.GetFormat(hitSoundPath);
                }
                catch (Exception exception)
                {
                    errorIssue = new Issue(GetTemplate("Exception"), null,
                                           PathStatic.RelativePath(hitSoundPath, beatmapSet.songPath),
                                           Common.ExceptionTag(exception));
                }

                if (errorIssue != null)
                {
                    yield return(errorIssue);

                    continue;
                }

                if ((hitSoundFormat & ManagedBass.ChannelType.OGG) != 0 &&
                    (hitSoundFormat & ManagedBass.ChannelType.MP3) != 0)
                {
                    continue;
                }

                foreach (var issue in GetIssue(beatmapSet, hitSoundPath, true))
                {
                    yield return(issue);
                }
            }
        }
Esempio n. 7
0
        public override IEnumerable <Issue> GetIssues(BeatmapSet beatmapSet)
        {
            foreach (string hsFile in beatmapSet.hitSoundFiles)
            {
                // Only hit sounds on active hit objects need to be delay-free.
                bool isActive = IsActive(beatmapSet, hsFile);
                if (!isActive)
                {
                    continue;
                }

                string hsPath = Path.Combine(beatmapSet.songPath, hsFile);

                List <float[]> peaks     = null;
                Exception      exception = null;
                try
                { peaks = AudioBASS.GetPeaks(hsPath); }
                catch (Exception ex)
                { exception = ex; }

                if (exception == null)
                {
                    // Ignore muted files since they don't have anything to be delayed.
                    if (peaks?.Count > 0 && peaks.Sum(peak => peak.Sum()) > 0)
                    {
                        double maxStrength = peaks.Select(value => Math.Abs(value.Sum())).Max();

                        int    delay     = 0;
                        int    pureDelay = 0;
                        double strength  = 0;
                        while (delay + pureDelay < peaks.Count)
                        {
                            strength += Math.Abs(peaks[delay].Sum());

                            if (strength >= maxStrength / 2)
                            {
                                break;
                            }

                            strength *= 0.95;

                            // The delay added by MP3 encoding still has very slight volume where it's basically silent.
                            if (strength < 0.001)
                            {
                                strength = 0;
                                ++pureDelay;
                                ++delay;
                            }
                            else
                            {
                                ++delay;
                            }
                        }

                        if (pureDelay >= 5)
                        {
                            yield return(new Issue(GetTemplate("Pure Delay"), null,
                                                   hsFile, $"{pureDelay:0.##}"));
                        }

                        else if (delay + pureDelay >= 5)
                        {
                            yield return(new Issue(GetTemplate("Delay"), null,
                                                   hsFile, $"{pureDelay:0.##}", $"{delay:0.##}"));
                        }

                        else if (delay + pureDelay >= 1)
                        {
                            yield return(new Issue(GetTemplate("Minor Delay"), null,
                                                   hsFile, $"{pureDelay:0.##}", $"{delay:0.##}"));
                        }
                    }
                }
                else
                {
                    yield return(new Issue(GetTemplate("Unable to check"), null,
                                           hsFile, Common.ExceptionTag(exception)));
                }
            }
        }
Esempio n. 8
0
        public override IEnumerable <Issue> GetIssues(BeatmapSet beatmapSet)
        {
            foreach (string hsFile in beatmapSet.hitSoundFiles)
            {
                string hsPath = Path.Combine(beatmapSet.songPath, hsFile);

                int            channels  = 0;
                List <float[]> peaks     = null;
                Exception      exception = null;
                try
                {
                    channels = AudioBASS.GetChannels(hsPath);
                    peaks    = AudioBASS.GetPeaks(hsPath);
                }
                catch (Exception ex)
                {
                    exception = ex;
                }

                // Cannot yield in catch statements, hence externally handled.
                if (exception != null)
                {
                    yield return(new Issue(GetTemplate("Unable to check"), null,
                                           hsFile, Common.ExceptionTag(exception)));

                    continue;
                }

                // Mono cannot be imbalanced; same audio on both sides.
                if (channels < 2)
                {
                    continue;
                }

                // Silent audio cannot be imbalanced.
                if (peaks.Count == 0)
                {
                    continue;
                }

                float leftSum  = peaks.Sum(peak => peak?[0] ?? 0);
                float rightSum = peaks.Sum(peak => peak.Length > 1 ? peak[1] : 0);
                if (leftSum == 0 || rightSum == 0)
                {
                    yield return(new Issue(GetTemplate("Warning Silent"), null,
                                           hsFile, leftSum - rightSum > 0 ? "left" : "right"));

                    continue;
                }

                // 2 would mean one is double the sum of the other.
                float relativeVolume =
                    leftSum > rightSum ?
                    leftSum / rightSum :
                    rightSum / leftSum;

                if (relativeVolume < 2)
                {
                    continue;
                }

                // Imbalance is only an issue if it is used frequently in a short timespan or it's overall common.
                Common.CollectHitSoundFrequency(beatmapSet, hsFile, scoreThreshold: 14 / relativeVolume,
                                                out string mostFrequentTimestamp, out Dictionary <Beatmap, int> uses);

                if (mostFrequentTimestamp != null)
                {
                    yield return(new Issue(GetTemplate("Warning Timestamp"), null,
                                           hsFile, leftSum - rightSum > 0 ? "left" : "right", mostFrequentTimestamp));
                }
                else
                {
                    Beatmap mapCommonlyUsedIn =
                        Common.GetBeatmapCommonlyUsedIn(beatmapSet, uses, commonUsageThreshold: 10000);

                    if (mapCommonlyUsedIn != null)
                    {
                        yield return(new Issue(GetTemplate("Warning Common"), null,
                                               hsFile, leftSum - rightSum > 0 ? "left" : "right", mapCommonlyUsedIn));
                    }
                    else
                    {
                        yield return(new Issue(GetTemplate("Minor"), null,
                                               hsFile, leftSum - rightSum > 0 ? "left" : "right"));
                    }
                }
            }
        }
        public override IEnumerable <Issue> GetIssues(BeatmapSet beatmapSet)
        {
            Dictionary <string, List <AudioUsage> > audioUsage = new Dictionary <string, List <AudioUsage> >();

            foreach (Beatmap beatmap in beatmapSet.beatmaps)
            {
                bool hasVideo      = beatmap.videos.Count > 0;
                bool hasStoryboard =
                    beatmap.HasDifficultySpecificStoryboard() ||
                    (beatmapSet.osb?.IsUsed() ?? false);

                string audioPath = beatmap.GetAudioFilePath();
                if (audioPath == null)
                {
                    continue;
                }

                double    duration  = 0;
                Exception exception = null;
                try
                { duration = AudioBASS.GetDuration(audioPath); }
                catch (Exception ex)
                { exception = ex; }

                if (exception != null)
                {
                    yield return(new Issue(GetTemplate("Unable to check"), null,
                                           PathStatic.RelativePath(audioPath, beatmap.songPath), Common.ExceptionTag(exception)));

                    continue;
                }

                double lastEndTime = beatmap.hitObjects.LastOrDefault()?.GetEndTime() ?? 0;
                double fraction    = lastEndTime / duration;

                if (!audioUsage.ContainsKey(audioPath))
                {
                    audioUsage[audioPath] = new List <AudioUsage>();
                }
                audioUsage[audioPath].Add(new AudioUsage(fraction, hasVideo || hasStoryboard));
            }

            foreach (string audioPath in audioUsage.Keys)
            {
                double maxFraction     = 0;
                bool   anyHasVideoOrSB = false;
                foreach (AudioUsage usage in audioUsage[audioPath])
                {
                    if (usage.fraction > maxFraction)
                    {
                        maxFraction = usage.fraction;
                    }
                    if (usage.hasVideoOrSB)
                    {
                        anyHasVideoOrSB = true;
                    }
                }

                if (maxFraction > 0.8d)
                {
                    continue;
                }

                string templateKey = (anyHasVideoOrSB ? "With" : "Without") + " Video/Storyboard";

                yield return(new Issue(GetTemplate(templateKey), null, $"{(1 - maxFraction) * 100:0.##}"));
            }
        }
Esempio n. 10
0
        public override IEnumerable <Issue> GetIssues(BeatmapSet beatmapSet)
        {
            foreach (string hsFile in beatmapSet.hitSoundFiles)
            {
                HitObject hitObjectActiveAt = GetHitObjectActiveAt(beatmapSet, hsFile);
                if (hitObjectActiveAt == null)
                {
                    // Hit sound is never active, so delay does not matter.
                    continue;
                }

                string hsPath = Path.Combine(beatmapSet.songPath, hsFile);

                List <float[]> peaks     = null;
                Exception      exception = null;
                try
                {
                    peaks = AudioBASS.GetPeaks(hsPath);
                }
                catch (Exception ex)
                {
                    exception = ex;
                }

                if (exception == null)
                {
                    if (!(peaks?.Count > 0) || !(peaks.Sum(peak => peak.Sum()) > 0))
                    {
                        // Muted files don't have anything to be delayed, hence ignore.
                        continue;
                    }

                    double maxStrength = peaks.Select(value => Math.Abs(value.Sum())).Max();

                    int    delay     = 0;
                    int    pureDelay = 0;
                    double strength  = 0;
                    while (delay + pureDelay < peaks.Count)
                    {
                        strength += Math.Abs(peaks[delay].Sum());

                        if (strength >= maxStrength / 2)
                        {
                            break;
                        }

                        strength *= 0.95;

                        // The delay added by MP3 encoding still has very slight volume where it's basically silent.
                        if (strength < 0.001)
                        {
                            strength = 0;
                            ++pureDelay;
                            ++delay;
                        }
                        else
                        {
                            ++delay;
                        }
                    }

                    if (pureDelay >= 5)
                    {
                        yield return(new Issue(GetTemplate("Pure Delay"), null,
                                               hsFile, $"{pureDelay:0.##}"));
                    }

                    else if (delay + pureDelay >= 5)
                    {
                        yield return(new Issue(GetTemplate("Delay"), null,
                                               hsFile, $"{pureDelay:0.##}", $"{delay:0.##}",
                                               Timestamp.Get(hitObjectActiveAt), hitObjectActiveAt.beatmap));
                    }

                    else if (delay + pureDelay >= 1)
                    {
                        yield return(new Issue(GetTemplate("Minor Delay"), null,
                                               hsFile, $"{pureDelay:0.##}", $"{delay:0.##}"));
                    }
                }
                else
                {
                    yield return(new Issue(GetTemplate("Unable to check"), null,
                                           hsFile, Common.ExceptionTag(exception)));
                }
            }
        }
Esempio n. 11
0
        public override IEnumerable <Issue> GetIssues(BeatmapSet beatmapSet)
        {
            if (beatmapSet.hitSoundFiles != null)
            {
                foreach (string hitSoundFile in beatmapSet.hitSoundFiles)
                {
                    string fullPath = Path.Combine(beatmapSet.songPath, hitSoundFile);

                    ManagedBass.ChannelType actualFormat = 0;
                    Exception exception = null;
                    try
                    { actualFormat = AudioBASS.GetFormat(fullPath); }
                    catch (Exception ex)
                    { exception = ex; }

                    if (exception != null)
                    {
                        yield return(new Issue(GetTemplate("Exception"), null,
                                               hitSoundFile, Common.ExceptionTag(exception)));

                        continue;
                    }

                    // The .mp3 format includes inherent delays and are as such not fit for active hit sounding.
                    if (actualFormat == ManagedBass.ChannelType.MP3)
                    {
                        bool foundActiveMp3 = false;
                        foreach (Beatmap beatmap in beatmapSet.beatmaps)
                        {
                            foreach (HitObject hitObject in beatmap.hitObjects)
                            {
                                if (hitObject is Spinner)
                                {
                                    continue;
                                }

                                // Only the hit sound edge at which the object is clicked is considered active.
                                if (hitObject.usedHitSamples.Any(sample =>
                                                                 sample.time == hitObject.time &&
                                                                 sample.hitSource == HitSample.HitSource.Edge &&
                                                                 sample.SameFileName(hitSoundFile)))
                                {
                                    yield return(new Issue(GetTemplate("mp3"), null,
                                                           hitSoundFile, Timestamp.Get(hitObject), beatmap));

                                    foundActiveMp3 = true;
                                    break;
                                }
                            }
                            if (foundActiveMp3)
                            {
                                break;
                            }
                        }
                    }
                    else
                    {
                        if ((ManagedBass.ChannelType.Wave & actualFormat) == 0 &&
                            (ManagedBass.ChannelType.OGG & actualFormat) == 0)
                        {
                            yield return(new Issue(GetTemplate("Unexpected Format"), null,
                                                   hitSoundFile, AudioBASS.EnumToString(actualFormat)));
                        }
                        else if (!hitSoundFile.ToLower().EndsWith(".wav") && !hitSoundFile.ToLower().EndsWith(".ogg"))
                        {
                            yield return(new Issue(GetTemplate("Incorrect Extension"), null,
                                                   hitSoundFile, AudioBASS.EnumToString(actualFormat)));
                        }
                    }
                }
            }
        }
Esempio n. 12
0
        private static string RenderResources(BeatmapSet aBeatmapSet)
        {
            string RenderFloat(List <string> aFiles, Func <string, string> aFunc)
            {
                string content =
                    String.Join("<br>",
                                aFiles.Select(aFile =>
                {
                    string path =
                        aBeatmapSet.hitSoundFiles.FirstOrDefault(anOtherFile =>
                                                                 anOtherFile.StartsWith(aFile + "."));
                    if (path == null)
                    {
                        return(null);
                    }

                    return(aFunc(path));
                }).Where(aValue => aValue != null)
                                );

                if (content.Length == 0)
                {
                    return("");
                }

                return(Div("overview-float", content));
            }

            Dictionary <string, int> hsUsedCount = new Dictionary <string, int>();

            return
                (RenderContainer("Resources",
                                 RenderBeatmapContent(aBeatmapSet, "Used Hit Sound File(s)", aBeatmap =>
            {
                List <string> usedHitSoundFiles =
                    aBeatmap.hitObjects.SelectMany(anObject => anObject.GetUsedHitSoundFileNames()).ToList();

                List <string> distinctSortedFiles =
                    usedHitSoundFiles.Distinct().OrderByDescending(aFile => aFile).ToList();

                return
                RenderFloat(distinctSortedFiles, aPath => Encode(aPath)) +
                RenderFloat(distinctSortedFiles, aPath =>
                {
                    int count = usedHitSoundFiles.Where(anOtherFile => aPath.StartsWith(anOtherFile + ".")).Count();

                    // Used for total hit sound usage overview
                    if (hsUsedCount.ContainsKey(aPath))
                    {
                        hsUsedCount[aPath] += count;
                    }
                    else
                    {
                        hsUsedCount[aPath] = count;
                    }

                    return $"× {count}";
                });
            }, false),
                                 RenderField("Total Used Hit Sound File(s)",
                                             (hsUsedCount.Any() ?
                                              Div("overview-float",
                                                  String.Join("<br>",
                                                              hsUsedCount.Select(aPair => aPair.Key)
                                                              )
                                                  ) +
                                              Div("overview-float",
                                                  String.Join("<br>",
                                                              hsUsedCount.Select(aPair =>
                                                                                 Try(() =>
            {
                string fullPath = Path.Combine(aBeatmapSet.songPath, aPair.Key);

                return Encode(RenderFileSize(fullPath));
            },
                                                                                     noteIfError: "Could not get hit sound file size"
                                                                                     )
                                                                                 )
                                                              )
                                                  ) +
                                              Div("overview-float",
                                                  String.Join("<br>",
                                                              hsUsedCount.Select(aPair =>
                                                                                 Try(() =>
            {
                string fullPath = Path.Combine(aBeatmapSet.songPath, aPair.Key);
                double duration = AudioBASS.GetDuration(fullPath);

                if (duration < 0)
                {
                    return "0 ms";
                }

                return $"{duration:0.##} ms";
            },
                                                                                     noteIfError: "Could not get hit sound duration"
                                                                                     )
                                                                                 )
                                                              )
                                                  ) +
                                              Div("overview-float",
                                                  String.Join("<br>",
                                                              hsUsedCount.Select(aPair =>
                                                                                 Try(() =>
            {
                string fullPath = Path.Combine(aBeatmapSet.songPath, aPair.Key);

                return Encode(AudioBASS.EnumToString(AudioBASS.GetFormat(fullPath)));
            },
                                                                                     noteIfError: "Could not get hit sound file path"
                                                                                     )
                                                                                 )
                                                              )
                                                  ) +
                                              Div("overview-float",
                                                  String.Join("<br>",
                                                              hsUsedCount.Select(aPair => "× " + aPair.Value)
                                                              )
                                                  )
                        : "")
                                             ),
                                 RenderBeatmapContent(aBeatmapSet, "Background File(s)", aBeatmap =>
            {
                if (aBeatmap.backgrounds.Any())
                {
                    string fullPath = Path.Combine(aBeatmap.songPath, aBeatmap.backgrounds.First().path);
                    if (!File.Exists(fullPath))
                    {
                        return "";
                    }

                    string error = null;
                    TagLib.File tagFile = null;
                    try
                    { tagFile = new FileAbstraction(fullPath).GetTagFile(); }
                    catch (Exception exception)
                    {
                        error = exception.Message;
                    }

                    return
                    Div("overview-float",
                        Try(() =>
                            Encode(aBeatmap.backgrounds.First().path),
                            noteIfError: "Could not get background file path"
                            )
                        ) +
                    Div("overview-float",
                        Try(() =>
                            Encode(RenderFileSize(fullPath)),
                            noteIfError: "Could not get background file size"
                            )
                        ) +
                    ((error != null || tagFile == null) ?
                     Div("overview-float",
                         Try(() =>
                             Encode(tagFile.Properties.PhotoWidth + " x " + tagFile.Properties.PhotoHeight),
                             noteIfError: "Could not get background resolution"
                             )
                         ) :
                     Div("overview-float",
                         Encode($"(failed getting proprties; {error})")
                         ));
                }
                else
                {
                    return "";
                }
            }, false),
                                 RenderBeatmapContent(aBeatmapSet, "Video File(s)", aBeatmap =>
            {
                if (aBeatmap.videos.Any() || (aBeatmapSet.osb?.videos.Any() ?? false))
                {
                    string fullPath = Path.Combine(aBeatmap.songPath, aBeatmap.videos.First().path);
                    if (!File.Exists(fullPath))
                    {
                        return "";
                    }

                    string error = null;
                    TagLib.File tagFile = null;
                    try
                    { tagFile = new FileAbstraction(fullPath).GetTagFile(); }
                    catch (Exception exception)
                    { error = exception.Message; }

                    return
                    Div("overview-float",
                        Try(() =>
                            Encode(aBeatmap.videos.First().path),
                            noteIfError: "Could not get video file path"
                            )
                        ) +
                    Div("overview-float",
                        Try(() =>
                            Encode(RenderFileSize(fullPath)),
                            noteIfError: "Could not get video file size"
                            )
                        ) +
                    ((error != null || tagFile == null) ?
                     Div("overview-float",
                         Try(() =>
                             FormatTimestamps(Encode(Timestamp.Get(tagFile.Properties.Duration.TotalMilliseconds))),
                             noteIfError: "Could not get video duration"
                             )
                         ) +
                     Div("overview-float",
                         Try(() =>
                             Encode(tagFile.Properties.VideoWidth + " x " + tagFile.Properties.VideoHeight),
                             noteIfError: "Could not get video resolution"
                             )
                         ) :
                     Div("overview-float",
                         Encode($"(failed getting proprties; {error})")
                         ));
                }
                else
                {
                    return "";
                }
            }, false),
                                 RenderBeatmapContent(aBeatmapSet, "Audio File(s)", aBeatmap =>
            {
                string path = aBeatmap.GetAudioFilePath();
                if (path == null)
                {
                    return "";
                }

                return
                Div("overview-float",
                    Try(() =>
                        Encode(PathStatic.RelativePath(path, aBeatmap.songPath)),
                        noteIfError: "Could not get audio file path"
                        )
                    ) +
                Div("overview-float",
                    Try(() =>
                        Encode(RenderFileSize(path)),
                        noteIfError: "Could not get audio file size"
                        )
                    ) +
                Div("overview-float",
                    Try(() =>
                        FormatTimestamps(Encode(Timestamp.Get(AudioBASS.GetDuration(path)))),
                        noteIfError: "Could not get audio duration"
                        )
                    ) +
                Div("overview-float",
                    Try(() =>
                        Encode(AudioBASS.EnumToString(AudioBASS.GetFormat(path))),
                        noteIfError: "Could not get audio format"
                        )
                    );
            }, false),
                                 RenderBeatmapContent(aBeatmapSet, "Audio Bitrate", aBeatmap =>
            {
                string path = aBeatmap.GetAudioFilePath();
                if (path == null)
                {
                    return "N/A";
                }

                return
                Div("overview-float",
                    $"average {Math.Round(AudioBASS.GetBitrate(path))} kbps"
                    );
            }, false),
                                 RenderField("Has .osb",
                                             Encode((aBeatmapSet.osb?.IsUsed() ?? false).ToString())
                                             ),
                                 RenderBeatmapContent(aBeatmapSet, "Has .osu Specific Storyboard", aBeatmap =>
                                                      aBeatmap.HasDifficultySpecificStoryboard().ToString()),
                                 RenderBeatmapContent(aBeatmapSet, "Song Folder Size", aBeatmap =>
                                                      RenderDirectorySize(aBeatmap.songPath))
                                 ));
        }