diff --git a/TwitchDownloaderCLI/Modes/DownloadChat.cs b/TwitchDownloaderCLI/Modes/DownloadChat.cs index 152cc780..07a8ba45 100644 --- a/TwitchDownloaderCLI/Modes/DownloadChat.cs +++ b/TwitchDownloaderCLI/Modes/DownloadChat.cs @@ -31,7 +31,7 @@ private static ChatDownloadOptions GetDownloadOptions(ChatDownloadArgs inputOpti Environment.Exit(1); } - var vodClipIdMatch = TwitchRegex.MatchVideoOrClipId(inputOptions.Id); + var vodClipIdMatch = IdParse.MatchVideoOrClipId(inputOptions.Id); if (vodClipIdMatch is not { Success: true }) { logger.LogError("Unable to parse Vod/Clip ID/URL."); diff --git a/TwitchDownloaderCLI/Modes/DownloadClip.cs b/TwitchDownloaderCLI/Modes/DownloadClip.cs index 24c44c26..a3e137a4 100644 --- a/TwitchDownloaderCLI/Modes/DownloadClip.cs +++ b/TwitchDownloaderCLI/Modes/DownloadClip.cs @@ -36,7 +36,7 @@ private static ClipDownloadOptions GetDownloadOptions(ClipDownloadArgs inputOpti Environment.Exit(1); } - var clipIdMatch = TwitchRegex.MatchClipId(inputOptions.Id); + var clipIdMatch = IdParse.MatchClipId(inputOptions.Id); if (clipIdMatch is not { Success: true }) { logger.LogError("Unable to parse Clip ID/URL."); diff --git a/TwitchDownloaderCLI/Modes/DownloadVideo.cs b/TwitchDownloaderCLI/Modes/DownloadVideo.cs index 4ab857b8..fe0043df 100644 --- a/TwitchDownloaderCLI/Modes/DownloadVideo.cs +++ b/TwitchDownloaderCLI/Modes/DownloadVideo.cs @@ -34,7 +34,7 @@ private static VideoDownloadOptions GetDownloadOptions(VideoDownloadArgs inputOp Environment.Exit(1); } - var vodIdMatch = TwitchRegex.MatchVideoId(inputOptions.Id); + var vodIdMatch = IdParse.MatchVideoId(inputOptions.Id); if (vodIdMatch is not { Success: true }) { logger.LogError("Unable to parse Vod ID/URL."); diff --git a/TwitchDownloaderCore.Tests/ToolTests/TwitchRegexTests.cs b/TwitchDownloaderCore.Tests/ToolTests/IdParseTests.cs similarity index 91% rename from TwitchDownloaderCore.Tests/ToolTests/TwitchRegexTests.cs rename to TwitchDownloaderCore.Tests/ToolTests/IdParseTests.cs index 11ef3f7a..713ff682 100644 --- a/TwitchDownloaderCore.Tests/ToolTests/TwitchRegexTests.cs +++ b/TwitchDownloaderCore.Tests/ToolTests/IdParseTests.cs @@ -3,7 +3,7 @@ namespace TwitchDownloaderCore.Tests.ToolTests { // ReSharper disable StringLiteralTypo - public class TwitchRegexTests + public class IdParseTests { [Theory] [InlineData("41546181")] // Oldest VODs - 8 @@ -12,7 +12,7 @@ public class TwitchRegexTests [InlineData("11987163407")] // Future VODs - 11 public void CorrectlyParsesVodId(string id) { - var match = TwitchRegex.MatchVideoId(id); + var match = IdParse.MatchVideoId(id); Assert.NotNull(match); Assert.Equal(id, match.Value); @@ -28,7 +28,7 @@ public void CorrectlyParsesVodId(string id) [InlineData("https://www.twitch.tv/videos/4894164023/", "4894164023")] public void CorrectlyParsesVodLink(string link, string expectedId) { - var match = TwitchRegex.MatchVideoId(link); + var match = IdParse.MatchVideoId(link); Assert.NotNull(match); Assert.Equal(expectedId, match.Value); @@ -39,7 +39,7 @@ public void CorrectlyParsesVodLink(string link, string expectedId) [InlineData("FuriousFlaccidTireArgieB8-NHbTiYQlzwHVvv_Vf")] public void CorrectlyParsesClipId(string id) { - var match = TwitchRegex.MatchClipId(id); + var match = IdParse.MatchClipId(id); Assert.NotNull(match); Assert.Equal(id, match.Value); @@ -56,7 +56,7 @@ public void CorrectlyParsesClipId(string id) [InlineData("https://clips.twitch.tv/FuriousFlaccidTireArgieB8-NHbTiYQlzwHVvv_Vf/", "FuriousFlaccidTireArgieB8-NHbTiYQlzwHVvv_Vf")] public void CorrectlyParsesClipLink(string link, string expectedId) { - var match = TwitchRegex.MatchClipId(link); + var match = IdParse.MatchClipId(link); Assert.NotNull(match); Assert.Equal(expectedId, match.Value); @@ -71,7 +71,7 @@ public void CorrectlyParsesClipLink(string link, string expectedId) [InlineData("FuriousFlaccidTireArgieB8-NHbTiYQlzwHVvv_Vf")] public void CorrectlyParsesVodOrClipId(string id) { - var match = TwitchRegex.MatchVideoOrClipId(id); + var match = IdParse.MatchVideoOrClipId(id); Assert.NotNull(match); Assert.Equal(id, match.Value); @@ -95,7 +95,7 @@ public void CorrectlyParsesVodOrClipId(string id) [InlineData("https://clips.twitch.tv/FuriousFlaccidTireArgieB8-NHbTiYQlzwHVvv_Vf/", "FuriousFlaccidTireArgieB8-NHbTiYQlzwHVvv_Vf")] public void CorrectlyParsesVodOrClipLink(string link, string expectedId) { - var match = TwitchRegex.MatchVideoOrClipId(link); + var match = IdParse.MatchVideoOrClipId(link); Assert.NotNull(match); Assert.Equal(expectedId, match.Value); @@ -106,7 +106,7 @@ public void DoesNotParseGarbageVodId() { const string GARBAGE = "SORRY FOR THE TRAFFIC NaM"; - var match = TwitchRegex.MatchVideoId(GARBAGE); + var match = IdParse.MatchVideoId(GARBAGE); Assert.Null(match); } @@ -116,7 +116,7 @@ public void DoesNotParseGarbageClipId() { const string GARBAGE = "SORRY FOR THE TRAFFIC NaM"; - var match = TwitchRegex.MatchClipId(GARBAGE); + var match = IdParse.MatchClipId(GARBAGE); Assert.Null(match); } @@ -126,7 +126,7 @@ public void DoesNotParseGarbageVodOrClipId() { const string GARBAGE = "SORRY FOR THE TRAFFIC NaM"; - var match = TwitchRegex.MatchVideoOrClipId(GARBAGE); + var match = IdParse.MatchVideoOrClipId(GARBAGE); Assert.Null(match); } diff --git a/TwitchDownloaderCore/Tools/IdParse.cs b/TwitchDownloaderCore/Tools/IdParse.cs new file mode 100644 index 00000000..485f4efc --- /dev/null +++ b/TwitchDownloaderCore/Tools/IdParse.cs @@ -0,0 +1,71 @@ +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text.RegularExpressions; + +namespace TwitchDownloaderCore.Tools +{ + public static class IdParse + { + // TODO: Use source generators when .NET7 + private static readonly Regex VideoId = new(@"(?<=^|twitch\.tv\/videos\/)\d+(?=\/?(?:$|\?))", RegexOptions.Compiled); + private static readonly Regex HighlightId = new(@"(?<=^|twitch\.tv\/\w+\/v(?:ideo)?\/)\d+(?=\/?(?:$|\?))", RegexOptions.Compiled); + private static readonly Regex ClipId = new(@"(?<=^|(?:clips\.)?twitch\.tv\/(?:\w+\/clip\/)?)[\w-]+?(?=\/?(?:$|\?))", RegexOptions.Compiled); + + /// A of the video's id or . + [return: MaybeNull] + public static Match MatchVideoId(string text) + { + text = text.Trim(); + + var videoIdMatch = VideoId.Match(text); + if (videoIdMatch.Success) + { + return videoIdMatch; + } + + var highlightIdMatch = HighlightId.Match(text); + if (highlightIdMatch.Success) + { + return highlightIdMatch; + } + + return null; + } + + /// A of the clip's id or . + [return: MaybeNull] + public static Match MatchClipId(string text) + { + text = text.Trim(); + + var clipIdMatch = ClipId.Match(text); + if (clipIdMatch.Success && !clipIdMatch.Value.All(char.IsDigit)) + { + return clipIdMatch; + } + + return null; + } + + /// A of the video/clip's id or . + [return: MaybeNull] + public static Match MatchVideoOrClipId(string text) + { + text = text.Trim(); + + var videoIdMatch = MatchVideoId(text); + if (videoIdMatch is { Success: true }) + { + return videoIdMatch; + } + + var clipIdMatch = MatchClipId(text); + if (clipIdMatch is { Success: true }) + { + return clipIdMatch; + } + + return null; + } + } +} \ No newline at end of file diff --git a/TwitchDownloaderCore/Tools/TwitchRegex.cs b/TwitchDownloaderCore/Tools/TwitchRegex.cs index 2eecaea4..6f2e4f8b 100644 --- a/TwitchDownloaderCore/Tools/TwitchRegex.cs +++ b/TwitchDownloaderCore/Tools/TwitchRegex.cs @@ -1,76 +1,12 @@ -using System.Diagnostics.CodeAnalysis; -using System.Linq; using System.Text.RegularExpressions; namespace TwitchDownloaderCore.Tools { public static class TwitchRegex { - // TODO: Use source generators when .NET7 - private static readonly Regex VideoId = new(@"(?<=^|twitch\.tv\/videos\/)\d+(?=\/?(?:$|\?))", RegexOptions.Compiled); - private static readonly Regex HighlightId = new(@"(?<=^|twitch\.tv\/\w+\/v(?:ideo)?\/)\d+(?=\/?(?:$|\?))", RegexOptions.Compiled); - private static readonly Regex ClipId = new(@"(?<=^|(?:clips\.)?twitch\.tv\/(?:\w+\/clip\/)?)[\w-]+?(?=\/?(?:$|\?))", RegexOptions.Compiled); - public static readonly Regex UrlTimeCode = new(@"(?<=(?:\?|&)t=)\d+h\d+m\d+s(?=$|\?|\s)", RegexOptions.Compiled); public static readonly Regex BitsRegex = new( @"(?<=(?:\s|^)(?:4Head|Anon|Bi(?:bleThumb|tBoss)|bday|C(?:h(?:eer|arity)|orgo)|cheerwal|D(?:ansGame|oodleCheer)|EleGiggle|F(?:rankerZ|ailFish)|Goal|H(?:eyGuys|olidayCheer)|K(?:appa|reygasm)|M(?:rDestructoid|uxy)|NotLikeThis|P(?:arty|ride|JSalt)|RIPCheer|S(?:coops|h(?:owLove|amrock)|eemsGood|wiftRage|treamlabs)|TriHard|uni|VoHiYo))[1-9]\d{0,6}(?=\s|$)", RegexOptions.Compiled); - - /// A of the video's id or . - [return: MaybeNull] - public static Match MatchVideoId(string text) - { - text = text.Trim(); - - var videoIdMatch = VideoId.Match(text); - if (videoIdMatch.Success) - { - return videoIdMatch; - } - - var highlightIdMatch = HighlightId.Match(text); - if (highlightIdMatch.Success) - { - return highlightIdMatch; - } - - return null; - } - - /// A of the clip's id or . - [return: MaybeNull] - public static Match MatchClipId(string text) - { - text = text.Trim(); - - var clipIdMatch = ClipId.Match(text); - if (clipIdMatch.Success && !clipIdMatch.Value.All(char.IsDigit)) - { - return clipIdMatch; - } - - return null; - } - - /// A of the video/clip's id or . - [return: MaybeNull] - public static Match MatchVideoOrClipId(string text) - { - text = text.Trim(); - - var videoIdMatch = MatchVideoId(text); - if (videoIdMatch is { Success: true }) - { - return videoIdMatch; - } - - var clipIdMatch = MatchClipId(text); - if (clipIdMatch is { Success: true }) - { - return clipIdMatch; - } - - return null; - } } } \ No newline at end of file diff --git a/TwitchDownloaderWPF/PageChatDownload.xaml.cs b/TwitchDownloaderWPF/PageChatDownload.xaml.cs index e6913302..9fcf5861 100644 --- a/TwitchDownloaderWPF/PageChatDownload.xaml.cs +++ b/TwitchDownloaderWPF/PageChatDownload.xaml.cs @@ -225,7 +225,7 @@ private void UpdateActionButtons(bool isDownloading) public static string ValidateUrl(string text) { - var vodClipIdMatch = TwitchRegex.MatchVideoOrClipId(text); + var vodClipIdMatch = IdParse.MatchVideoOrClipId(text); return vodClipIdMatch is { Success: true } ? vodClipIdMatch.Value : null; diff --git a/TwitchDownloaderWPF/PageClipDownload.xaml.cs b/TwitchDownloaderWPF/PageClipDownload.xaml.cs index 06b7770d..596e23cb 100644 --- a/TwitchDownloaderWPF/PageClipDownload.xaml.cs +++ b/TwitchDownloaderWPF/PageClipDownload.xaml.cs @@ -113,7 +113,7 @@ private void UpdateActionButtons(bool isDownloading) private static string ValidateUrl(string text) { - var clipIdMatch = TwitchRegex.MatchClipId(text); + var clipIdMatch = IdParse.MatchClipId(text); return clipIdMatch is { Success: true } ? clipIdMatch.Value : null; diff --git a/TwitchDownloaderWPF/PageVodDownload.xaml.cs b/TwitchDownloaderWPF/PageVodDownload.xaml.cs index bb8732e1..1202a5e2 100644 --- a/TwitchDownloaderWPF/PageVodDownload.xaml.cs +++ b/TwitchDownloaderWPF/PageVodDownload.xaml.cs @@ -285,7 +285,7 @@ public void SetImage(string imageUri, bool isGif) private static long ValidateUrl(string text) { - var vodIdMatch = TwitchRegex.MatchVideoId(text); + var vodIdMatch = IdParse.MatchVideoId(text); if (vodIdMatch is {Success: true} && long.TryParse(vodIdMatch.ValueSpan, out var vodId)) { return vodId;