using System; using System.Diagnostics; using System.IO; using System.Windows.Forms; using TweetDuck.Core.Controls; using TweetDuck.Core.Utils; using TweetLib.Communication; namespace TweetDuck.Core.Other.Management{ sealed class VideoPlayer : IDisposable{ private readonly string PlayerExe = Path.Combine(Program.ProgramPath, "TweetDuck.Video.exe"); public bool Running{ get{ if (currentProcess == null){ return false; } currentProcess.Refresh(); return !currentProcess.HasExited; } } public event EventHandler ProcessExited; private readonly Form owner; private string lastUrl; private string lastUsername; private Process currentProcess; private DuplexPipe.Server currentPipe; private bool isClosing; public VideoPlayer(Form owner){ this.owner = owner; this.owner.FormClosing += owner_FormClosing; } public void Launch(string url, string username){ if (Running){ Destroy(); isClosing = false; } lastUrl = url; lastUsername = username; try{ currentPipe = DuplexPipe.CreateServer(); currentPipe.DataIn += currentPipe_DataIn; if ((currentProcess = Process.Start(new ProcessStartInfo{ FileName = PlayerExe, Arguments = $"{owner.Handle} {Program.UserConfig.VideoPlayerVolume} \"{url}\" \"{currentPipe.GenerateToken()}\"", UseShellExecute = false, RedirectStandardOutput = true })) != null){ currentProcess.EnableRaisingEvents = true; currentProcess.Exited += process_Exited; #if DEBUG currentProcess.BeginOutputReadLine(); currentProcess.OutputDataReceived += (sender, args) => Debug.WriteLine("VideoPlayer: "+args.Data); #endif } currentPipe.DisposeToken(); }catch(Exception e){ Program.Reporter.HandleException("Video Playback Error", "Error launching video player.", true, e); } } public void SendKeyEvent(Keys key){ currentPipe?.Write("key", ((int)key).ToString()); } private void currentPipe_DataIn(object sender, DuplexPipe.PipeReadEventArgs e){ owner.InvokeSafe(() => { switch(e.Key){ case "vol": if (int.TryParse(e.Data, out int volume) && volume != Program.UserConfig.VideoPlayerVolume){ Program.UserConfig.VideoPlayerVolume = volume; Program.UserConfig.Save(); } break; case "download": TwitterUtils.DownloadVideo(lastUrl, lastUsername); break; case "rip": currentPipe.Dispose(); currentPipe = null; currentProcess.Dispose(); currentProcess = null; isClosing = false; TriggerProcessExitEventUnsafe(); break; } }); } public void Close(){ if (currentProcess != null){ if (isClosing){ Destroy(); isClosing = false; } else{ isClosing = true; currentProcess.Exited -= process_Exited; currentPipe.Write("die"); } } } public void Dispose(){ ProcessExited = null; isClosing = true; Destroy(); } private void Destroy(){ if (currentProcess != null){ try{ currentProcess.Kill(); }catch{ // kill me instead then } currentProcess.Dispose(); currentProcess = null; currentPipe.Dispose(); currentPipe = null; TriggerProcessExitEventUnsafe(); } } private void owner_FormClosing(object sender, FormClosingEventArgs e){ if (currentProcess != null){ currentProcess.Exited -= process_Exited; } } private void process_Exited(object sender, EventArgs e){ int exitCode = currentProcess.ExitCode; currentProcess.Dispose(); currentProcess = null; currentPipe.Dispose(); currentPipe = null; switch(exitCode){ case 3: // CODE_LAUNCH_FAIL if (FormMessage.Error("Video Playback Error", "Error launching video player, this may be caused by missing Windows Media Player. Do you want to open the video in your browser?", FormMessage.Yes, FormMessage.No)){ BrowserUtils.OpenExternalBrowser(lastUrl); } break; case 4: // CODE_MEDIA_ERROR if (FormMessage.Error("Video Playback Error", "The video could not be loaded, most likely due to unknown format. Do you want to open the video in your browser?", FormMessage.Yes, FormMessage.No)){ BrowserUtils.OpenExternalBrowser(lastUrl); } break; } owner.InvokeAsyncSafe(TriggerProcessExitEventUnsafe); } private void TriggerProcessExitEventUnsafe(){ ProcessExited?.Invoke(this, EventArgs.Empty); } } }