diff --git a/TwitchDownloaderWPF/PageChatRender.xaml.cs b/TwitchDownloaderWPF/PageChatRender.xaml.cs index e800590e..c83aece9 100644 --- a/TwitchDownloaderWPF/PageChatRender.xaml.cs +++ b/TwitchDownloaderWPF/PageChatRender.xaml.cs @@ -27,8 +27,8 @@ namespace TwitchDownloaderWPF /// public partial class PageChatRender : System.Windows.Controls.Page { - public SKPaint imagePaint = new SKPaint() { IsAntialias = true, FilterQuality = SKFilterQuality.High}; - public SKPaint emotePaint = new SKPaint() { IsAntialias = true, FilterQuality = SKFilterQuality.High}; + public SKPaint imagePaint = new SKPaint() { IsAntialias = true, FilterQuality = SKFilterQuality.High }; + public SKPaint emotePaint = new SKPaint() { IsAntialias = true, FilterQuality = SKFilterQuality.High }; public SKFontManager fontManager = SKFontManager.CreateDefault(); public ConcurrentDictionary fallbackCache = new ConcurrentDictionary(); public WindowPreview windowPreview = null; @@ -101,7 +101,7 @@ private void BackgroundRenderManager_DoWork(object sender, DoWorkEventArgs e) chatJson.streamer.id = Int32.Parse(chatJson.comments.First().channel_id); chatJson.streamer.name = GetStreamerName(chatJson.streamer.id); } - BlockingCollection finalComments = new BlockingCollection(); + BlockingCollection finalComments = new BlockingCollection(); List thirdPartyEmotes = new List(); List chatBadges = new List(); Dictionary chatEmotes = new Dictionary(); @@ -154,7 +154,7 @@ private void BackgroundRenderManager_DoWork(object sender, DoWorkEventArgs e) List imageList = new List(); SKBitmap sectionImage = new SKBitmap((int)canvasSize.Width, (int)canvasSize.Height); - + List currentGifEmotes = new List(); List emoteList = new List(); List emotePositionList = new List(); @@ -197,7 +197,7 @@ private void BackgroundRenderManager_DoWork(object sender, DoWorkEventArgs e) } (sender as BackgroundWorker).ReportProgress(0, new Progress("Rendering Video 0%")); - RenderVideo(renderOptions, new List(finalComments.ToArray()), chatJson.comments, sender); + RenderVideo(renderOptions, new Queue(finalComments.ToArray()), chatJson.comments, sender); (sender as BackgroundWorker).ReportProgress(0, new Progress("Cleaning up...")); try @@ -236,6 +236,11 @@ private string GetStreamerName(int id) private SKColor GenerateUserColor(SKColor userColor, SKColor background_color) { + if (userColor.Red == 0 && userColor.Green == 0 && userColor.Blue == 0) + { + SKColor newColor = SKColor.Parse("#858585"); + } + //I don't really know much about this, but i'll give it a shot float[] userColorHsl = new float[3]; float[] backgroundColorHsl = new float[3]; @@ -255,15 +260,23 @@ private SKColor GenerateUserColor(SKColor userColor, SKColor background_color) return userColor; } - private void RenderVideo(RenderOptions renderOptions, List finalComments, List comments, object sender) + private void RenderVideo(RenderOptions renderOptions, Queue finalComments, List comments, object sender) { SKBitmap bufferBitmap = new SKBitmap(renderOptions.chat_width, renderOptions.chat_height); SKCanvas bufferCanvas = new SKCanvas(bufferBitmap); + SKPaint gifBackgroundPaint = new SKPaint(); + SKPaint gifPaint = new SKPaint(); int videoStart = (int)Math.Floor(comments.First().content_offset_seconds); int duration = (int)Math.Ceiling(comments.Last().content_offset_seconds) - videoStart; List displayedGifs = new List(); Stopwatch stopwatch = new Stopwatch(); + if (renderOptions.background_color.Alpha < 255) + { + gifPaint.BlendMode = SKBlendMode.SrcOver; + gifBackgroundPaint.BlendMode = SKBlendMode.Src; + } + if (File.Exists(renderOptions.save_path)) File.Delete(renderOptions.save_path); @@ -305,18 +318,27 @@ private void RenderVideo(RenderOptions renderOptions, List finalC int y = 0; List old = new List(displayedGifs); List newly_added = new List(); - for (int j = 0; j < finalComments.Count; j++) + List new_comments = new List(); + + bool isDone = false; + while (finalComments.Count > 0 && !isDone) { - int commentTick = (int)Math.Floor(finalComments[j].secondsOffset / (1.0 / renderOptions.framerate)); + int commentTick = (int)Math.Floor(finalComments.Peek().secondsOffset / (1.0 / renderOptions.framerate)); if (commentTick >= lastUpdateTick && commentTick < globalTick) { - foreach (var emote in finalComments[j].gifEmotes) + TwitchComment currentComment = finalComments.Dequeue(); + foreach (var emote in currentComment.gifEmotes) { GifEmote newGif = new GifEmote(new Point(emote.offset.X, emote.offset.Y + height), emote.name, emote.codec, emote.imageScale, emote.image_frames); displayedGifs.Add(newGif); newly_added.Add(newGif); } - height += SKBitmap.Decode(finalComments[j].section).Height; + height += SKBitmap.Decode(currentComment.section).Height; + new_comments.Add(currentComment); + } + else + { + isDone = true; } } foreach (var emote in old) @@ -335,17 +357,17 @@ private void RenderVideo(RenderOptions renderOptions, List finalC SKCanvas sectionCanvas = new SKCanvas(sectionBitmap); sectionCanvas.Clear(renderOptions.background_color); - for (int j = 0; j < finalComments.Count; j++) + for (int j = 0; j < new_comments.Count; j++) { - int commentTick = (int)Math.Floor(finalComments[j].secondsOffset / (1.0 / renderOptions.framerate)); + int commentTick = (int)Math.Floor(new_comments[j].secondsOffset / (1.0 / renderOptions.framerate)); if (commentTick >= lastUpdateTick && commentTick < globalTick) { - SKBitmap sectionImage = SKBitmap.Decode(finalComments[j].section); + SKBitmap sectionImage = SKBitmap.Decode(new_comments[j].section); sectionCanvas.DrawBitmap(sectionImage, 0, y); - for (int k = 0; k < finalComments[j].normalEmotes.Count; k++) + for (int k = 0; k < new_comments[j].normalEmotes.Count; k++) { - emoteList.Add(finalComments[j].normalEmotes[k]); - SKRect refrenceRect = finalComments[j].normalEmotesPositions[k]; + emoteList.Add(new_comments[j].normalEmotes[k]); + SKRect refrenceRect = new_comments[j].normalEmotesPositions[k]; float top = bufferBitmap.Height - sectionBitmap.Height + y + refrenceRect.Top; emotePos.Add(new SKRect(refrenceRect.Left, top, refrenceRect.Right, top + (refrenceRect.Bottom - refrenceRect.Top))); } @@ -372,7 +394,7 @@ private void RenderVideo(RenderOptions renderOptions, List finalC { float temp_x = (float)emote.offset.X; float temp_y = (float)emote.offset.Y + (int)Math.Floor((renderOptions.text_height - ((emote.image_frames.First().Height / emote.imageScale) * renderOptions.image_scale)) / 2.0); - SKRect copyRect = new SKRect(temp_x, temp_y, temp_x + (float) ((emote.image_frames.First().Width / emote.imageScale) * renderOptions.image_scale), temp_y + (float) ((emote.image_frames.First().Height / emote.imageScale) * renderOptions.image_scale)); + SKRect copyRect = new SKRect(temp_x, temp_y, temp_x + (float)((emote.image_frames.First().Width / emote.imageScale) * renderOptions.image_scale), temp_y + (float)((emote.image_frames.First().Height / emote.imageScale) * renderOptions.image_scale)); emote.backgroundImage = new SKBitmap((int)copyRect.Width, (int)copyRect.Height); using (SKCanvas tempCanvas = new SKCanvas(emote.backgroundImage)) { @@ -410,7 +432,7 @@ private void RenderVideo(RenderOptions renderOptions, List finalC SKBitmap gifBitmap = emote.image_frames[frame]; float x = (float)emote.offset.X; float y = (float)emote.offset.Y + (int)Math.Floor((renderOptions.text_height - ((gifBitmap.Height / emote.imageScale) * renderOptions.image_scale)) / 2.0); - bufferCanvas.DrawBitmap(gifBitmap, new SKRect(x, y, x + (float)((gifBitmap.Width / emote.imageScale) * renderOptions.image_scale), y + (float)((gifBitmap.Height / emote.imageScale) * renderOptions.image_scale)), emotePaint); + bufferCanvas.DrawBitmap(gifBitmap, new SKRect(x, y, x + (float)((gifBitmap.Width / emote.imageScale) * renderOptions.image_scale), y + (float)((gifBitmap.Height / emote.imageScale) * renderOptions.image_scale)), gifPaint); } } @@ -427,7 +449,7 @@ private void RenderVideo(RenderOptions renderOptions, List finalC foreach (var emote in displayedGifs) { SKRect drawRect = new SKRect((float)emote.offset.X, (float)emote.offset.Y + (int)Math.Floor((renderOptions.text_height - ((emote.height / emote.imageScale) * renderOptions.image_scale)) / 2.0), (float)emote.offset.X + (float)((emote.width / emote.imageScale) * renderOptions.image_scale), (float)emote.offset.Y + (int)Math.Floor((renderOptions.text_height - ((emote.height / emote.imageScale) * renderOptions.image_scale)) / 2.0) + (float)((emote.height / emote.imageScale) * renderOptions.image_scale)); - bufferCanvas.DrawBitmap(emote.backgroundImage, drawRect, imagePaint); + bufferCanvas.DrawBitmap(emote.backgroundImage, drawRect, gifBackgroundPaint); } globalTick += 1; double percentDouble = (double)(globalTick - startTick) / (double)(endTick - startTick) * 100.0; @@ -513,7 +535,7 @@ public SKBitmap DrawMessage(SKBitmap sectionImage, List imageList, Ren float emojiLeft = (float)Math.Floor(drawPos.X); float emojiTop = (float)Math.Floor((renderOptions.text_height - emojiSize) / 2.0); - SKRect emojiRect = new SKRect(emojiLeft, emojiTop, emojiLeft+ emojiSize, emojiTop+ emojiSize); + SKRect emojiRect = new SKRect(emojiLeft, emojiTop, emojiLeft + emojiSize, emojiTop + emojiSize); sectionImageCanvas.DrawBitmap(emojiBitmap, emojiRect, imagePaint); drawPos.X += emojiSize + (int)Math.Floor(3 * renderOptions.image_scale); } @@ -616,7 +638,7 @@ public SKBitmap DrawText(SKBitmap sectionImage, string message, SKPaint messageF SKPaint outlinePaint = new SKPaint() { Style = SKPaintStyle.Stroke, StrokeWidth = (float)(4 * renderOptions.image_scale), StrokeJoin = SKStrokeJoin.Round, Color = SKColors.Black, IsAntialias = true, LcdRenderText = true, SubpixelText = true, HintingLevel = SKPaintHinting.Full, FilterQuality = SKFilterQuality.High }; sectionImageCanvas.DrawPath(outlinePath, outlinePaint); } - + sectionImageCanvas.DrawText(message, xPos, yPos, messageFont); } drawPos.X += textWidth + (padding ? (int)Math.Floor(4 * renderOptions.image_scale) : 0); @@ -694,7 +716,7 @@ private bool isNotAscii(char input) { return input > 127; } - + public SKBitmap DrawBadges(SKBitmap sectionImage, List imageList, RenderOptions renderOptions, List chatBadges, Comment comment, Size canvasSize, ref Point drawPos) { if (comment.message.user_badges != null) @@ -845,7 +867,7 @@ private void GetThirdPartyEmotes(List thirdPartyEmotes, Streame bytes = client.DownloadData(String.Format("https://cdn.betterttv.net/emote/{0}/2x", id)); File.WriteAllBytes(fileName, bytes); } - + MemoryStream ms = new MemoryStream(bytes); MemoryStream ms2 = new MemoryStream(bytes); SKBitmap temp_emote = SKBitmap.Decode(ms2); @@ -983,7 +1005,7 @@ private void GetTwitterEmojis(Dictionary emojiCache, List chatEmotes, List co } } catch { } - + if (foundEmote) continue; @@ -1185,9 +1207,9 @@ private void SaveSettings() Settings.Default.FontColorG = colorFont.SelectedColor.Value.G; Settings.Default.FontColorB = colorFont.SelectedColor.Value.B; if (comboFormat.SelectedItem != null) - Settings.Default.VideoContainer = ((VideoContainer) comboFormat.SelectedItem).Name; + Settings.Default.VideoContainer = ((VideoContainer)comboFormat.SelectedItem).Name; if (comboCodec.SelectedItem != null) - Settings.Default.VideoCodec = ((Codec) comboCodec.SelectedItem).Name; + Settings.Default.VideoCodec = ((Codec)comboCodec.SelectedItem).Name; try { Settings.Default.Height = Int32.Parse(textHeight.Text); @@ -1279,7 +1301,7 @@ private void Page_Initialized(object sender, EventArgs e) Codec vp8Codec = new Codec() { Name = "VP8", InputArgs = "-framerate {fps} -f rawvideo -analyzeduration {max_int} -probesize {max_int} -pix_fmt bgra -video_size {width}x{height} -i -", OutputArgs = "-c:v libvpx -crf 18 -b:v 2M -pix_fmt yuva420p -auto-alt-ref 0 \"{save_path}\"" }; Codec vp9Codec = new Codec() { Name = "VP9", InputArgs = "-framerate {fps} -f rawvideo -analyzeduration {max_int} -probesize {max_int} -pix_fmt bgra -video_size {width}x{height} -i -", OutputArgs = "-c:v libvpx-vp9 -crf 18 -b:v 2M -pix_fmt yuva420p \"{save_path}\"" }; Codec rleCodec = new Codec() { Name = "RLE", InputArgs = "-framerate {fps} -f rawvideo -analyzeduration {max_int} -probesize {max_int} -pix_fmt bgra -video_size {width}x{height} -i -", OutputArgs = "-c:v qtrle -pix_fmt argb \"{save_path}\"" }; - VideoContainer mp4Container = new VideoContainer() {Name = "MP4", SupportedCodecs = new List() { h264Codec, h265Codec } }; + VideoContainer mp4Container = new VideoContainer() { Name = "MP4", SupportedCodecs = new List() { h264Codec, h265Codec } }; VideoContainer movContainer = new VideoContainer() { Name = "MOV", SupportedCodecs = new List() { h264Codec, h265Codec, rleCodec } }; VideoContainer webmContainer = new VideoContainer() { Name = "WEBM", SupportedCodecs = new List() { vp8Codec, vp9Codec } }; VideoContainer mkvContainer = new VideoContainer() { Name = "MKV", SupportedCodecs = new List() { h264Codec, h265Codec, vp8Codec, vp9Codec } }; diff --git a/TwitchDownloaderWPF/PageVodDownload.xaml.cs b/TwitchDownloaderWPF/PageVodDownload.xaml.cs index beb954f9..09b2d4a0 100644 --- a/TwitchDownloaderWPF/PageVodDownload.xaml.cs +++ b/TwitchDownloaderWPF/PageVodDownload.xaml.cs @@ -21,6 +21,7 @@ using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.IO; +using TwitchDownloader; using TwitchDownloaderWPF; using Xabe.FFmpeg.Model; using Xabe.FFmpeg; @@ -80,7 +81,7 @@ private async void btnGetInfo_Click(object sender, RoutedEventArgs e) { AppendLog("ERROR: Unable to find thumbnail"); } - + comboQuality.Items.Clear(); videoQualties.Clear(); string[] playlist = taskPlaylist.Result; @@ -130,7 +131,7 @@ private TimeSpan GenerateTimespan(string input) { //There might be a better way to do this, gets string 0h0m0s and returns timespan TimeSpan returnSpan = new TimeSpan(0); - string[] inputArray = input.Remove(input.Length-1).Replace('h', ':').Replace('m', ':').Split(':'); + string[] inputArray = input.Remove(input.Length - 1).Replace('h', ':').Replace('m', ':').Split(':'); returnSpan = returnSpan.Add(TimeSpan.FromSeconds(Int32.Parse(inputArray[inputArray.Length - 1]))); if (inputArray.Length > 1) @@ -169,6 +170,7 @@ private void btnDownload_Click(object sender, RoutedEventArgs e) SetImage("Images/ppOverheat.gif", true); statusMessage.Text = "Downloading"; + backgroundDownloadManager.RunWorkerAsync(options); } } @@ -200,6 +202,7 @@ private void BackgroundDownloadManager_DoWork(object sender, DoWorkEventArgs e) int downloadThreads = options.download_threads; string tempFolder = Path.Combine(Path.GetTempPath(), "TwitchDownloader"); string downloadFolder = Path.Combine(tempFolder, options.id.ToString()); + ServicePointManager.DefaultConnectionLimit = downloadThreads; if (Directory.Exists(downloadFolder)) DeleteDirectory(downloadFolder); @@ -230,39 +233,14 @@ private void BackgroundDownloadManager_DoWork(object sender, DoWorkEventArgs e) int partCount = videoParts.Count; int doneCount = 0; - while (videoParts.Count > 0 || threads.Count > 0) + Parallel.ForEach(videoParts, new ParallelOptions { MaxDegreeOfParallelism = downloadThreads }, + videoPart => { - Thread.Sleep(1000); - - List toRemove = new List(); - foreach (var thread in threads) - { - if (!thread.IsAlive) - toRemove.Add(thread); - } - foreach (var thread in toRemove) - { - threads.Remove(thread); - doneCount++; - - int percent = (int)Math.Floor(((double)doneCount / (double)partCount) * 100); - (sender as BackgroundWorker).ReportProgress(percent, String.Format("Downloading {0}% (1/3)", percent)); - } - - for (int i = threads.Count - 1; i < downloadThreads - 1; i++) - { - if (videoParts.Count > 0) - { - string videoPart = videoParts.Dequeue(); - if (videoPart is string && videoPart != null) - { - Thread thread = new Thread(() => DownloadThread(videoPart, baseUrl, downloadFolder)); - thread.Start(); - threads.Add(thread); - } - } - } - } + DownloadThread(videoPart, baseUrl, downloadFolder); + doneCount++; + int percent = (int)Math.Floor(((double)doneCount / (double)partCount) * 100); + (sender as BackgroundWorker).ReportProgress(percent, String.Format("Downloading {0}% (1/3)", percent)); + }); (sender as BackgroundWorker).ReportProgress(0, "Combining Parts (2/3)"); @@ -400,7 +378,7 @@ private void BackgroundDownloadManager_RunWorkerCompleted(object sender, RunWork { statusMessage.Text = "Done"; SetImage("Images/ppHop.gif", true); - + } else { @@ -487,7 +465,7 @@ private bool ValidateInput() return true; } - private void AppendLog(string message) + public void AppendLog(string message) { textLog.Dispatcher.BeginInvoke((Action)(() => textLog.AppendText(message + Environment.NewLine) @@ -497,7 +475,7 @@ private void AppendLog(string message) private void Page_Initialized(object sender, EventArgs e) { SetEnabled(false); - + WebRequest.DefaultWebProxy = null; numDownloadThreads.Value = Settings.Default.VodDownloadThreads; } @@ -540,7 +518,7 @@ public class DownloadOptions public int download_threads { get; set; } public DownloadOptions() { - + } public void UpdateValues(PageVodDownload currentPage)