Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kick support #834

Open
wants to merge 48 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
7a82b22
Limited VOD + Clip + Chat Download support for desktop app
lay295 Sep 25, 2023
e45d8af
Render Kick chats
lay295 Sep 26, 2023
55d5ba6
Fix downloading
lay295 Sep 27, 2023
06e0f51
Merge with master
ScrubN Nov 2, 2023
95f0b3d
Clean curl buffer before returning
ScrubN Nov 2, 2023
d79ebc0
Clean all the buffers, reduce ProgressCopyToAsync overhead, reduce ma…
ScrubN Nov 2, 2023
c8c222d
Change all FileShare.None to FileShare.Read
ScrubN Nov 2, 2023
a2d7a64
Fix memory leak
ScrubN Nov 3, 2023
ba6b184
Forgot to stage
ScrubN Nov 3, 2023
0561c26
This is handled by SetHandleAsInvalid
ScrubN Nov 3, 2023
70924de
Fix
ScrubN Nov 7, 2023
7a5711b
Merge master into kick-support (#880)
ScrubN Nov 15, 2023
c15d121
Use strings for VideoPlatform in JSON
ScrubN Nov 15, 2023
c1cb452
Merge branch 'kick-support-merge' into kick-support
ScrubN Nov 15, 2023
bd5322e
Merge with master
ScrubN Nov 15, 2023
08524f3
Fix potential NRE
ScrubN Nov 16, 2023
a065c2c
Merge branch 'master' into kick-support
ScrubN Nov 16, 2023
440baaa
Fix incorrect invalid Twitch VOD detection
ScrubN Nov 16, 2023
57c0265
Do not support replacing escape chars in ReadOnlySpanExtensions.TryRe…
ScrubN Nov 17, 2023
8a9ee2a
Create M3U8 parser
ScrubN Nov 17, 2023
7dbae7a
Create M3U8 tests
ScrubN Nov 17, 2023
56b5a91
Use DateTimeOffset instead of DateTime
ScrubN Nov 17, 2023
ba4b028
Update M3U8
ScrubN Nov 18, 2023
578634f
Implement M3U8
ScrubN Nov 18, 2023
52854a7
Merge branch 'master' into kick-support
ScrubN Nov 21, 2023
77e2d38
Fix downloading videos as audio with CLI
ScrubN Nov 21, 2023
0da3dca
Fix being unable to download clips that return non-integer framerates…
ScrubN Nov 21, 2023
f5a4481
This really should not have needed a merge, git.
ScrubN Nov 21, 2023
89de6cd
Rename UrlParse -> IdParse, add support for parsing Kick IDs without …
ScrubN Nov 22, 2023
7331eaf
Merge branch 'master' into kick-support
ScrubN Nov 29, 2023
856d10d
Merge branch 'master' into kick-support
ScrubN Nov 29, 2023
86a537f
Merge with master
ScrubN Dec 11, 2023
47c5749
Update M3U8
ScrubN Dec 14, 2023
b7a10f5
Add dedicated tests for ExtByteRange & StreamResolution
ScrubN Dec 14, 2023
e7c74b7
Create ReadOnlySpanExtensions.UnEscapedIndexOfAny
ScrubN Dec 14, 2023
8bf2516
Add tests for ReadOnlySpanExtensions.UnEscapedIndexOfAny
ScrubN Dec 14, 2023
0f6ed7f
Update TimeSpanHFormat and TimeSpanHFormatTests
ScrubN Dec 14, 2023
9c193b8
Do not target net6.0-windows for TwitchDownloaderCore.Tests
ScrubN Dec 14, 2023
64ad1f2
Simplify TwitchVideoDownloader.GetQualitiesPlaylist
ScrubN Dec 14, 2023
e6566af
Use Utc ticks for kick temp clip files
ScrubN Dec 14, 2023
77bc293
Merge branch 'master' into kick-support
ScrubN Dec 14, 2023
3280823
Merge branch 'master' into kick-support
ScrubN Dec 30, 2023
41534ee
Merge branch 'master' into kick-support
ScrubN Jan 21, 2024
ea2f38e
Replace some HttpClient requests with Curl requests
ScrubN Jan 21, 2024
474154c
Use M3U8Extensions.GetStreamOfQuality for kick quality selection
ScrubN Jan 21, 2024
027e5ac
Make curl requests free-er
ScrubN Jan 21, 2024
dc5cc89
Add overload to copy curl response to stream
ScrubN Jan 21, 2024
be21ee7
Update kick-support branch to support new Kick URL scheme (#1243)
cst8t Nov 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions TwitchDownloaderCLI/Modes/Arguments/ChatDownloadArgs.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using CommandLine;
using TwitchDownloaderCore.Chat;
using TwitchDownloaderCore.Tools;

namespace TwitchDownloaderCLI.Modes.Arguments
{
Expand Down
2 changes: 1 addition & 1 deletion TwitchDownloaderCLI/Modes/Arguments/ChatUpdateArgs.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using CommandLine;
using TwitchDownloaderCore.Chat;
using TwitchDownloaderCore.Tools;

namespace TwitchDownloaderCLI.Modes.Arguments
{
Expand Down
2 changes: 2 additions & 0 deletions TwitchDownloaderCLI/Modes/Arguments/ClipDownloadArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand Down
21 changes: 21 additions & 0 deletions TwitchDownloaderCLI/Modes/Arguments/CurlArgs.cs
Original file line number Diff line number Diff line change
@@ -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; }
}
}
15 changes: 8 additions & 7 deletions TwitchDownloaderCLI/Modes/DownloadChat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
using System.IO;
using System.Threading;
using TwitchDownloaderCLI.Modes.Arguments;
using TwitchDownloaderCLI.Tools;
using TwitchDownloaderCore;
using TwitchDownloaderCore.Chat;
using TwitchDownloaderCore.Options;
using TwitchDownloaderCore.Tools;
using TwitchDownloaderCore.VideoPlatforms.Interfaces;

namespace TwitchDownloaderCLI.Modes
{
Expand All @@ -16,10 +16,10 @@ internal static void Download(ChatDownloadArgs inputOptions)
{
var downloadOptions = GetDownloadOptions(inputOptions);

ChatDownloader chatDownloader = new(downloadOptions);
Progress<ProgressReport> 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)
Expand All @@ -30,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 (!UrlParse.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);
Expand All @@ -48,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,
Expand Down
17 changes: 12 additions & 5 deletions TwitchDownloaderCLI/Modes/DownloadClip.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using TwitchDownloaderCore;
using TwitchDownloaderCore.Options;
using TwitchDownloaderCore.Tools;
using TwitchDownloaderCore.VideoPlatforms.Interfaces;

namespace TwitchDownloaderCLI.Modes
{
Expand All @@ -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();
}

Expand All @@ -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 (!UrlParse.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;
Expand Down
10 changes: 6 additions & 4 deletions TwitchDownloaderCLI/Modes/DownloadVideo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using TwitchDownloaderCore;
using TwitchDownloaderCore.Options;
using TwitchDownloaderCore.Tools;
using TwitchDownloaderCore.VideoPlatforms.Interfaces;

namespace TwitchDownloaderCLI.Modes
{
Expand All @@ -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();
}

Expand All @@ -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 (UrlParse.TryParseVod(inputOptions.Id, out var videoPlatform, out var videoId))
{
Console.WriteLine("[ERROR] - Unable to parse Vod ID/URL.");
Environment.Exit(1);
Expand All @@ -50,7 +51,8 @@ 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
Expand Down
2 changes: 1 addition & 1 deletion TwitchDownloaderCLI/Modes/UpdateChat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
using TwitchDownloaderCLI.Modes.Arguments;
using TwitchDownloaderCLI.Tools;
using TwitchDownloaderCore;
using TwitchDownloaderCore.Chat;
using TwitchDownloaderCore.Options;
using TwitchDownloaderCore.Tools;

namespace TwitchDownloaderCLI.Modes
{
Expand Down
5 changes: 4 additions & 1 deletion TwitchDownloaderCLI/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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 =>
Expand All @@ -24,7 +26,7 @@ private static void Main(string[] args)
config.HelpWriter = TextWriter.Null;
});

var parserResult = parser.ParseArguments<VideoDownloadArgs, ClipDownloadArgs, ChatDownloadArgs, ChatUpdateArgs, ChatRenderArgs, FfmpegArgs, CacheArgs>(preParsedArgs);
var parserResult = parser.ParseArguments<VideoDownloadArgs, ClipDownloadArgs, ChatDownloadArgs, ChatUpdateArgs, ChatRenderArgs, FfmpegArgs, CurlArgs, CacheArgs>(preParsedArgs);
parserResult.WithNotParsed(errors => WriteHelpText(errors, parserResult, parser.Settings));

CoreLicensor.EnsureFilesExist(AppContext.BaseDirectory);
Expand All @@ -37,6 +39,7 @@ private static void Main(string[] args)
.WithParsed<ChatUpdateArgs>(UpdateChat.Update)
.WithParsed<ChatRenderArgs>(RenderChat.Render)
.WithParsed<FfmpegArgs>(FfmpegHandler.ParseArgs)
.WithParsed<CurlArgs>(CurlHandler.ParseArgs)
.WithParsed<CacheArgs>(CacheHandler.ParseArgs);
}

Expand Down
2 changes: 1 addition & 1 deletion TwitchDownloaderCLI/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
70 changes: 70 additions & 0 deletions TwitchDownloaderCLI/Tools/CurlHandler.cs
Original file line number Diff line number Diff line change
@@ -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();
}
}
}
}
1 change: 1 addition & 0 deletions TwitchDownloaderCLI/TwitchDownloaderCLI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
<PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
<PackageReference Include="SharpZipLib" Version="1.4.2" />
<PackageReference Include="Xabe.FFmpeg.Downloader" Version="5.2.6" />
</ItemGroup>

