public void Process(MediaConversionOptions aOptions)
        {
            Directory.CreateDirectory(IntermediateEpisodeFolder);
            Directory.CreateDirectory(ReadyToEditEpisodeFolder);

            List <Task> tasks = new List <Task>();

            if (!aOptions.disableSubtitles)
            {
                foreach (var subtitleTrack in mSubtitleInfos)
                {
                    tasks.Add(Task.Run(() => ProcessTrack(subtitleTrack, aOptions)));
                }
            }

            if (!aOptions.disableAudio)
            {
                foreach (var audioTrack in mAudioInfos)
                {
                    tasks.Add(Task.Run(() => ProcessTrack(audioTrack, aOptions)));
                }
            }

            if (!aOptions.disableVideo)
            {
                foreach (var videoTrack in mVideoInfos)
                {
                    tasks.Add(Task.Run(() => ProcessTrack(videoTrack, aOptions)));
                }
            }

            Task.WaitAll(tasks.ToArray());
        }
        public MkvInfo(string aFile, List <string> aTracks, MediaConversionOptions aOptions)
        {
            mVideoInfos    = new List <VideoInfo>();
            mAudioInfos    = new List <AudioInfo>();
            mSubtitleInfos = new List <SubtitleInfo>();

            FileName    = aFile;
            EpisodeName = Path.GetFileNameWithoutExtension(aFile);
            IntermediateEpisodeFolder = Path.Combine(aOptions.rootPath, "2_Intermediate", EpisodeName);
            ReadyToEditEpisodeFolder  = Path.Combine(aOptions.rootPath, "3_ReadyToEdit", EpisodeName);
            IntermediateFilePath      = Path.Combine(IntermediateEpisodeFolder, Path.GetFileNameWithoutExtension(aFile));
            FinalFilePath             = Path.Combine(ReadyToEditEpisodeFolder, Path.GetFileNameWithoutExtension(aFile));

            foreach (var track in aTracks)
            {
                // Get Track properties
                var properties = track.Split("|  + ").ToList();
                properties.RemoveAt(0); //Empty Line

                if (properties[2].StartsWith("Track type: video"))
                {
                    mVideoInfos.Add(new VideoInfo(properties));
                }
                else if (properties[2].StartsWith("Track type: audio"))
                {
                    mAudioInfos.Add(new AudioInfo(properties));
                }
                else if (properties[2].StartsWith("Track type: subtitles"))
                {
                    mSubtitleInfos.Add(new SubtitleInfo(properties));
                }
            }
        }
        ////////////////////////////////////////////////////////////////
        // Subtitles
        void ProcessTrack(SubtitleInfo aTrack, MediaConversionOptions aOptions)
        {
            ////////////////////////////////////////////////////////////////
            // Extract sup files
            var supPath = $"{IntermediateFilePath}_Track_{aTrack.mMediaInfo.TrackNumber}.sup";

            RunProcess("mkvextract", $"\"{FileName}\" tracks {aTrack.mMediaInfo.TrackNumber}:{supPath}").WaitAndCheck();

            ////////////////////////////////////////////////////////////////
            // Convert sup files to SRT
            if (!aOptions.disableSubtitleConversion)
            {
                string pgsApp  = "\"C:\\Program Files\\PgsToSrt\\PgsToSrt.dll\"";
                string srtFile = Path.ChangeExtension(supPath, ".srt").Replace("2_Intermediate", "3_ReadyToEdit");
                RunProcess("dotnet", $"{pgsApp} --input {supPath} --output {srtFile} --tesseractlanguage eng").WaitAndCheck();
            }
        }
        ////////////////////////////////////////////////////////////////
        // Video
        void ProcessTrack(VideoInfo aTrack, MediaConversionOptions aOptions)
        {
            var codecSanitized = $"({aTrack.mMediaInfo.TrackCodec.Replace('\\', '/').Split('/').Join('_')})";
            var langSanitized  = $"({aTrack.mMediaInfo.Language.Replace('\\', '/').Split('/').Join('_')})";

            ////////////////////////////////////////////////////////////////
            // Extract our video track (id: 0 all files)
            var mkvPath = $"{IntermediateFilePath}_Lang_{langSanitized}_Codec_{codecSanitized}_Track_{aTrack.mMediaInfo.TrackNumber}.mkv";

            RunProcess("mkvmerge", $"-o {mkvPath} --no-audio --no-subtitles {FileName}").WaitAndCheck();

            ////////////////////////////////////////////////////////////////
            // Convert MKV to MP4
            if (!aOptions.disableVideoConversion)
            {
                var mp4Path = $"{FinalFilePath}_Lang_{aTrack.mMediaInfo.Language}_Codec_{codecSanitized}_Track_{aTrack.mMediaInfo.TrackNumber}.mp4";
                RunProcess("ffmpeg", $"-i {mkvPath} -c copy {mp4Path}").WaitAndCheck();
            }
        }
        ////////////////////////////////////////////////////////////////
        // Audio
        void ProcessTrack(AudioInfo aTrack, MediaConversionOptions aOptions)
        {
            var codecSanitized = $"({aTrack.mMediaInfo.TrackCodec.Replace('\\', '/').Split('/').Join('_')})";
            var langSanitized  = $"({aTrack.mMediaInfo.Language.Replace('\\', '/').Split('/').Join('_')})";
            var codec          = cBlurayCodecToOutputCodec[aTrack.mMediaInfo.TrackCodec];

            ////////////////////////////////////////////////////////////////
            // Extract out Bluray track
            var blurayAudioPath = $"{IntermediateFilePath}_Lang_{langSanitized}_Codec_{codecSanitized}_Track_{aTrack.mMediaInfo.TrackNumber}.truehd";

            RunProcess("mkvextract", $"\"{FileName}\" tracks {aTrack.mMediaInfo.TrackNumber}:{blurayAudioPath}").WaitAndCheck();

            ////////////////////////////////////////////////////////////////
            // Convert Bluray audio tracks to FLAC
            if (!aOptions.disableAudioConversion)
            {
                var flacPath = Path.ChangeExtension(blurayAudioPath, ".flac").Replace("2_Intermediate", "3_ReadyToEdit");
                RunProcess("ffmpeg", $"-analyzeduration 30000000 -acodec {codec} -i {blurayAudioPath} -vn -sn -acodec flac {flacPath}").WaitAndCheck();
            }
        }
        static MkvInfo GetMkvInfo(string aFile, MediaConversionOptions aOptions)
        {
            var builder = new StringBuilder();
            var process = MkvInfo.RunProcess("mkvinfo", aFile, builder);

            process.WaitForExit();
            var tracks = builder
                         .ToString()
                         .Split("\r\n")
                         .ToList()
                         .RemoveAllOf(line => line.StartsWith("| + EBML void: "))
                         .Join()
                         .Split("|+ ")
                         .First(line => line.StartsWith("Tracks"))
                         .Split("| + Track\r\n")
                         .ToList();

            // First one is just the beginning of the track section.
            tracks.RemoveAt(0);

            return(new MkvInfo(aFile, tracks, aOptions));
        }
        static void Main(string[] args)
        {
            //var files = GetFileList();
            //
            //Console.WriteLine($"File Count: {files.Count}");
            //Console.WriteLine($"Files");
            //
            //var changedNames = new List<string>();
            //
            //foreach (var episode in Enumerable.Range(1, files.Count()))
            //    changedNames.Add(files[episode - 1].Replace(Path.GetFileName(files[episode - 1]), cNameRemapper[episode]));
            //
            //var filesToProcess = new List<string>();
            //
            //foreach (var episode in Enumerable.Range(0, files.Count()))
            //{
            //    var normalizedFileName = Path.GetFileName(changedNames[episode]).Replace(" - ", "-").Replace(' ', '_');
            //    filesToProcess.Add(Path.Combine("D:\\BlackCloverFanEdit\\1_ToBeProcessed\\", normalizedFileName));
            //
            //    var copyingTo = Path.Combine("D:\\Kodi\\TV\\Black Clover", Path.GetFileName(changedNames[episode]));
            //    Console.WriteLine($"    Processing: {files[episode]}");
            //    Console.WriteLine($"        Copying file from {files[episode]} to {copyingTo}");
            //    Directory.CreateDirectory(Path.GetDirectoryName(copyingTo));
            //    Directory.CreateDirectory(Path.GetDirectoryName(filesToProcess.Back()));
            //    File.Copy(files[episode], copyingTo);
            //
            //    Console.WriteLine($"        Moving file from {files[episode]} to {filesToProcess.Back()}");
            //    File.Move(files[episode], filesToProcess.Back());
            //}
            // these are the available options, note that they set the variables
            var mediaOptions = new MediaConversionOptions();

            var optionsSet = new OptionSet {
                { "r|root=", "Root path that contains the folders we'll be processing.", r => mediaOptions.rootPath = r },
                { "dv|no-video", "Don't do video processing", h => mediaOptions.disableVideo = h != null },
                { "dvc|no-video-conversion", "Don't do video conversion", h => mediaOptions.disableVideoConversion = h != null },
                { "da|no-audio", "Don't do audio processing", h => mediaOptions.disableAudio = h != null },
                { "dac|no-audio-conversion", "Don't do video conversion", h => mediaOptions.disableAudioConversion = h != null },
                { "ds|no-subtitle", "Don't do subtitle processing", h => mediaOptions.disableSubtitles = h != null },
                { "dsc|no-subtitle-conversion", "Don't do subtitle conversion", h => mediaOptions.disableSubtitleConversion = h != null },
            };

            try
            {
                // parse the command line
                var extra = optionsSet.Parse(args);
            }
            catch (OptionException e)
            {
                // output some error message
                Console.Write("greet: ");
                Console.WriteLine(e.Message);
                Console.WriteLine("Try `greet --help' for more information.");
                return;
            }

            var codecSet = new SortedSet <string>();

            foreach (var file in GetFileListToProcess(mediaOptions))
            {
                var mkvInfo = GetMkvInfo(file, mediaOptions);

                foreach (var videoTrack in mkvInfo.mVideoInfos)
                {
                    codecSet.Add(videoTrack.mMediaInfo.TrackCodec);
                }

                foreach (var audioTrack in mkvInfo.mAudioInfos)
                {
                    codecSet.Add(audioTrack.mMediaInfo.TrackCodec);
                }

                foreach (var subtitleTrack in mkvInfo.mSubtitleInfos)
                {
                    codecSet.Add(subtitleTrack.mMediaInfo.TrackCodec);
                }

                mkvInfo.Process(mediaOptions);
            }
        }
 static List <string> GetFileListToProcess(MediaConversionOptions aOptions)
 {
     return(Directory.GetFiles(Path.Combine(aOptions.rootPath, "1_ToBeProcessed")).OrderBy(file => file).ToList());
 }