using Newtonsoft.Json.Linq;
using System.Diagnostics;
using System.Globalization;
using System.IO.Pipes;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Timers;
//using System.Globalization;
namespace DragonVideoPlayer
{

    public class MpvPlayer
    {
       
        private MpvController _MpvController;
        public string Mpv_argument { get; set; }

        // public MpvController Controller => _mpv;

        public MpvController Controller
        {
            get { return _mpv; }
        }

        private Process _mpvProcess;
        private Process _streamlink;
        private NamedPipeClientStream _pipe;
        private StreamWriter _writer;
        private StreamReader _reader;
        private Thread _readThread;

        private string lastM3u8 = "";
        private MpvController _mpv;
        public event Action<string>? MpvMessageReceived;
        private IntPtr _wid;
        public VideoEffects Video { get; private set; }
        public AudioEffects Audio { get; private set; }




        // request_idごとの待機
        private readonly Dictionary<int, TaskCompletionSource<double>> _pendingRequests
            = new Dictionary<int, TaskCompletionSource<double>>();

        private int _requestIdCounter = 1;

        public MpvPlayer(IntPtr wid)
        {
            _wid = wid;
            

        }

        public void InitEffects()
        {
            Video = new VideoEffects(this);
            Audio = new AudioEffects(this);
        }

        private void ConnectPipe(String PipeName)
        {

            const int maxRetries = 30; // 最大リトライ回数
            const int timeoutMs = 500; // 1回あたりの接続タイムアウト (0.5秒)
            const int sleepMs = 300;   // リトライ間の待機時間 (0.2秒)

            for (int i = 0; i < maxRetries; i++)
            {
                try
                {
                    // パイプのインスタンス生成（接続ごとに新しく作るのが安全）
                    _pipe = new NamedPipeClientStream(".", PipeName, PipeDirection.InOut, PipeOptions.Asynchronous);

                    // 接続試行
                    _pipe.Connect(timeoutMs);

                    // 接続に成功したらループを抜ける
                    if (_pipe.IsConnected)
                    {
                        return;
                    }
                }
                catch (Exception ex) when (ex is TimeoutException || ex is IOException)
                {
                    // 失敗時はリソースを解放してリトライ
                    _pipe?.Dispose();
                    _pipe = null;

                    // 最後の試行でもダメなら例外を投げるかログを出す
                    if (i == maxRetries - 1)
                    {
                        throw new Exception($"mpvのパイプ接続に失敗しました（{maxRetries}回試行）", ex);
                    }

                    // 少し待機してからリトライ
                    System.Threading.Thread.Sleep(sleepMs);
                }
            }
        }
       
