private static string GetFileName(ExecutedQuery executedQuery, Video video, SettingDownload settingDownload, int videoOrder)
        {
            var result = settingDownload.FileNameTemplate;

            result = result.Replace("{playlistName}", FileHelper.GetSafeFilename(executedQuery.Title));
            result = result.Replace("{playlistNumber}", FileHelper.GetSafeFilename(videoOrder.ToString()));
            result = result.Replace("{videoTitle}", FileHelper.GetSafeFilename(video.Title));
            result = Path.Combine(settingDownload.DownloadDirectory, result);

            return(result);
        }
        private static void Save_Media(string videoId, Settings settings, SettingDownload settingDownload, string filename)
        {
            var cacheDir = Path.Combine(
                settings.CacheDirectory,
                videoId);

            Directory.CreateDirectory(cacheDir);

            var streamManifest = _youtube.Videos.Streams.GetManifestAsync(videoId).GetAwaiter().GetResult();

            AudioOnlyStreamInfo audioStreamInfo;
            VideoOnlyStreamInfo videoStreamInfo;

            SelectMediaStreamInfoSet(streamManifest, settings, settingDownload, out audioStreamInfo, out videoStreamInfo);

            var audiofilename = Path.Combine(
                settings.CacheDirectory,
                videoId,
                $"{audioStreamInfo.AudioCodec}_({audioStreamInfo.Bitrate}).{audioStreamInfo.Container.Name}"
                );

            if (
                !File.Exists(audiofilename) &&
                (
                    settingDownload.MediaType == MediaType.AudioVideo ||
                    settingDownload.MediaType == MediaType.Audio
                )
                )
            {
                DownloadMediaStream(audioStreamInfo, audiofilename, settings);
            }

            var videoFilename = Path.Combine(
                settings.CacheDirectory,
                videoId,
                $"{videoStreamInfo.VideoQuality}_{videoStreamInfo.Resolution.Width}x{videoStreamInfo.Resolution.Height}@{videoStreamInfo.Framerate}_{videoStreamInfo.VideoCodec}_({videoStreamInfo.Bitrate}).{videoStreamInfo.Container.Name}"
                );

            if (
                !File.Exists(videoFilename) &&
                (
                    settingDownload.MediaType == MediaType.AudioVideo ||
                    settingDownload.MediaType == MediaType.Video
                )
                )
            {
                DownloadMediaStream(videoStreamInfo, videoFilename, settings);
            }

            try
            {
                if (settingDownload.MediaType == MediaType.AudioVideo)
                {
                    var transcode    = (audioStreamInfo.Container != videoStreamInfo.Container);
                    var tempFileName = Path.Combine(settings.TempDirectory, Guid.NewGuid().ToString());

                    var args = new List <string>();
                    // Set input files
                    args.Add($"-i \"{videoFilename}\"");
                    args.Add($"-i \"{audiofilename}\"");

                    // Set output format
                    args.Add($"-f {videoStreamInfo.Container.Name}");

                    // Skip transcoding if it's not required
                    if (!transcode)
                    {
                        args.Add("-c copy");
                    }

                    // Optimize mp4 transcoding
                    if (transcode && string.Equals(videoStreamInfo.Container.Name, "mp4", StringComparison.OrdinalIgnoreCase))
                    {
                        args.Add("-preset ultrafast");
                    }

                    // Set max threads
                    args.Add($"-threads {Environment.ProcessorCount}");

                    // Disable stdin so that the process will not hang waiting for user input
                    args.Add("-nostdin");

                    // Trim streams to shortest
                    args.Add("-shortest");

                    // Overwrite files
                    args.Add("-y");

                    // Set output file
                    args.Add($"\"{tempFileName}\"");
                    Directory.CreateDirectory(Path.GetDirectoryName(tempFileName));
                    Directory.CreateDirectory(Path.GetDirectoryName(filename));
                    Console.WriteLine($"Saving Video to {filename}...");
                    FileHelper.LaunchCommandLineApp(cacheDir, "ffmpeg.exe", string.Join(" ", args));
                    File.Move(tempFileName, filename);
                }

                if (settingDownload.MediaType == MediaType.Audio)
                {
                    File.Move(audiofilename, filename);
                }

                if (settingDownload.MediaType == MediaType.Video)
                {
                    File.Move(videoFilename, filename);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Media Move/Transcode Exception: {ex.Message}");
            }
        }
        private static void Save_Videos(ExecutedQuery executedQuery, Settings settings, SettingDownload settingDownload)
        {
            Directory.CreateDirectory(settingDownload.DownloadDirectory);

            int videoOrder = 1;

            foreach (var video in executedQuery.Videos)
            {
                var fileName = GetFileName(executedQuery, video, settingDownload, videoOrder);
                if (!File.Exists(fileName))
                {
                    try
                    {
                        Save_Media(video.Id, settings, settingDownload, fileName);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine($"Save_Media. Exception: {ex.Message}");
                    }
                }
                videoOrder++;
            }
        }
        private static void SelectMediaStreamInfoSet(StreamManifest streamManifest, Settings settings, SettingDownload settingDownload, out AudioOnlyStreamInfo audioStreamInfo, out VideoOnlyStreamInfo videoStreamInfo)
        {
            //Todo make a better selection process
            //by largest container bitrate

            audioStreamInfo = streamManifest
                              .GetAudioOnly()
                              .Where(s => s.Container == Container.Mp4)
                              .OrderByDescending(s => s.Bitrate)
                              .First();

            videoStreamInfo = streamManifest
                              .GetVideoOnly()
                              .Where(s => s.Container == Container.Mp4)
                              .OrderByDescending(s => s.VideoQuality)
                              .ThenByDescending(s => s.Framerate)
                              .First();

            if (settingDownload.MediaType == MediaType.Audio)
            {
                audioStreamInfo = streamManifest
                                  .GetAudioOnly()
                                  .OrderByDescending(s => s.Bitrate)
                                  .First();
            }

            if (settingDownload.MediaType == MediaType.Video)
            {
                videoStreamInfo = streamManifest
                                  .GetVideoOnly()
                                  .Where(s => s.Container == Container.Mp4)
                                  .OrderByDescending(s => s.VideoQuality)
                                  .ThenByDescending(s => s.Framerate)
                                  .First();
            }
        }