public async Task <bool> CreateVideoAsync(IProgress <VideoProgress> progress, CancellationToken ct) { return(await Task.Run(async() => { var video = await TwitchDownloader.DownloadVideoInfoAsync(ID, progress, ct); if (video?.ID == null) { return false; } var bits = await TwitchDownloader.DownloadBitsAsync(video.StreamerID, progress, ct); var badges = await TwitchDownloader.DownloadBadgesAsync(video.StreamerID, progress, ct); var bttv = await BTTV.CreateAsync(video.StreamerID, progress, ct); var ffz = await FFZ.CreateAsync(video.Streamer, progress, ct); var messages = await TwitchDownloader.GetChatAsync(ID, video.Duration, progress, ct); if (ct.IsCancellationRequested) { return false; } using (var chat_handler = new ChatHandler(this, bttv, ffz, badges, bits)) { int current = 0; try { var drawables = messages?.Select(m => { progress?.Report(new VideoProgress(++current, messages.Count, VideoProgress.VideoStatus.Drawing)); return chat_handler.MakeDrawableMessage(m); }).ToList(); var max = (int)(FPS *video.Duration); var path = string.Format("{0}{1}-{2}.mp4", OutputDirectory, video.Streamer, video.ID); var result = await WriteVideoFrames(path, drawables, 0, max, progress, ct); progress?.Report(new VideoProgress(1, 1, VideoProgress.VideoStatus.CleaningUp)); drawables.ForEach(d => d.Lines.ForEach(l => l.Drawables.ForEach(dr => dr.Dispose()))); return result; } catch (Exception e) { using (StreamWriter w = File.AppendText("error.txt")) { w.WriteLine($"{DateTime.Now.ToLongTimeString()} : {e.ToString()}"); } return false; }; } })); }
public ChatHandler(ChatVideo cv, BTTV bttv, FFZ ffz, Badges badges, Bits bits) { Font = (Font)cv.Font.Clone(); BoldFont = new Font(Font, FontStyle.Bold); ChatColor = Color.FromArgb(cv.ChatColor.A, cv.ChatColor.R, cv.ChatColor.G, cv.ChatColor.B); BGColor = Color.FromArgb(cv.BGColor.A, cv.BGColor.R, cv.BGColor.G, cv.BGColor.B); Width = cv.Width; Spacing = cv.LineSpacing; ShowBadges = cv.ShowBadges; BTTV = bttv; FFZ = ffz; Badges = badges; Bits = bits; }
public DrawableMessage MakeDrawableMessage(ChatMessage message) { var lines = new List <Line>(); // \p{Cs} or \p{Surrogate}: one half of a surrogate pair in UTF-16 encoding. var words = Regex.Replace(message.Text, @"\p{Cs}\p{Cs}", m => { message.Emotes?.Where(e => e.Begin > m.Index)?.ToList().ForEach(e2 => e2.Begin += 1); return(Emoji.Exists(m.Value) ? ' ' + m.Value + ' ' : "?"); }).Split(' ').Where(s => s != string.Empty); var builder = new StringBuilder(); float x = ChatVideo.HorizontalPad; float y = 0; float maximum_y_offset = 0; int cursor = 0; List <Drawable> dl = new List <Drawable>(); var user_tag = message.Name + ": "; var user_size = MeasureText(user_tag, BoldFont, (int)Width); if (ShowBadges) { message.Badges?.ForEach(b => { var img = Badges?.Lookup(b.ID, b.Version); if (img != null) { var align_vertical = user_size.Height * .5f - img.Height * .5f; if (align_vertical < 0) { maximum_y_offset = Math.Max(Math.Abs(align_vertical), maximum_y_offset); } var badge = new Badge(x, align_vertical, img); dl.Add(badge); x += img.Width + ChatVideo.BadgePad; } }); } var color = Colors.GetCorrected(message.Color, BGColor, message.Name); dl.Add(new User(x, y, user_size.Height, user_tag, color, BoldFont)); x += user_size.Width; Action new_line = delegate { y += maximum_y_offset; var line = new Line(ChatVideo.HorizontalPad, y, Font.Height + maximum_y_offset, dl); lines.Add(line); y += maximum_y_offset + line.Height; maximum_y_offset = 0; dl = new List <Drawable>(); x = ChatVideo.HorizontalPad; }; Action empty_builder = delegate { var txt = builder.ToString(); var sz = MeasureText(txt, Font, (int)Width); dl.Add(new Text(x, 0, sz.Height, txt, ChatColor, Font)); builder.Clear(); x += sz.Width; }; foreach (var word in words) { var emote = Emoji.GetEmoji(word) ?? FFZ?.GetEmote(word) ?? BTTV?.GetEmote(word) ?? TwitchEmote.GetEmote(message.Emotes?.FirstOrDefault(e => e.Begin == cursor)?.ID); cursor += word.Length + 1; if (emote != null) { if (builder.Length > 0) { builder.Append(' '); empty_builder(); } if (x + emote.Width + 2 * ChatVideo.HorizontalPad > Width) { new_line(); } var align_vertical = Font.Height * .5f - emote.Height * .5f; if (align_vertical < 0) { maximum_y_offset = Math.Max(Math.Abs(align_vertical), maximum_y_offset); } x += ChatVideo.EmotePad; dl.Add(new Emote(x, align_vertical, emote)); x += emote.Width; continue; } var cheer = message.Content.Bits > 0 ? Bits?.GetCheer(word) : default(Bits.Cheer); if (cheer?.Amount > 0) { var ch = cheer.Value; if (builder.Length > 0) { builder.Append(' '); empty_builder(); } if (x + ch.Emote.Width + 2 * ChatVideo.HorizontalPad > Width) { new_line(); } var align_vertical = Font.Height * .5f - ch.Emote.Height * .5f; if (align_vertical < 0) { maximum_y_offset = Math.Max(Math.Abs(align_vertical), maximum_y_offset); } x += ChatVideo.EmotePad; dl.Add(new Emote(x, align_vertical, ch.Emote)); x += ch.Emote.Width; var cheer_amount = cheer?.Amount.ToString(); var sz = MeasureText(cheer_amount, Font, (int)Width); if (x + sz.Width + 2 * ChatVideo.HorizontalPad >= Width) { new_line(); } dl.Add(new Text(x, 0, sz.Height, cheer_amount, ch.Color, Font)); x += sz.Width; continue; } var text = builder.ToString(); var word_tag = word + ' '; var size = MeasureText(text + word_tag, Font, (int)Width); if (x + size.Width + 2 * ChatVideo.HorizontalPad >= Width) { empty_builder(); new_line(); builder.Append(word_tag); } else { builder.Append(word_tag); } } if (builder.Length > 0) { var text = builder.ToString(); var size = MeasureText(text, Font, (int)Width); dl.Add(new Text(x, 0, size.Height, text, ChatColor, Font)); } if (dl.Count > 0) { lines.Add(new Line(ChatVideo.HorizontalPad, y + maximum_y_offset, Font.Height + maximum_y_offset, dl)); } return(new DrawableMessage { Lines = lines, StartFrame = (uint)(message.TimeOffset * ChatVideo.FPS), Live = message.Source == "chat", }); }