        //------------------------------------------------
        // mpv起動
        //------------------------------------------------
        public async Task StartMpv(String url, string args, String pipe_Arguments, String PipeName,String mpv_path,String ytdlp_path, MainForm form1)
        {


            String DVD = "";

            bool steam_get = false;

            if (System.IO.File.Exists(url))
            {

            }
            else
            {

                if (System.Text.RegularExpressions.Regex.IsMatch(url, "https?:", System.Text.RegularExpressions.RegexOptions.IgnoreCase))
                {

                    steam_get = true;
                    if (Regex.IsMatch(url, @"\.m3u8?(\?|$)", RegexOptions.IgnoreCase))
                    {
                        steam_get = false;
                    }


                }
            }

            if (IsPlaylist(url))
            {

                steam_get = false;
            }

            if (System.Text.RegularExpressions.Regex.IsMatch(url, "ytdl://", System.Text.RegularExpressions.RegexOptions.IgnoreCase))
            {
                steam_get = false;
            }

            bool dvd_bool = false;

           
                if (System.Text.RegularExpressions.Regex.IsMatch(url, @"dvd://", System.Text.RegularExpressions.RegexOptions.IgnoreCase))
            {
                dvd_bool = true;

                steam_get = false;

                DVD = "dvd:// --dvd-device=" + config_class.config_text_read("DVDドライブ");

            }
            if (System.Text.RegularExpressions.Regex.IsMatch(url, @"bd://", System.Text.RegularExpressions.RegexOptions.IgnoreCase))
            {
                dvd_bool = true;

                steam_get = false;

                DVD = "bd:// --bluray-device=" + config_class.config_text_read("DVDドライブ");

            }
            if (System.Text.RegularExpressions.Regex.IsMatch(url, @"cdda://", System.Text.RegularExpressions.RegexOptions.IgnoreCase))
            {
                dvd_bool = true;

                steam_get = false;

                DVD = "cdda:// --cdda-device=" + config_class.config_text_read("DVDドライブ");

            }

            if (System.Text.RegularExpressions.Regex.IsMatch(url, @".iso", System.Text.RegularExpressions.RegexOptions.IgnoreCase))
            {
                String drive = url;
                dvd_bool = true;
                steam_get = false;
                DiscKind discType = DiscDetector.DetectBySizeOnly(url);

                if (discType == DiscKind.BluRay)
                {
                    DVD = "bd:// --bluray-device=\"" + drive + "\"";

                }
                if (discType == DiscKind.Dvd)
                {
                    DVD = "dvd:// --dvd-device=\"" + drive + "\"";

                }
                if (discType == DiscKind.AudioCd)
                {
                    DVD = "cdda:// --cdda-device=\"" + drive + "\"";

                }
            }


            if (System.Text.RegularExpressions.Regex.IsMatch(url, @"ytdl://ytsearch", System.Text.RegularExpressions.RegexOptions.IgnoreCase))
            {

                steam_get = false;

            }
            if (steam_get == true)
            {

                url = await GetStreamUrlFromYouTube(url,ytdlp_path);


            }
        
            try
            {

                if (dvd_bool == false)
                {
                    ProcessStartInfo psi = new ProcessStartInfo
                    {
                        FileName = mpv_path,
                        Arguments = $"  -wid={_wid} --screenshot-directory=\"./screenshot\" {pipe_Arguments} {args} \"{url}\"",

                        UseShellExecute = false,
                        CreateNoWindow = true
                    };

                    _mpvProcess = Process.Start(psi);

                    Mpv_argument = psi.Arguments;
                }
                if (dvd_bool == true)
                {
                    ProcessStartInfo psi = new ProcessStartInfo
                    {
                        FileName = mpv_path,
                        Arguments = $"  -wid={_wid} --volume-max=100 --screenshot-directory=\"./screenshot\" {pipe_Arguments} {args} {DVD}",
                        UseShellExecute = false,
                        CreateNoWindow = true
                    };

                    _mpvProcess = Process.Start(psi);

                    Mpv_argument = psi.Arguments;
                }
            
                ConnectPipe(PipeName);



                _writer = new StreamWriter(_pipe) { AutoFlush = true };


                _reader = new StreamReader(_pipe);

                _mpv = new MpvController(this);


                _readThread = new Thread(ReadLoop)
                {
                    IsBackground = true
                };
                _readThread.Start();


            }

            catch (System.Exception ex)
            {
                error.write_error_log(ex.Message + ex.StackTrace);
                Debug.WriteLine(ex.Message + ex.StackTrace);
            }
            InitEffects();


            RegisterObservers();


            if (config_class.config_bool_read("起動時に縮小再生する") == true)
            {
                var str = config_class.config_text_read("縮小する倍率");
                double scale = double.Parse(config_class.config_text_read("縮小する倍率"), CultureInfo.InvariantCulture);

                ZoomChange(scale);

            }
     
       
        }