Expand Down
9 changes: 0 additions & 9 deletions TwitchDownloaderCore/Chat/ChatCompression.cs

This file was deleted.

9 changes: 0 additions & 9 deletions TwitchDownloaderCore/Chat/ChatFormat.cs

This file was deleted.

4 changes: 2 additions & 2 deletions TwitchDownloaderCore/Chat/ChatHtml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
using System.Threading.Tasks;
using System.Web;
using TwitchDownloaderCore.Tools;
using TwitchDownloaderCore.TwitchObjects;
using TwitchDownloaderCore.VideoPlatforms.Twitch;

namespace TwitchDownloaderCore.Chat
{
Expand Down Expand Up @@ -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);
Expand Down
13 changes: 10 additions & 3 deletions TwitchDownloaderCore/Chat/ChatJson.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,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
{
Expand Down Expand Up @@ -78,6 +79,11 @@ public static async Task<ChatRoot> DeserializeAsync(string filePath, bool getCom
returnChatRoot.video = videoElement.Deserialize<Video>(options: _jsonSerializerOptions);
}

if (jsonDocument.RootElement.TryGetProperty("videoPlatform", out JsonElement platformElement))
{
returnChatRoot.videoPlatform = platformElement.Deserialize<VideoPlatform>(options: _jsonSerializerOptions);
}

if (getComments)
{
if (jsonDocument.RootElement.TryGetProperty("comments", out JsonElement commentsElement))
Expand Down Expand Up @@ -217,7 +223,7 @@ private static async Task UpgradeChatJson(ChatRoot chatRoot)
{
foreach (var comment in chatRoot.comments)
{
var bitMatch = TwitchRegex.BitsRegex.Match(comment.message.body);
var bitMatch = TwitchChatDownloader.BitsRegex.Match(comment.message.body);
if (bitMatch.Success && int.TryParse(bitMatch.ValueSpan, out var result))
{
comment.message.bits_spent = result;
Expand All @@ -236,9 +242,10 @@ public static async Task SerializeAsync(string filePath, ChatRoot chatRoot, Chat
var outputDirectory = Directory.GetParent(Path.GetFullPath(filePath))!;
if (!outputDirectory.Exists)
{
TwitchHelper.CreateDirectory(outputDirectory.FullName);
PlatformHelper.CreateDirectory(outputDirectory.FullName);
}

// TODO: Maybe add Bzip2 and 7z support
await using var fs = File.Create(filePath);
switch (compression)
{
Expand Down
Loading