diff --git a/TwitchDownloaderCLI/Modes/Arguments/ClipDownloadArgs.cs b/TwitchDownloaderCLI/Modes/Arguments/ClipDownloadArgs.cs index bb88c7df..6f07041c 100644 --- a/TwitchDownloaderCLI/Modes/Arguments/ClipDownloadArgs.cs +++ b/TwitchDownloaderCLI/Modes/Arguments/ClipDownloadArgs.cs @@ -22,6 +22,8 @@ public class ClipDownloadArgs : ITwitchDownloaderArgs [Option("ffmpeg-path", HelpText = "Path to FFmpeg executable.")] public string FfmpegPath { get; set; } + [Option("curl-path", HelpText = "Path to curl-impersonate executable.")] + public string CurlImpersonatePath { get; set; } [Option("temp-path", Default = "", HelpText = "Path to temporary caching folder.")] public string TempFolder { get; set; } diff --git a/TwitchDownloaderCLI/Modes/Arguments/CurlArgs.cs b/TwitchDownloaderCLI/Modes/Arguments/CurlArgs.cs new file mode 100644 index 00000000..4d6f3f5b --- /dev/null +++ b/TwitchDownloaderCLI/Modes/Arguments/CurlArgs.cs @@ -0,0 +1,21 @@ +using CommandLine.Text; +using CommandLine; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace TwitchDownloaderCLI.Modes.Arguments +{ + [Verb("curl", HelpText = "Manage standalone curl-impersonate")] + public class CurlArgs : ITwitchDownloaderArgs + { + [Option('d', "download", Default = false, Required = false, HelpText = "Downloads curl-impersonate as a standalone file.")] + public bool DownloadCurl { get; set; } + + [Option("banner", Default = true, HelpText = "Displays a banner containing version and copyright information.")] + public bool? ShowBanner { get; set; } + } +} diff --git a/TwitchDownloaderCLI/Modes/DownloadChat.cs b/TwitchDownloaderCLI/Modes/DownloadChat.cs index bd6a2159..187d9190 100644 --- a/TwitchDownloaderCLI/Modes/DownloadChat.cs +++ b/TwitchDownloaderCLI/Modes/DownloadChat.cs @@ -6,6 +6,7 @@ using TwitchDownloaderCore; using TwitchDownloaderCore.Options; using TwitchDownloaderCore.Tools; +using TwitchDownloaderCore.VideoPlatforms.Interfaces; namespace TwitchDownloaderCLI.Modes { @@ -15,10 +16,10 @@ internal static void Download(ChatDownloadArgs inputOptions) { var downloadOptions = GetDownloadOptions(inputOptions); - ChatDownloader chatDownloader = new(downloadOptions); Progress progress = new(); - progress.ProgressChanged += ProgressHandler.Progress_ProgressChanged; - chatDownloader.DownloadAsync(progress, new CancellationToken()).Wait(); + ChatDownloaderFactory downloadFactory = new ChatDownloaderFactory(progress); + IChatDownloader chatDownloader = downloadFactory.Create(downloadOptions); + chatDownloader.DownloadAsync(new CancellationToken()).Wait(); } private static ChatDownloadOptions GetDownloadOptions(ChatDownloadArgs inputOptions) @@ -29,8 +30,7 @@ private static ChatDownloadOptions GetDownloadOptions(ChatDownloadArgs inputOpti Environment.Exit(1); } - var vodClipIdMatch = TwitchRegex.MatchVideoOrClipId(inputOptions.Id); - if (vodClipIdMatch is not { Success: true }) + if (!IdParse.TryParseVideoOrClipId(inputOptions.Id, out var videoPlatform, out var videoType, out var videoId)) { Console.WriteLine("[ERROR] - Unable to parse Vod/Clip ID/URL."); Environment.Exit(1); @@ -47,7 +47,9 @@ private static ChatDownloadOptions GetDownloadOptions(ChatDownloadArgs inputOpti ".txt" or ".text" or "" => ChatFormat.Text, _ => throw new NotSupportedException($"{fileExtension} is not a valid chat file extension.") }, - Id = vodClipIdMatch.Value, + Id = videoId, + VideoPlatform = videoPlatform, + VideoType = videoType, CropBeginning = inputOptions.CropBeginningTime > 0.0, CropBeginningTime = inputOptions.CropBeginningTime, CropEnding = inputOptions.CropEndingTime > 0.0, diff --git a/TwitchDownloaderCLI/Modes/DownloadClip.cs b/TwitchDownloaderCLI/Modes/DownloadClip.cs index 321b1b52..7a2e3265 100644 --- a/TwitchDownloaderCLI/Modes/DownloadClip.cs +++ b/TwitchDownloaderCLI/Modes/DownloadClip.cs @@ -6,6 +6,7 @@ using TwitchDownloaderCore; using TwitchDownloaderCore.Options; using TwitchDownloaderCore.Tools; +using TwitchDownloaderCore.VideoPlatforms.Interfaces; namespace TwitchDownloaderCLI.Modes { @@ -23,7 +24,8 @@ internal static void Download(ClipDownloadArgs inputOptions) var downloadOptions = GetDownloadOptions(inputOptions); - ClipDownloader clipDownloader = new(downloadOptions, progress); + ClipDownloaderFactory downloadFactory = new ClipDownloaderFactory(progress); + IClipDownloader clipDownloader = downloadFactory.Create(downloadOptions); clipDownloader.DownloadAsync(new CancellationToken()).Wait(); } @@ -35,22 +37,27 @@ private static ClipDownloadOptions GetDownloadOptions(ClipDownloadArgs inputOpti Environment.Exit(1); } - var clipIdMatch = TwitchRegex.MatchClipId(inputOptions.Id); - if (clipIdMatch is not { Success: true }) + if (!IdParse.TryParseClip(inputOptions.Id, out var videoPlatform, out var videoId)) { Console.WriteLine("[ERROR] - Unable to parse Clip ID/URL."); Environment.Exit(1); } + if (videoPlatform == VideoPlatform.Kick) + { + CurlHandler.DetectCurl(inputOptions.CurlImpersonatePath); + } + ClipDownloadOptions downloadOptions = new() { - Id = clipIdMatch.Value, + Id = videoId, Filename = inputOptions.OutputFile, Quality = inputOptions.Quality, ThrottleKib = inputOptions.ThrottleKib, FfmpegPath = string.IsNullOrWhiteSpace(inputOptions.FfmpegPath) ? FfmpegHandler.FfmpegExecutableName : Path.GetFullPath(inputOptions.FfmpegPath), EncodeMetadata = inputOptions.EncodeMetadata!.Value, - TempFolder = inputOptions.TempFolder + TempFolder = inputOptions.TempFolder, + VideoPlatform = videoPlatform, }; return downloadOptions; diff --git a/TwitchDownloaderCLI/Modes/DownloadVideo.cs b/TwitchDownloaderCLI/Modes/DownloadVideo.cs index b7a1574f..edeeb97c 100644 --- a/TwitchDownloaderCLI/Modes/DownloadVideo.cs +++ b/TwitchDownloaderCLI/Modes/DownloadVideo.cs @@ -6,6 +6,7 @@ using TwitchDownloaderCore; using TwitchDownloaderCore.Options; using TwitchDownloaderCore.Tools; +using TwitchDownloaderCore.VideoPlatforms.Interfaces; namespace TwitchDownloaderCLI.Modes { @@ -19,7 +20,8 @@ internal static void Download(VideoDownloadArgs inputOptions) progress.ProgressChanged += ProgressHandler.Progress_ProgressChanged; var downloadOptions = GetDownloadOptions(inputOptions); - VideoDownloader videoDownloader = new(downloadOptions, progress); + VideoDownloaderFactory downloadFactory = new VideoDownloaderFactory(progress); + IVideoDownloader videoDownloader = downloadFactory.Create(downloadOptions); videoDownloader.DownloadAsync(new CancellationToken()).Wait(); } @@ -31,8 +33,7 @@ private static VideoDownloadOptions GetDownloadOptions(VideoDownloadArgs inputOp Environment.Exit(1); } - var vodIdMatch = TwitchRegex.MatchVideoId(inputOptions.Id); - if (vodIdMatch is not { Success: true}) + if (IdParse.TryParseVod(inputOptions.Id, out var videoPlatform, out var videoId)) { Console.WriteLine("[ERROR] - Unable to parse Vod ID/URL."); Environment.Exit(1); @@ -50,13 +51,14 @@ private static VideoDownloadOptions GetDownloadOptions(VideoDownloadArgs inputOp { DownloadThreads = inputOptions.DownloadThreads, ThrottleKib = inputOptions.ThrottleKib, - Id = int.Parse(vodIdMatch.ValueSpan), + Id = videoId, + VideoPlatform = videoPlatform, Oauth = inputOptions.Oauth, Filename = inputOptions.OutputFile, Quality = Path.GetExtension(inputOptions.OutputFile)!.ToLower() switch { - ".mp4" => inputOptions.Quality, - ".m4a" => "Audio", + ".m4a" when videoPlatform is VideoPlatform.Twitch => "Audio", + ".mp4" or ".m4a" => inputOptions.Quality, _ => throw new ArgumentException("Only MP4 and M4A audio files are supported.") }, CropBeginning = inputOptions.CropBeginningTime > 0.0, diff --git a/TwitchDownloaderCLI/Program.cs b/TwitchDownloaderCLI/Program.cs index f6e4aabe..810745f9 100644 --- a/TwitchDownloaderCLI/Program.cs +++ b/TwitchDownloaderCLI/Program.cs @@ -16,6 +16,8 @@ internal static class Program { private static void Main(string[] args) { + Environment.SetEnvironmentVariable("CURL_IMPERSONATE", "chrome110"); + var preParsedArgs = PreParseArgs.Parse(args, Path.GetFileName(Environment.ProcessPath)); var parser = new Parser(config => @@ -24,7 +26,7 @@ private static void Main(string[] args) config.HelpWriter = null; // Use null instead of TextWriter.Null due to how CommandLine works internally }); - var parserResult = parser.ParseArguments(preParsedArgs); + var parserResult = parser.ParseArguments(preParsedArgs); parserResult.WithNotParsed(errors => WriteHelpText(errors, parserResult, parser.Settings)); CoreLicensor.EnsureFilesExist(AppContext.BaseDirectory); @@ -37,6 +39,7 @@ private static void Main(string[] args) .WithParsed(UpdateChat.Update) .WithParsed(RenderChat.Render) .WithParsed(FfmpegHandler.ParseArgs) + .WithParsed(CurlHandler.ParseArgs) .WithParsed(CacheHandler.ParseArgs); } diff --git a/TwitchDownloaderCLI/Properties/launchSettings.json b/TwitchDownloaderCLI/Properties/launchSettings.json index a28423c5..b03889fd 100644 --- a/TwitchDownloaderCLI/Properties/launchSettings.json +++ b/TwitchDownloaderCLI/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "TwitchDownloaderCLI": { "commandName": "Project", - "commandLineArgs": "chatrender -i chat.json --badge-filter 255 -o chat.mp4" + "commandLineArgs": "curl --download" }, "WSL": { "commandName": "WSL2", diff --git a/TwitchDownloaderCLI/Tools/CurlHandler.cs b/TwitchDownloaderCLI/Tools/CurlHandler.cs new file mode 100644 index 00000000..324cfeea --- /dev/null +++ b/TwitchDownloaderCLI/Tools/CurlHandler.cs @@ -0,0 +1,70 @@ +using ICSharpCode.SharpZipLib.GZip; +using ICSharpCode.SharpZipLib.Tar; +using Microsoft.Extensions.Hosting.Internal; +using Mono.Unix; +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Net.Http; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using TwitchDownloaderCLI.Modes.Arguments; + +namespace TwitchDownloaderCLI.Tools +{ + public class CurlHandler + { + public static readonly string[] FilesToExtract = new string[] { "curl-ca-bundle.crt", "libcurl.dll", "libcurl.a", "libcurldll.a", }; + + public static void DetectCurl(string curlImpersonatePath) + { + throw new NotImplementedException(); + + Console.WriteLine("[ERROR] - Unable to find curl-impersonate, exiting."); + Environment.Exit(1); + } + public static void ParseArgs(CurlArgs args) + { + if (args.DownloadCurl) + { + DownloadCurl(args).Wait(); + } + } + + private static async Task DownloadCurl(CurlArgs args) + { + using HttpClient httpClient = new HttpClient(); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + using (HttpResponseMessage response = await httpClient.GetAsync("https://github.com/depler/curl-impersonate-win/releases/download/20230227/curl-impersonate-win.zip")) + using (Stream streamToReadFrom = await response.Content.ReadAsStreamAsync()) + { + using (var fs = new FileStream("curl-impersonate-win.zip", FileMode.Create)) + { + await response.Content.CopyToAsync(fs); + } + } + + using (var archive = ZipFile.OpenRead("curl-impersonate-win.zip")) + { + foreach (var item in archive.Entries) + { + if (FilesToExtract.Contains(item.Name)) + { + item.ExtractToFile(Path.Combine(Directory.GetCurrentDirectory(), item.FullName.Replace("curl-impersonate-win/", "")), true); + } + } + } + + File.Delete("curl-impersonate-win.zip"); + } + else + { + throw new NotImplementedException(); + } + } + } +} diff --git a/TwitchDownloaderCLI/TwitchDownloaderCLI.csproj b/TwitchDownloaderCLI/TwitchDownloaderCLI.csproj index 1515eb60..5b733e6b 100644 --- a/TwitchDownloaderCLI/TwitchDownloaderCLI.csproj +++ b/TwitchDownloaderCLI/TwitchDownloaderCLI.csproj @@ -15,6 +15,7 @@ + diff --git a/TwitchDownloaderCore.Tests/IdParseTests.cs b/TwitchDownloaderCore.Tests/IdParseTests.cs new file mode 100644 index 00000000..fbd99a97 --- /dev/null +++ b/TwitchDownloaderCore.Tests/IdParseTests.cs @@ -0,0 +1,159 @@ +using TwitchDownloaderCore.Tools; + +namespace TwitchDownloaderCore.Tests +{ + // ReSharper disable StringLiteralTypo + public class IdParseTests + { + [Theory] + [InlineData("9d02cf06-52e8-4023-abd5-16b21d143867", VideoPlatform.Kick)] + [InlineData("79200ecf-c46d-4d74-b3ad-d06a7e0e5d8d", VideoPlatform.Kick)] + [InlineData("41546181", VideoPlatform.Twitch)] // Oldest VODs - 8 + [InlineData("982306410", VideoPlatform.Twitch)] // Old VODs - 9 + [InlineData("6834869128", VideoPlatform.Twitch)] // Current VODs - 10 + [InlineData("11987163407", VideoPlatform.Twitch)] // Future VODs - 11 + public void CorrectlyParsesVodId(string id, VideoPlatform expectedPlatform) + { + var success = IdParse.TryParseVod(id, out var videoPlatform, out var videoId); + + Assert.True(success); + Assert.Equal(expectedPlatform, videoPlatform); + Assert.Equal(id, videoId); + } + + [Theory] + [InlineData("https://kick.com/streamer8/videos/9d02cf06-52e8-4023-abd5-16b21d143867", "9d02cf06-52e8-4023-abd5-16b21d143867", VideoPlatform.Kick)] + [InlineData("https://kick.com/streamer8/videos/79200ecf-c46d-4d74-b3ad-d06a7e0e5d8d", "79200ecf-c46d-4d74-b3ad-d06a7e0e5d8d", VideoPlatform.Kick)] + [InlineData("https://www.twitch.tv/videos/41546181", "41546181", VideoPlatform.Twitch)] // Oldest VODs - 8 + [InlineData("https://www.twitch.tv/videos/982306410", "982306410", VideoPlatform.Twitch)] // Old VODs - 9 + [InlineData("https://www.twitch.tv/videos/6834869128", "6834869128", VideoPlatform.Twitch)] // Current VODs - 10 + [InlineData("https://www.twitch.tv/videos/11987163407", "11987163407", VideoPlatform.Twitch)] // Future VODs - 11 + [InlineData("https://www.twitch.tv/kitboga/video/2865132173", "2865132173", VideoPlatform.Twitch)] // Alternate highlight URL + [InlineData("https://www.twitch.tv/kitboga/v/2865132173", "2865132173", VideoPlatform.Twitch)] // Alternate highlight URL + public void CorrectlyParsesVodLink(string link, string expectedId, VideoPlatform expectedPlatform) + { + var success = IdParse.TryParseVod(link, out var videoPlatform, out var videoId); + + Assert.True(success); + Assert.Equal(expectedPlatform, videoPlatform); + Assert.Equal(expectedId, videoId); + } + + [Theory] + [InlineData("clip_F786F81SF785610534215S23D0", VideoPlatform.Kick)] + [InlineData("SpineyPieTwitchRPGNurturing", VideoPlatform.Twitch)] + [InlineData("FuriousFlaccidTireArgieB8-NHbTiYQlzwHVvv_Vf", VideoPlatform.Twitch)] + public void CorrectlyParsesClipId(string id, VideoPlatform expectedPlatform) + { + var success = IdParse.TryParseClip(id, out var videoPlatform, out var videoId); + + Assert.True(success); + Assert.Equal(expectedPlatform, videoPlatform); + Assert.Equal(id, videoId); + } + + [Theory] + [InlineData("https://kick.com/streamer8/clips/clip_F786F81SF785610534215S23D0", "clip_F786F81SF785610534215S23D0", VideoPlatform.Kick)] + [InlineData("https://www.twitch.tv/streamer8/clip/SpineyPieTwitchRPGNurturing", "SpineyPieTwitchRPGNurturing", VideoPlatform.Twitch)] + [InlineData("https://www.twitch.tv/streamer8/clip/FuriousFlaccidTireArgieB8-NHbTiYQlzwHVvv_Vf", "FuriousFlaccidTireArgieB8-NHbTiYQlzwHVvv_Vf", VideoPlatform.Twitch)] + [InlineData("https://www.twitch.tv/streamer8/clip/SpineyPieTwitchRPGNurturing?featured=false&filter=clips&range=all&sort=time", "SpineyPieTwitchRPGNurturing", VideoPlatform.Twitch)] + [InlineData("https://www.twitch.tv/streamer8/clip/FuriousFlaccidTireArgieB8-NHbTiYQlzwHVvv_Vf?featured=false&filter=clips&range=all&sort=time", "FuriousFlaccidTireArgieB8-NHbTiYQlzwHVvv_Vf", VideoPlatform.Twitch)] + [InlineData("https://clips.twitch.tv/SpineyPieTwitchRPGNurturing", "SpineyPieTwitchRPGNurturing", VideoPlatform.Twitch)] + [InlineData("https://clips.twitch.tv/FuriousFlaccidTireArgieB8-NHbTiYQlzwHVvv_Vf", "FuriousFlaccidTireArgieB8-NHbTiYQlzwHVvv_Vf", VideoPlatform.Twitch)] + public void CorrectlyParsesClipLink(string link, string expectedId, VideoPlatform expectedPlatform) + { + var success = IdParse.TryParseClip(link, out var videoPlatform, out var videoId); + + Assert.True(success); + Assert.Equal(expectedPlatform, videoPlatform); + Assert.Equal(expectedId, videoId); + } + + [Theory] + [InlineData("9d02cf06-52e8-4023-abd5-16b21d143867", VideoType.Video, VideoPlatform.Kick)] + [InlineData("79200ecf-c46d-4d74-b3ad-d06a7e0e5d8d", VideoType.Video, VideoPlatform.Kick)] + [InlineData("41546181", VideoType.Video, VideoPlatform.Twitch)] // Oldest VODs - 8 + [InlineData("982306410", VideoType.Video, VideoPlatform.Twitch)] // Old VODs - 9 + [InlineData("6834869128", VideoType.Video, VideoPlatform.Twitch)] // Current VODs - 10 + [InlineData("11987163407", VideoType.Video, VideoPlatform.Twitch)] // Future VODs - 11 + [InlineData("clip_F786F81SF785610534215S23D0", VideoType.Clip, VideoPlatform.Kick)] + [InlineData("SpineyPieTwitchRPGNurturing", VideoType.Clip, VideoPlatform.Twitch)] + [InlineData("FuriousFlaccidTireArgieB8-NHbTiYQlzwHVvv_Vf", VideoType.Clip, VideoPlatform.Twitch)] + public void CorrectlyParsesVodOrClipId(string id, VideoType expectedType, VideoPlatform expectedPlatform) + { + var success = IdParse.TryParseVideoOrClipId(id, out var videoPlatform, out var videoType, out var videoId); + + Assert.True(success); + Assert.Equal(expectedType, videoType); + Assert.Equal(expectedPlatform, videoPlatform); + Assert.Equal(id, videoId); + } + + [Theory] + [InlineData("https://kick.com/streamer8/videos/9d02cf06-52e8-4023-abd5-16b21d143867", "9d02cf06-52e8-4023-abd5-16b21d143867", VideoType.Video, VideoPlatform.Kick)] + [InlineData("https://kick.com/streamer8/videos/79200ecf-c46d-4d74-b3ad-d06a7e0e5d8d", "79200ecf-c46d-4d74-b3ad-d06a7e0e5d8d", VideoType.Video, VideoPlatform.Kick)] + [InlineData("https://www.twitch.tv/videos/41546181", "41546181", VideoType.Video, VideoPlatform.Twitch)] // Oldest VODs - 8 + [InlineData("https://www.twitch.tv/videos/982306410", "982306410", VideoType.Video, VideoPlatform.Twitch)] // Old VODs - 9 + [InlineData("https://www.twitch.tv/videos/6834869128", "6834869128", VideoType.Video, VideoPlatform.Twitch)] // Current VODs - 10 + [InlineData("https://www.twitch.tv/videos/11987163407", "11987163407", VideoType.Video, VideoPlatform.Twitch)] // Future VODs - 11 + [InlineData("https://www.twitch.tv/kitboga/video/2865132173", "2865132173", VideoType.Video, VideoPlatform.Twitch)] // Alternate highlight URL + [InlineData("https://www.twitch.tv/kitboga/v/2865132173", "2865132173", VideoType.Video, VideoPlatform.Twitch)] // Alternate VOD URL + [InlineData("https://kick.com/streamer8/clips/clip_F786F81SF785610534215S23D0", "clip_F786F81SF785610534215S23D0", VideoType.Clip, VideoPlatform.Kick)] + [InlineData("https://www.twitch.tv/streamer8/clip/SpineyPieTwitchRPGNurturing", "SpineyPieTwitchRPGNurturing", VideoType.Clip, VideoPlatform.Twitch)] + [InlineData("https://www.twitch.tv/streamer8/clip/FuriousFlaccidTireArgieB8-NHbTiYQlzwHVvv_Vf", "FuriousFlaccidTireArgieB8-NHbTiYQlzwHVvv_Vf", VideoType.Clip, VideoPlatform.Twitch)] + [InlineData("https://www.twitch.tv/streamer8/clip/SpineyPieTwitchRPGNurturing?featured=false&filter=clips&range=all&sort=time", "SpineyPieTwitchRPGNurturing", VideoType.Clip, VideoPlatform.Twitch)] + [InlineData("https://www.twitch.tv/streamer8/clip/FuriousFlaccidTireArgieB8-NHbTiYQlzwHVvv_Vf?featured=false&filter=clips&range=all&sort=time", "FuriousFlaccidTireArgieB8-NHbTiYQlzwHVvv_Vf", VideoType.Clip, VideoPlatform.Twitch)] + [InlineData("https://clips.twitch.tv/SpineyPieTwitchRPGNurturing", "SpineyPieTwitchRPGNurturing", VideoType.Clip, VideoPlatform.Twitch)] + [InlineData("https://clips.twitch.tv/FuriousFlaccidTireArgieB8-NHbTiYQlzwHVvv_Vf", "FuriousFlaccidTireArgieB8-NHbTiYQlzwHVvv_Vf", VideoType.Clip, VideoPlatform.Twitch)] + public void CorrectlyParsesVodOrClipLink(string link, string expectedId, VideoType expectedType, VideoPlatform expectedPlatform) + { + var success = IdParse.TryParseVideoOrClipId(link, out var videoPlatform, out var videoType, out var videoId); + + Assert.True(success); + Assert.Equal(expectedType, videoType); + Assert.Equal(expectedPlatform, videoPlatform); + Assert.Equal(expectedId, videoId); + } + + [Fact] + public void DoesNotParseGarbageVodId() + { + const string GARBAGE = "SORRY FOR THE TRAFFIC NaM"; + const VideoPlatform EXPECTED_PLATFORM = VideoPlatform.Unknown; + + var success = IdParse.TryParseVod(GARBAGE, out var videoPlatform, out var videoId); + + Assert.False(success); + Assert.Equal(EXPECTED_PLATFORM, videoPlatform); + Assert.NotEqual(GARBAGE, videoId); + } + + [Fact] + public void DoesNotParseGarbageClipId() + { + const string GARBAGE = "SORRY FOR THE TRAFFIC NaM"; + const VideoPlatform EXPECTED_PLATFORM = VideoPlatform.Unknown; + + var success = IdParse.TryParseClip(GARBAGE, out var videoPlatform, out var videoId); + + Assert.False(success); + Assert.Equal(EXPECTED_PLATFORM, videoPlatform); + Assert.NotEqual(GARBAGE, videoId); + } + + [Fact] + public void DoesNotParseGarbageVodOrClipId() + { + const string GARBAGE = "SORRY FOR THE TRAFFIC NaM"; + const VideoType EXPECTED_TYPE = VideoType.Unknown; + const VideoPlatform EXPECTED_PLATFORM = VideoPlatform.Unknown; + + var success = IdParse.TryParseVideoOrClipId(GARBAGE, out var videoPlatform, out var videoType, out var videoId); + + Assert.False(success); + Assert.Equal(EXPECTED_TYPE, videoType); + Assert.Equal(EXPECTED_PLATFORM, videoPlatform); + Assert.NotEqual(GARBAGE, videoId); + } + } +} \ No newline at end of file diff --git a/TwitchDownloaderCore.Tests/ToolTests/HighlightIconsTests.cs b/TwitchDownloaderCore.Tests/ToolTests/HighlightIconsTests.cs index c47ccd45..0f1ad879 100644 --- a/TwitchDownloaderCore.Tests/ToolTests/HighlightIconsTests.cs +++ b/TwitchDownloaderCore.Tests/ToolTests/HighlightIconsTests.cs @@ -1,6 +1,6 @@ using System.Text.Json; using TwitchDownloaderCore.Tools; -using TwitchDownloaderCore.TwitchObjects; +using TwitchDownloaderCore.VideoPlatforms.Twitch; namespace TwitchDownloaderCore.Tests.ToolTests { diff --git a/TwitchDownloaderCore.Tests/ToolTests/M3U8Tests.cs b/TwitchDownloaderCore.Tests/ToolTests/M3U8Tests.cs index c0819f15..123fd246 100644 --- a/TwitchDownloaderCore.Tests/ToolTests/M3U8Tests.cs +++ b/TwitchDownloaderCore.Tests/ToolTests/M3U8Tests.cs @@ -1,4 +1,4 @@ -using System.Globalization; +using System.Globalization; using System.Text; using TwitchDownloaderCore.Tools; diff --git a/TwitchDownloaderCore/Chat/ChatHtml.cs b/TwitchDownloaderCore/Chat/ChatHtml.cs index 1b2ccf61..0255b9a4 100644 --- a/TwitchDownloaderCore/Chat/ChatHtml.cs +++ b/TwitchDownloaderCore/Chat/ChatHtml.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; using System.Web; using TwitchDownloaderCore.Tools; -using TwitchDownloaderCore.TwitchObjects; +using TwitchDownloaderCore.VideoPlatforms.Twitch; namespace TwitchDownloaderCore.Chat { @@ -37,7 +37,7 @@ public static class ChatHtml var outputDirectory = Directory.GetParent(Path.GetFullPath(filePath))!; if (!outputDirectory.Exists) { - TwitchHelper.CreateDirectory(outputDirectory.FullName); + PlatformHelper.CreateDirectory(outputDirectory.FullName); } await using var fs = File.Create(filePath); diff --git a/TwitchDownloaderCore/Chat/ChatJson.cs b/TwitchDownloaderCore/Chat/ChatJson.cs index 204e5263..7d1713c6 100644 --- a/TwitchDownloaderCore/Chat/ChatJson.cs +++ b/TwitchDownloaderCore/Chat/ChatJson.cs @@ -12,7 +12,8 @@ using System.Threading.Tasks; using TwitchDownloaderCore.Extensions; using TwitchDownloaderCore.Tools; -using TwitchDownloaderCore.TwitchObjects; +using TwitchDownloaderCore.VideoPlatforms.Twitch; +using TwitchDownloaderCore.VideoPlatforms.Twitch.Downloaders; namespace TwitchDownloaderCore.Chat { @@ -67,6 +68,11 @@ public static async Task DeserializeAsync(string filePath, bool getCom returnChatRoot.video = videoElement.Deserialize