        private bool IsPlaylist(string url)
        {
            if (System.Text.RegularExpressions.Regex.IsMatch(url, @"youtube.com", System.Text.RegularExpressions.RegexOptions.IgnoreCase) == false)
            {
            }
            else
            {
                return false;
            }
            return url.Contains("list=") ||
                   url.Contains("/videos") ||
                   url.Contains("/@");
        }
        public void Seek(double seconds)
        {

            if (config_class.config_bool_read("シークバーのOSDを有効") == true)
            {
                SendToMpv($"{{\"command\": [\"osd-bar\",\"seek\", {seconds.ToString(CultureInfo.InvariantCulture)}, \"absolute\"]}}");

            }
            else
            {
                SendToMpv($"{{\"command\": [\"seek\", {seconds.ToString(CultureInfo.InvariantCulture)}, \"absolute\"]}}");



            }
        }
  

        public  void abema_cm_reload(String url)
        {
          
            var obj = new
            {
                command = new object[] { "loadfile", url, "replace" }
            };


            if (_pipe != null && _pipe.IsConnected)
                try
                {
                    string json = JsonSerializer.Serialize(obj);
                    _writer.WriteLine(json);
                    SendToMpv(json);
                }
                catch (System.Exception ex)
                {
                    error.write_error_log(ex.Message + ex.StackTrace);
                    Debug.WriteLine(ex.Message + ex.StackTrace);
                }

           
        }

        public void url_replace(String url, Double db_time)
        {
            var obj = new
            {
                command = new object[] { "loadfile", url, "replace" }
            };


            if (_pipe != null && _pipe.IsConnected)
                try
                {
                    string json = JsonSerializer.Serialize(obj);
                    _writer.WriteLine(json);
                    SendToMpv(json);
                }
                catch (System.Exception ex)
                {
                    error.write_error_log(ex.Message + ex.StackTrace);
                    Debug.WriteLine(ex.Message + ex.StackTrace);
                }

            if (config_class.config_bool_read("レジューム機能") == true)
            {





                if (db_time != 0)
                {
                    SendToMpv($"{{\"command\": [\"seek\", {db_time.ToString(CultureInfo.InvariantCulture)}, \"absolute\"]}}");

                }
            }

            if (config_class.config_bool_read("再生開始時に途中から再生する") == true)
            {

                var str = config_class.config_text_read("開始する時間");



                if (double.TryParse(str,
                    System.Globalization.NumberStyles.Float,
                    System.Globalization.CultureInfo.InvariantCulture,
                    out double minutes))
                {
                    double seconds = minutes * 60;

                    SendToMpv($"{{\"command\": [\"seek\", {seconds.ToString(System.Globalization.CultureInfo.InvariantCulture)}, \"absolute\"]}}");
                }

            }
        }


    
        
      
       
       
        public bool IsMpvRunning
        {
            get
            {
                return _mpvProcess != null && !_mpvProcess.HasExited;
            }
        }

        public void SendToMpv(string json)
        {
         
            if (config_class.config_bool_read("mpvのコマンドのログを取る") == true)
                try
                {
                    File.AppendAllText(@"command_log.txt", json + Environment.NewLine);
                }
                catch (System.Exception ex)
                {
                    error.write_error_log(ex.Message + ex.StackTrace);
                    Debug.WriteLine(ex.Message + ex.StackTrace);


                }


            try
            {

                if (_writer != null && _pipe != null && _pipe.IsConnected)
                {
                 
                    _writer.WriteLine(json);
                    _writer.Flush();
                }

            }
            catch (System.Exception ex)
            {
                Debug.WriteLine(ex.Message + ex.StackTrace);
                error.write_error_log(ex.Message + ex.StackTrace);


            }
        }



        //------------------------------------------------
        // pipe接続
        //------------------------------------------------



        //------------------------------------------------
        // 読み取りスレッド
        //------------------------------------------------
        private void RegisterObservers()
        {

            SendToMpv("{\"command\": [\"observe_property\", 1, \"time-pos\"]}");
            SendToMpv("{\"command\": [\"observe_property\", 2, \"duration\"]}");
            SendToMpv("{\"command\": [\"observe_property\", 3, \"seekable\"]}");
            SendToMpv("{\"command\": [\"observe_property\", 4, \"width\"]}");
            SendToMpv("{\"command\": [\"observe_property\", 5, \"height\"]}");
            SendToMpv("{\"command\": [\"observe_property\", 6, \"file-size\"]}");
            SendToMpv("{\"command\": [\"observe_property\", 7, \"media-title\"]}");
            SendToMpv("{\"command\": [\"observe_property\", 8, \"video-codec\"]}");
            SendToMpv("{\"command\": [\"observe_property\", 9, \"audio-codec\"]}");
            SendToMpv("{\"command\": [\"observe_property\", 10, \"filename\"]}");
            SendToMpv("{\"command\": [\"observe_property\", 11, \"video-bitrate\"]}");
            SendToMpv("{\"command\": [\"observe_property\", 12, \"container-fps\"]}");
            SendToMpv("{\"command\": [\"observe_property\", 13, \"estimated-vf-fps\"]}");
            SendToMpv("{\"command\": [\"observe_property\", 14, \"audio-bitrate\"]}");

            SendToMpv("{\"command\": [\"observe_property\", 15, \"end-file\"]}");
            SendToMpv("{\"command\": [\"observe_property\", 16, \"time-remaining\"]}");
            SendToMpv("{\"command\": [\"observe_property\", 17, \"playlist\"]}");
            SendToMpv("{\"command\": [\"observe_property\", 18, \"track-list\"]}");
            SendToMpv("{\"command\": [\"observe_property\", 19, \"chapter-list\"]}");
            SendToMpv("{\"command\": [\"observe_property\", 20, \"mute\"]}");
            SendToMpv("{\"command\": [\"observe_property\", 21, \"sub-delay\"]}");


        }

        //------------------------------------------------
        // mpvメッセージ読み取り
        //------------------------------------------------
        private void ReadLoop()
        {
            // 受信バッファ用
            byte[] buffer = new byte[8192];
            StringBuilder sb = new StringBuilder();

            while (true)
            {
                try
                {
                    if (_pipe == null || !_pipe.IsConnected)
                    {
                        Thread.Sleep(100);
                        continue;
                    }

                    // Pipe から直接バイトを読み取る
                    int bytesRead = _pipe.Read(buffer, 0, buffer.Length);
                    if (bytesRead == 0) continue;

                    // 文字列に変換して StringBuilder に蓄積
                    sb.Append(Encoding.UTF8.GetString(buffer, 0, bytesRead));

                    string currentContent = sb.ToString();

                    // mpv の IPC は各メッセージが \n (改行) で区切られている
                    int newlineIndex;
                    while ((newlineIndex = currentContent.IndexOf('\n')) >= 0)
                    {
                        // 1行分取り出す
                        string line = currentContent.Substring(0, newlineIndex).Trim();

                        // 残りの文字列を保持
                        currentContent = currentContent.Substring(newlineIndex + 1);

                        if (!string.IsNullOrEmpty(line))
                        {
                            // 解析処理へ（非同期で投げるとよりスムーズ）
                            HandleMpvMessage(line);
                        }
                    }

                    // StringBuilder を更新
                    sb.Clear();
                    sb.Append(currentContent);
                }
                catch (System.Exception ex)
                {
                    error.write_error_log(ex.Message + ex.StackTrace);
                    Debug.WriteLine("Read Error: " + ex.Message + ex.StackTrace);
                    break;
                }
            }



        }

        //------------------------------------------------
        // mpvメッセージ処理
        //------------------------------------------------
        private void HandleMpvMessage(string json)
        {

            MpvMessageReceived?.Invoke(json);

        }

        //------------------------------------------------
        // youtube stream URL取得
        //------------------------------------------------
        private async Task<string> GetStreamUrlFromYouTube(string url,String ytdlp_path)
        {
            String login_text = "";
            if (config_class.config_bool_read("yt-dlpでログインする") == true)
            {

                if (config_class.config_int_read("ログイン方法") == 1)
                {
                    login_text = " --cookies-from-browser firefox";
                }
                if (config_class.config_int_read("ログイン方法") == 2)
                {
                    login_text = " --cookies-from-browser Chrome";
                }
                if (config_class.config_int_read("ログイン方法") ==3)
                {
                    if (System.IO.File.Exists("cookies.txt"))
                    {
                        login_text = " --cookies cookies.txt";
                    }
                }

            }

            try
            {
                var psi = new ProcessStartInfo
                {
                    FileName =ytdlp_path,
                    Arguments = $"-f best -g \"{url}\""+ login_text,
                    RedirectStandardOutput = true,
                    RedirectStandardError = true, // ★重要
                    UseShellExecute = false,
                    CreateNoWindow = true
                };

                using (var process = new Process())
                {
                    process.StartInfo = psi;
                    process.Start();

                    var outputTask = process.StandardOutput.ReadToEndAsync();
                    var errorTask = process.StandardError.ReadToEndAsync();

                    // ★ タイムアウト付き待機
                    var waitTask = Task.Run(() => process.WaitForExit());
                    var timeoutTask = Task.Delay(5000);

                    if (await Task.WhenAny(waitTask, timeoutTask) != waitTask)
                    {
                        try { process.Kill(); }
                        catch (Exception ex)
                        {

                            Debug.WriteLine(ex.Message + ex.StackTrace);
                        }
                        return null;
                    }

                    var output = await outputTask;
                    var error = await errorTask;

                    if (string.IsNullOrWhiteSpace(output))
                    {
                        Debug.WriteLine("yt-dlp error: " + error);
                        return null;
                    }

                    return output.Trim();
                }
            }
            catch (Exception ex)
            {
                error.write_error_log(ex.Message + ex.StackTrace);
                Debug.WriteLine(ex.Message + ex.StackTrace);
                return null;
            }


        }


        public void StopMpv(String currentFilePath)
        {
            try
            {
                if (_pipe != null && _pipe.IsConnected)
                {
                    if (config_class.config_bool_read("MPV標準機能でレジューム") == true)
                    {
                        if (System.Text.RegularExpressions.Regex.IsMatch(currentFilePath, config_class.config_text_read("streamlinkで取得するURLの正規表現"), System.Text.RegularExpressions.RegexOptions.IgnoreCase) ||
                System.Text.RegularExpressions.Regex.IsMatch(currentFilePath, @"\.m3u8?(\?|$)", RegexOptions.IgnoreCase))
                        {
                        }
                        else
                        {

                            SendToMpv("{\"command\": [\"write-watch-later-config\"]}");
                        }
                    }
                    SendToMpv("{\"command\": [\"stop\"]}");
                    SendToMpv("{\"command\": [\"quit\"]}");
                }
            }
            catch { }

            try
            {
                _writer?.Dispose();
                _reader?.Dispose();
                _pipe?.Dispose();
            }
            catch { }

            try
            {
                if (_mpvProcess != null && !_mpvProcess.HasExited)
                    _mpvProcess.Kill();
            }
            catch { }

            try
            {
                if (_streamlink != null && !_streamlink.HasExited)
                    _streamlink.Kill();
            }
            catch { }
        }


        public void ChangeSpeed_no_osd(double delta)
        {
            var val = delta.ToString(System.Globalization.CultureInfo.InvariantCulture);



            SendToMpv($"{{\"command\": [\"set_property\", \"speed\", {val}]}}");

               }
        public void ChangeSpeed(double delta)
        {
            var val = delta.ToString(System.Globalization.CultureInfo.InvariantCulture);



            SendToMpv($"{{\"command\": [\"set_property\", \"speed\", {val}]}}");
            if (config_class.config_bool_read("OSDを有効") == true)
            {
                SendToMpv($"{{\"command\": [\"show-text\", \"speed{val} \",1000]}}");
            }
            }

        public void Set_mutli_audio(int val)
        {


            SendToMpv($"{{\"command\": [\"set_property\", \"aid\", {val}]}}");


        }
        public void Set_mutli_sub(int val)
        {


            SendToMpv($"{{\"command\": [\"set_property\", \"sid\", {val}]}}");


        }
        public void Set_mutli_sub_no()
        {


            SendToMpv($"{{\"command\": [\"set_property\", \"sid\",  \"no\"]}}");

        }

        public void ChangeChapter(int val)
        {


            SendToMpv($"{{\"command\": [\"set_property\", \"chapter\", {val}]}}");


        }
        public void FrameStep()
        {


            SendToMpv("{\"command\": [\"frame-step\"]}");


        }
        public void pan_x_plus()
        {
            SendToMpv("{\"command\": [\"add\", \"video-pan-x\", 0.05]}");

        }
        public void pan_x_minus()
        {
            SendToMpv("{\"command\": [\"add\", \"video-pan-x\", -0.05]}");

        }
        public void pan_y_plus()
        {
            SendToMpv("{\"command\": [\"add\", \"video-pan-y\", 0.05]}");

        }
        public void sub_srt_zoom(Double value)
        {

            SendToMpv($"{{\"command\": [\"set_property\", \"sub-scale\", {value}]}}");

        }
        public void sub_delay_up()
        {
            SendToMpv("{\"command\": [\"osd-msg\", \"add\", \"sub-delay\", 0.5]}");


        }
        public void sub_delay_down()
        {
            SendToMpv("{\"command\": [\"osd-msg\", \"add\", \"sub-delay\", -0.5]}");


        }

        public void sub_pos_y(int value)
        {

            SendToMpv($"{{\"command\": [\"set_property\", \"sub-pos\", {value}]}}");

        }
        public void pan_y_minus()
        {
            SendToMpv("{\"command\": [\"add\", \"video-pan-y\", -0.05]}");

        }

        public void ZoomNormal()
        {

            SendToMpv("{\"command\": [\"add\", \"video-zoom\", 0]}");





        }
        public void ZoomChange(Double value)
        {
            SendToMpv($"{{\"command\": [\"add\", \"video-zoom\", {value}]}}");







        }
        public void audio_pitch_correction()
        {
            SendToMpv($"{{\"command\": [\"set_property\", \"audio-pitch-correction\", true]}}");
        }
        public void VolumeChange(int vol)
        {


            SendToMpv($"{{\"command\": [\"set_property\", \"volume\", {vol}]}}");





        }
        public void VolumeUp()
        {


            SendToMpv("{\"command\": [\"osd-msg-bar\", \"add\", \"volume\", 5]}");





        }
        public void VolumeDown()
        {


            SendToMpv("{\"command\": [\"osd-msg-bar\", \"add\", \"volume\", -5]}");





        }
        public void ZoomDown()
        {

            SendToMpv("{\"command\": [\"add\", \"video-zoom\", -0.1]}");





        }
        public void stats_script_binding(String page)
        {
            SendToMpv("{\"command\": [\"set\", \"options/script-opts\", \"stats-font_size=30\"]}");

            SendToMpv(
         $"{{\"command\": [\"script-binding\", \"stats/display-page-{page}\"]}}"
     );

        }
        public void ZoomUp()
        {

            SendToMpv("{\"command\": [\"add\", \"video-zoom\", 0.1]}");





        }
        public void FrameBackStep()
        {

            SendToMpv("{\"command\": [\"frame-back-step\"]}");


        }

        public void Screenshot()
        {
            SendToMpv("{\"command\": [\"screenshot\"]}");
        }
        //フルスクリーン、解除後にmpvに「サイズ更新」を強制する

        public void ToggleMute()
        {
            
         SendToMpv("{\"command\": [\"cycle\", \"mute\"]}");

        }

       
        public void audio_delay_down()
        {

            SendToMpv("{\"command\": [\"osd-msg\", \"add\", \"audio-delay\", -0.1]}");

        }
        public void audio_delay_up()
        {
            SendToMpv("{\"command\": [\"osd-msg\", \"add\", \"audio-delay\", 0.1]}");
        
        }
        public void show_text(String value)
		{
            if (config_class.config_bool_read("OSDを有効") == true)
            {
                SendToMpv($"{{\"command\": [\"show-text\", \"{value}\",1000]}}");
            }
     
        }


        public void TogglePause()
        {

            SendToMpv("{\"command\": [\"cycle\", \"pause\"]}");
        }
        public void NextSeek(double value)
        {



            if (config_class.config_bool_read("シークバーのOSDを有効") == true)
            {
                SendToMpv($"{{\"command\": [\"osd-bar\",\"seek\", {value}, \"relative\"]}}");

            }
            else
            {
                SendToMpv($"{{\"command\": [\"seek\", {value}, \"relative\"]}}");


            }



        }

        public void PrevSeek(double value)
        {

            if (config_class.config_bool_read("シークバーのOSDを有効") == true)
            {
                SendToMpv($"{{\"command\": [\"osd-bar\",\"seek\", {-value}, \"relative\"]}}");

            }
            else
            {
                SendToMpv($"{{\"command\": [\"seek\", {-value}, \"relative\"]}}");


            }

        }
        //------------------------------------------------
        // stream再生
        //------------------------------------------------



        public async Task StartStream(string url, String args, String Pipeargs, String PipeName,String mpv_path, String streamlink_path)
        {

            string mpvArgs =
$"--force-window=yes --volume-max=100 --screenshot-directory=\"./screenshot\" " +
$"-wid={_wid}{Pipeargs}" +
$"{args}";
            Debug.WriteLine(mpvArgs);
            // 1. mpvの起動設定 (標準入力を受け付ける設定)
            // - : 標準入力を再生する指定
            // --demuxer-lavf-format=hls : ストリーム形式を明示（任意）
            //  string mpvArgs = $"--force-window=yes --wid={_wid} --input-ipc-server=\\\\.\\pipe\\{PipeName}";
            // mpv 起動
            try
            {
                ProcessStartInfo mpvPsi = new ProcessStartInfo
                {
                    FileName = mpv_path,
                    Arguments = $"- {mpvArgs}",
                    RedirectStandardInput = true,
                    UseShellExecute = false,
                    CreateNoWindow = true
                };
                Mpv_argument = mpvPsi.Arguments;
                _mpvProcess = Process.Start(mpvPsi);

                // streamlink 起動
                ProcessStartInfo streamPsi = new ProcessStartInfo
                {
                    FileName = streamlink_path,
                    Arguments = $"\"{url}\" best --stdout",
                    RedirectStandardOutput = true,
                    UseShellExecute = false,
                    CreateNoWindow = true
                };

                _streamlink = Process.Start(streamPsi);

                // データ転送
                Task.Run(async () =>
                {
                    await _streamlink.StandardOutput.BaseStream.CopyToAsync(
                       _mpvProcess.StandardInput.BaseStream
                    );

                    _mpvProcess.StandardInput.Close();
                });
                // 4. IPCパイプへの接続

                ConnectPipe(PipeName);
                _writer = new StreamWriter(_pipe) { AutoFlush = true };


                _reader = new StreamReader(_pipe);

                _mpv = new MpvController(this);

                //_mpv.Connect(PipeName);
                _readThread = new Thread(ReadLoop)
                {
                    IsBackground = true
                };
                _readThread.Start();


            }

            catch (System.Exception ex)
            {
                error.write_error_log(ex.Message + ex.StackTrace);
                Debug.WriteLine(ex.Message + ex.StackTrace);
            }

            InitEffects();


            RegisterObservers();



        }
        public void StopStream()
        {
            try { _streamlink?.Kill(); } catch { }
            try { _mpvProcess?.Kill(); } catch { }
        }


    }
}