예제 #1
0
        public static async Task <string> Update()
        {
            string          returnMsg    = string.Empty;
            Regex           versionRegex = new Regex(@"(\d{4}\.\d{2}\.\d{2})");
            OperationLogger logger       = OperationLogger.Create(OperationLogger.YTDLogFile);

            await Task.Run(delegate
            {
                Helper.StartProcess(YouTubeDlPath, Commands.Update,
                                    delegate(Process process, string line)
                {
                    Match m;
                    if ((m = versionRegex.Match(line)).Success)
                    {
                        returnMsg = m.Groups[1].Value;
                    }
                },
                                    delegate(Process process, string line)
                {
                    returnMsg = "Failed";
                }, null, logger)
                .WaitForExit();
            });

            return(returnMsg);
        }
        protected override void WorkerDoWork(DoWorkEventArgs e)
        {
            try
            {
                using (var logger = OperationLogger.Create(OperationLogger.FFmpegDLogFile))
                {
                    if (_end == TimeSpan.MinValue)
                    {
                        FFmpeg.Crop(this.Input, this.Output, _start, this.ReportProgress, _cts.Token, logger);
                    }
                    else
                    {
                        FFmpeg.Crop(this.Input, this.Output, _start, _end, this.ReportProgress, _cts.Token, logger);
                    }
                }

                _start = _end = TimeSpan.MinValue;

                e.Result = this.CancellationPending ? OperationStatus.Canceled : OperationStatus.Success;
            }
            catch (Exception ex)
            {
                Common.SaveException(ex);
                Helper.DeleteFiles(this.Output);
                e.Result = OperationStatus.Failed;
            }
        }
        protected override void WorkerDoWork(DoWorkEventArgs e)
        {
            try
            {
                using (var logger = OperationLogger.Create(OperationLogger.FFmpegDLogFile))
                {
                    string tempFilename = this.Output.Substring(0, this.Output.LastIndexOf('.') + 1) + "ts";

                    if (this.Download(tempFilename))
                    {
                        this.Optimize(logger, tempFilename);
                    }
                    else
                    {
                        // Download was canceled
                        Helper.DeleteFiles(tempFilename);
                    }
                }

                // Make sure progress reaches 100%
                if (this.Progress < ProgressMax)
                {
                    this.ReportProgress(ProgressMax, null);
                }

                e.Result = OperationStatus.Success;
            }
            catch (Exception ex)
            {
                Common.SaveException(ex);
                e.Result = OperationStatus.Failed;
            }
        }
예제 #4
0
        /// <summary>
        /// Fixes and optimizes final .ts file from .m3u8 playlist.
        /// </summary>
        public static FFmpegResult <bool> FixM3U8(string input, string output, OperationLogger logger = null)
        {
            var    lines     = new StringBuilder();
            string arguments = string.Format(Commands.FixM3U8, input, output);

            LogHeader(arguments, logger);

            var p = Helper.StartProcess(FFmpegPath, arguments,
                                        null,
                                        delegate(Process process, string line)
            {
                lines.Append(line);
            }, EnvironmentVariables, logger);

            p.WaitForExit();
            LogFooter(logger);

            if (p.ExitCode != 0)
            {
                string reportFile = FindReportFile(lines.ToString());

                return(new FFmpegResult <bool>(false, p.ExitCode, CheckForErrors(reportFile)));
            }

            return(new FFmpegResult <bool>(true));
        }
예제 #5
0
        public TestLog(
            MethodInfo testMethod,
            bool writeToFile = false,
            string filename  = null)
        {
            if (testMethod == null)
            {
                throw new ArgumentNullException(nameof(testMethod));
            }

            var testName = $"{testMethod.DeclaringType.Name}.{testMethod.Name}";

            disposables.Add(Disposable.Create(() =>
            {
                Log.Dispose();
            }));

            if (writeToFile)
            {
                filename = filename ??
                           $"{testName}-{DateTime.Now:yyyy-MM-dd-hh-mm-ss}.log";
                LogFile = new FileInfo(filename);

                LogToFile();
            }

            disposables.Add(
                LogEvents.Subscribe(e => Write(e.ToLogString())));

            TestName = testName;

            Log = new OperationLogger(TestName, logOnStart: true);
        }
예제 #6
0
 /// <summary>
 /// Writes log header to log.
 /// </summary>
 private static void LogHeader(string arguments, OperationLogger logger, [CallerMemberName] string caller = "")
 {
     logger?.LogLine($"[{DateTime.Now}]");
     logger?.LogLine($"function: {caller}");
     logger?.LogLine($"cmd: {arguments}");
     logger?.LogLine();
     logger?.LogLine("OUTPUT");
 }
예제 #7
0
 public async Task Does_Not_Log_Exceptions_With_Inner_Exception()
 {
     using (var stream = new MemoryStream())
         using (var logger = new OperationLogger(stream))
         {
             await logger.LogOperationAsync(DoWithInnerException);
         }
 }
예제 #8
0
        public async Task Can_Log_A_Successful_Operations()
        {
            var stream = new MemoryStream();
            var logger = new OperationLogger(stream);

            await logger.LogOperationAsync(DoWork);

            Assert.AreEqual("DoWork executed", await ReadStream(stream));
        }
예제 #9
0
        public async Task Can_Log_A_Failed_Operation()
        {
            var stream = new MemoryStream();
            var logger = new OperationLogger(stream);

            await logger.LogOperationAsync(DoException);

            Assert.AreEqual("DoException failed", await ReadStream(stream));
        }
예제 #10
0
 /// <summary>
 /// Writes log header to log.
 /// </summary>
 /// <param name="arguments">The arguments to log in header.</param>
 /// <param name="url">The URL to log in header.</param>
 public static void LogHeader(OperationLogger logger, string arguments, [CallerMemberName] string caller = "")
 {
     logger?.LogLine("[" + DateTime.Now + "]");
     logger?.LogLine("version: " + GetVersion());
     logger?.LogLine("caller: " + caller);
     logger?.LogLine("cmd: " + arguments.Trim());
     logger?.LogLine();
     logger?.LogLine("OUTPUT");
 }
예제 #11
0
        /// <summary>
        /// Returns a <see cref="VideoInfo"/> of the given video.
        /// </summary>
        /// <param name="url">The url to the video.</param>
        public static VideoInfo GetVideoInfo(string url,
                                             YTDAuthentication authentication = null,
                                             OperationLogger logger           = null)
        {
            string json_dir  = Common.GetJsonDirectory();
            string json_file = string.Empty;
            string arguments = string.Format(Commands.GetJsonInfo,
                                             json_dir,
                                             url,
                                             authentication == null ? string.Empty : authentication.ToCmdArgument());
            VideoInfo video = new VideoInfo();

            LogHeader(logger, arguments);

            Helper.StartProcess(YouTubeDlPath, arguments,
                                delegate(Process process, string line)
            {
                line = line.Trim();

                if (line.StartsWith("[info] Writing video description metadata as JSON to:"))
                {
                    // Store file path
                    json_file = line.Substring(line.IndexOf(":") + 1).Trim();
                }
                else if (line.Contains("Refetching age-gated info webpage"))
                {
                    video.RequiresAuthentication = true;
                }
            },
                                delegate(Process process, string error)
            {
                error = error.Trim();

                if (error.Contains(ErrorSignIn))
                {
                    video.RequiresAuthentication = true;
                }
                else if (error.StartsWith("ERROR:"))
                {
                    video.Failure       = true;
                    video.FailureReason = error.Substring("ERROR: ".Length);
                }
            }, null, logger)
            .WaitForExit();

            if (!video.Failure && !video.RequiresAuthentication)
            {
                video.DeserializeJson(json_file);
            }

            return(video);
        }
예제 #12
0
        private static void ExemplifyScoping(IWindsorContainer container, string scope)
        {
            using IDisposable _ = container.BeginScope();

            OperationLogger logger = container.Resolve <OperationLogger>();

            logger.LogOperations($"{scope}-Call 1 to GetRequiredService<OperationLogger>()");

            Console.WriteLine("...");

            logger = container.Resolve <OperationLogger>();
            logger.LogOperations($"{scope}-Call 2 to GetRequiredService<OperationLogger>()");
        }
예제 #13
0
        static void ExemplifyScoping(IServiceProvider serviceProvider, string scope)
        {
            using var serviceScope = serviceProvider.CreateScope();
            IServiceProvider provider = serviceScope.ServiceProvider;

            OperationLogger logger = provider.GetRequiredService <OperationLogger>();

            logger.LogOperations($"{scope}-Call 1 to .GetRequiredService<OperationLogger>()");

            Console.WriteLine("...");

            logger = provider.GetRequiredService <OperationLogger>();
            logger.LogOperations($"{scope}-Call 2 to .GetRequiredService<OperationLogger>()");
        }
예제 #14
0
        private void ProcessRequest(string operationId, HttpContext context)
        {
            this.logger?.Log(EventType.OperationLoading, "(OperationId: {0}) Find matching operation for request.", operationId);

            // create matching operation
            Operation operation = this.operationFactory.CreateMatchingOperation(context);

            if (operation == null)
            {
                this.logger?.Log(EventType.OperationLoadingError, "(OperationId: {0}) No matching operation was found for Url '{1}'.", operationId, context.Request.Url);

                context.Response.SetDefaultValues();
                context.Response.StatusCode = 404;
                context.Response.WriteContent("No matching operation was found :(");

                return;
            }

            // initialize logger
            OperationLogger operationLogger = new OperationLogger(
                logWriter: this.logger?.LogWriter,
                clientIp: context.Request.RemoteEndpoint.Address.ToString(),
                localPort: context.Request.LocalEndpoint.Port.ToString(),
                operationId: operationId,
                operationName: operation.Name,
                url: context.Request.Url.PathAndQuery);

            // initialize operation
            operation.Initialize(operationLogger, operationId);

            operationLogger?.Log(
                EventType.OperationLoading,
                "Start executing operation for client '{0}: name: '{1}', id: '{2}', request: '{3}'",
                context.Request.RemoteEndpoint.Address,
                operation.Name,
                operation.ID,
                context.Request.RawUrl);

            DateTime startTime = DateTime.Now;

            // execute operation
            operation.Execute(context, this.authManager);

            operationLogger?.Log(EventType.OperationLoading, "Successfully finished operation '{0}' with id '{1}' after {2}s.", operation.Name, operation.ID, (DateTime.Now - startTime).TotalSeconds);
        }
예제 #15
0
        public BatchOperation(string output, ICollection <string> inputs, PreferredQuality preferredQuality)
        {
            this.Title           = $"Batch download (0/{inputs.Count} videos)";
            this.ReportsProgress = true;
            this.Input           = string.Join("|", inputs);
            this.Output          = output;
            this.Inputs.AddRange(inputs);

            _preferredQuality = preferredQuality;
            _logger           = OperationLogger.Create(OperationLogger.YTDLogFile);

            _downloader = new FileDownloader();
            // Attach events
            _downloader.Canceled                += downloader_Canceled;
            _downloader.Completed               += downloader_Completed;
            _downloader.FileDownloadFailed      += downloader_FileDownloadFailed;
            _downloader.CalculatedTotalFileSize += downloader_CalculatedTotalFileSize;
            _downloader.ProgressChanged         += downloader_ProgressChanged;
        }
예제 #16
0
        /// <summary>
        /// Returns true if given file can be converted to a MP3 file, false otherwise.
        /// </summary>
        /// <param name="file">The file to check.</param>
        public static FFmpegResult <bool> CanConvertToMP3(string file, OperationLogger logger = null)
        {
            bool   hasAudioStream = false;
            string arguments      = string.Format(Commands.GetFileInfo, file);
            var    lines          = new StringBuilder();

            LogHeader(arguments, logger);

            var p = Helper.StartProcess(FFmpegPath, arguments,
                                        null,
                                        delegate(Process process, string line)
            {
                lines.AppendLine(line = line.Trim());

                if (line.StartsWith("Stream #") && line.Contains("Audio"))
                {
                    // File has audio stream
                    hasAudioStream = true;
                }
            }, EnvironmentVariables, logger);

            p.WaitForExit();
            LogFooter(logger);

            if (p.ExitCode != 0)
            {
                string reportFile = FindReportFile(lines.ToString());
                var    errors     = (List <string>)CheckForErrors(reportFile);

                if (errors[0] != "At least one output file must be specified")
                {
                    return(new FFmpegResult <bool>(p.ExitCode, CheckForErrors(reportFile)));
                }
            }

            return(new FFmpegResult <bool>(hasAudioStream));
        }
예제 #17
0
        /// <summary>
        /// Crops file from given start position to given end position.
        /// </summary>
        /// <param name="input">The input file.</param>
        /// <param name="output">The output destination.</param>
        /// <param name="start">The <see cref="System.TimeSpan"/> crop start position.</param>
        /// <param name="end">The <see cref="System.TimeSpan"/> crop end position.</param>
        /// <param name="reportProgress">The method to call when there is progress. Can be null.</param>
        public static FFmpegResult <bool> Crop(string input,
                                               string output,
                                               TimeSpan start,
                                               TimeSpan end,
                                               Action <int, object> reportProgress,
                                               CancellationToken ct,
                                               OperationLogger logger = null)
        {
            bool isTempFile = false;

            if (input == output)
            {
                input      = FFmpeg.RenameTemp(input);
                isTempFile = true;
            }

            TimeSpan length = new TimeSpan((long)Math.Abs(start.Ticks - end.Ticks));

            string[] args = new string[]
            {
                string.Format("{0:00}:{1:00}:{2:00}.{3:000}", start.Hours, start.Minutes, start.Seconds, start.Milliseconds),
                input,
                string.Format("{0:00}:{1:00}:{2:00}.{3:000}", length.Hours, length.Minutes, length.Seconds, length.Milliseconds),
                output
            };
            bool    canceled     = false;
            bool    started      = false;
            double  milliseconds = end.TotalMilliseconds;
            string  arguments    = string.Format(Commands.CropFromTo, args);
            var     lines        = new StringBuilder();
            Process p            = null;

            reportProgress?.Invoke(0, null);
            LogHeader(arguments, logger);

            p = Helper.StartProcess(FFmpegPath, arguments,
                                    null,
                                    delegate(Process process, string line)
            {
                // Queued lines might still fire even after canceling process, don't actually know
                if (canceled)
                {
                    return;
                }

                lines.AppendLine(line = line.Trim());

                if (ct != null && ct.IsCancellationRequested)
                {
                    process.Kill();
                    canceled = true;
                    return;
                }

                // If reportProgress is null it can't be invoked. So skip code below
                if (reportProgress == null)
                {
                    return;
                }

                if (line == "Press [q] to stop, [?] for help")
                {
                    started = true;
                    reportProgress.Invoke(0, null);
                }
                else if (started && line.StartsWith("frame="))
                {
                    int lineStart  = line.IndexOf("time=") + 5;
                    int lineLength = "00:00:00.00".Length;

                    string time = line.Substring(lineStart, lineLength);

                    double currentMilli = TimeSpan.Parse(time).TotalMilliseconds;
                    double percentage   = (currentMilli / milliseconds) * 100;

                    reportProgress.Invoke(System.Convert.ToInt32(percentage), null);
                }
                else if (started && line == string.Empty)
                {
                    started = false;
                    reportProgress.Invoke(100, null);
                }
            }, EnvironmentVariables, logger);

            p.WaitForExit();

            if (isTempFile)
            {
                Helper.DeleteFiles(input);
            }

            LogFooter(logger);

            if (canceled)
            {
                return(new FFmpegResult <bool>(false));
            }
            else if (p.ExitCode != 0)
            {
                string reportFile = FindReportFile(lines.ToString());

                return(new FFmpegResult <bool>(false, p.ExitCode, CheckForErrors(reportFile)));
            }

            return(new FFmpegResult <bool>(true));
        }
예제 #18
0
        public static async Task GetVideoInfoBatchAsync(ICollection <string> urls,
                                                        Action <VideoInfo> videoReady,
                                                        YTDAuthentication authentication = null,
                                                        OperationLogger logger           = null)
        {
            string json_dir        = Common.GetJsonDirectory();
            string arguments       = string.Format(Commands.GetJsonInfoBatch, json_dir, string.Join(" ", urls));
            var    videos          = new OrderedDictionary();
            var    jsonFiles       = new Dictionary <string, string>();
            var    findVideoID     = new Regex(@"(?:\]|ERROR:)\s(.{11}):", RegexOptions.Compiled);
            var    findVideoIDJson = new Regex(@":\s.*\\(.{11})_", RegexOptions.Compiled);

            LogHeader(logger, arguments);

            await Task.Run(() =>
            {
                Helper.StartProcess(YouTubeDlPath, arguments,
                                    (Process process, string line) =>
                {
                    line = line.Trim();
                    Match m;
                    string id;
                    VideoInfo video = null;

                    if ((m = findVideoID.Match(line)).Success)
                    {
                        id    = findVideoID.Match(line).Groups[1].Value;
                        video = videos.Get <VideoInfo>(id, new VideoInfo()
                        {
                            ID = id
                        });
                    }

                    if (line.StartsWith("[info] Writing video description metadata as JSON to:"))
                    {
                        id           = findVideoIDJson.Match(line).Groups[1].Value;
                        var jsonFile = line.Substring(line.IndexOf(":") + 1).Trim();
                        jsonFiles.Put(id, jsonFile);

                        video = videos[id] as VideoInfo;
                        video.DeserializeJson(jsonFile);
                        videoReady(video);
                    }
                    else if (line.Contains("Refetching age-gated info webpage"))
                    {
                        video.RequiresAuthentication = true;
                    }
                },
                                    (Process process, string error) =>
                {
                    error     = error.Trim();
                    var id    = findVideoID.Match(error).Groups[1].Value;
                    var video = videos.Get <VideoInfo>(id, new VideoInfo()
                    {
                        ID = id
                    });

                    if (error.Contains(ErrorSignIn))
                    {
                        video.RequiresAuthentication = true;
                    }
                    else if (error.StartsWith("ERROR:"))
                    {
                        video.Failure       = true;
                        video.FailureReason = error.Substring("ERROR: ".Length);
                    }
                }, null, logger)
                .WaitForExit();
            });
        }
예제 #19
0
        /// <summary>
        /// Combines separate audio &amp; video to a single MP4 file.
        /// </summary>
        /// <param name="video">The input video file.</param>
        /// <param name="audio">The input audio file.</param>
        /// <param name="output">The output destination.</param>
        /// <param name="reportProgress">The method to call when there is progress. Can be null.</param>
        public static FFmpegResult <bool> Combine(string video,
                                                  string audio,
                                                  string output,
                                                  Action <int> reportProgress,
                                                  OperationLogger logger = null)
        {
            bool   started      = false;
            double milliseconds = 0;
            string arguments    = string.Format(Commands.Combine, video, audio, output);
            var    lines        = new StringBuilder();

            LogHeader(arguments, logger);

            var p = Helper.StartProcess(FFmpegPath, arguments,
                                        null,
                                        delegate(Process process, string line)
            {
                lines.AppendLine(line = line.Trim());

                // If reportProgress is null it can't be invoked. So skip code below
                if (reportProgress == null)
                {
                    return;
                }

                if (line.StartsWith("Duration: "))
                {
                    int lineStart = "Duration: ".Length;
                    int length    = "00:00:00.00".Length;

                    string time = line.Substring(lineStart, length);

                    milliseconds = TimeSpan.Parse(time).TotalMilliseconds;
                }
                else if (line == "Press [q] to stop, [?] for help")
                {
                    started = true;
                    reportProgress.Invoke(0);
                }
                else if (started && line.StartsWith("frame="))
                {
                    int lineStart = line.IndexOf("time=") + 5;
                    int length    = "00:00:00.00".Length;

                    string time = line.Substring(lineStart, length);

                    double currentMilli = TimeSpan.Parse(time).TotalMilliseconds;
                    double percentage   = (currentMilli / milliseconds) * 100;

                    reportProgress.Invoke(System.Convert.ToInt32(percentage));
                }
                else if (started && line == string.Empty)
                {
                    started = false;
                    reportProgress.Invoke(100);
                }
            }, EnvironmentVariables, logger);

            p.WaitForExit();
            LogFooter(logger);

            if (p.ExitCode != 0)
            {
                string reportFile = FindReportFile(lines.ToString());

                return(new FFmpegResult <bool>(false, p.ExitCode, CheckForErrors(reportFile)));
            }

            return(new FFmpegResult <bool>(true));
        }
        protected override void WorkerDoWork(DoWorkEventArgs e)
        {
            downloader.Start();

            while (downloader?.IsBusy == true)
            {
                Thread.Sleep(200);
            }

            if (_combine && _downloadSuccessful)
            {
                string audio = downloader.Files[0].Path;
                string video = downloader.Files[1].Path;

                this.ReportProgress(-1, new Dictionary <string, object>()
                {
                    { nameof(Progress), 0 }
                });
                this.ReportProgress(ProgressMax, null);

                try
                {
                    FFmpegResult <bool> result;

                    this.ReportProgress(-1, new Dictionary <string, object>()
                    {
                        { nameof(ProgressText), "Combining..." }
                    });

                    using (var logger = OperationLogger.Create(OperationLogger.FFmpegDLogFile))
                    {
                        result = new FFmpegProcess(logger).Combine(video, audio, this.Output, delegate(int percentage)
                        {
                            // Combine progress
                            this.ReportProgress(percentage, null);
                        });
                    }

                    if (result.Value)
                    {
                        e.Result = OperationStatus.Success;
                    }
                    else
                    {
                        e.Result = OperationStatus.Failed;
                        this.ErrorsInternal.AddRange(result.Errors);
                    }

                    // Cleanup the separate audio and video files
                    Helper.DeleteFiles(audio, video);
                }
                catch (Exception ex)
                {
                    Common.SaveException(ex);
                    e.Result = OperationStatus.Failed;
                }
            }
            else
            {
                e.Result = this.Status;
            }
        }
예제 #21
0
        public static bool Combine(string audio,
                                   string video,
                                   string title,
                                   OperationLogger logger,
                                   out Exception exception,
                                   Action <int, object> reportProgress)
        {
            // Remove '_video' from video file to get a final filename.
            string error  = string.Empty;
            string output = video.Replace("_video", string.Empty);
            FFmpegResult <bool> result = null;

            try
            {
                // Raise events on main thread
                reportProgress(-1, new Dictionary <string, object>()
                {
                    { "ProgressText", "Combining..." }
                });

                result = FFmpeg.Combine(video, audio, output, delegate(int percentage)
                {
                    // Combine progress
                    reportProgress(percentage, null);
                }, logger);

                // Save errors if combining failed
                if (!result.Value)
                {
                    var sb = new StringBuilder();

                    sb.AppendLine(title);
                    sb.AppendLine(string.Join(
                                      Environment.NewLine,
                                      result.Errors.Select(err => $" - {err}")));

                    error = sb.ToString();
                }

                // Cleanup the separate audio and video files
                Helper.DeleteFiles(audio, video);
            }
            catch (Exception ex)
            {
                exception = ex;
                Common.SaveException(ex);
                return(false);
            }
            finally
            {
                // Raise events on main thread
                reportProgress(-1, new Dictionary <string, object>()
                {
                    { "ProgressText", null }
                });
            }

            exception = new Exception(error);

            return(result.Value);
        }
        protected override void WorkerDoWork(DoWorkEventArgs e)
        {
            if (_mode == ConvertingMode.File)
            {
                try
                {
                    using (var logger = OperationLogger.Create(OperationLogger.FFmpegDLogFile))
                    {
                        FFmpeg.Convert(this.Input, this.Output, this.ReportProgress, _cts.Token, logger);

                        // Crop if not operation wasn't canceled and _start has a valid value
                        if (!this.CancellationPending && _start != TimeSpan.MinValue)
                        {
                            // Crop to end of file, unless _end has a valid value
                            if (_end == TimeSpan.MinValue)
                            {
                                FFmpeg.Crop(this.Output, this.Output, _start, this.ReportProgress, _cts.Token, logger);
                            }
                            else
                            {
                                FFmpeg.Crop(this.Output, this.Output, _start, _end, this.ReportProgress, _cts.Token, logger);
                            }
                        }
                    }

                    // Reset variables
                    _start = _end = TimeSpan.MinValue;
                }
                catch (Exception ex)
                {
                    Common.SaveException(ex);
                    Helper.DeleteFiles(this.Output);
                    e.Result = OperationStatus.Failed;
                }
            }
            else
            {
                using (var logger = OperationLogger.Create(OperationLogger.FFmpegDLogFile))
                {
                    foreach (string input in Directory.GetFiles(this.Input, _searchPattern))
                    {
                        if (this.CancellationPending)
                        {
                            break;
                        }

                        _count++;
                        try
                        {
                            string output = string.Format("{0}\\{1}.mp3",
                                                          this.Output,
                                                          Path.GetFileNameWithoutExtension(input));

                            this.ReportProgress(UpdateProperties, new Dictionary <string, object>()
                            {
                                { nameof(Operation.Title), Path.GetFileName(input) },
                                { nameof(Operation.Duration), (int)FFmpeg.GetDuration(input).Value.TotalSeconds },
                                { nameof(Operation.FileSize), Helper.GetFileSize(input) }
                            });

                            _currentOutput = output;
                            FFmpeg.Convert(input, output, this.ReportProgress, _cts.Token, logger);
                            _currentOutput = null;

                            this.ProcessedFiles.Add(output);
                        }
                        catch (Exception ex)
                        {
                            _failures++;
                            Common.SaveException(ex);
                            Helper.DeleteFiles(_currentOutput);
                            continue;
                        }
                    }
                }
            }

            // Set operation result
            e.Result = this.CancellationPending ? OperationStatus.Canceled : OperationStatus.Success;
        }
        private bool Combine()
        {
            string audio = _downloader.Files[0].Path;
            string video = _downloader.Files[1].Path;
            // Remove '_video' from video file to get a final filename.
            string output = video.Replace("_video", string.Empty);
            FFmpegResult <bool> result = null;

            try
            {
                // Raise events on main thread
                this.ReportProgress(-1, new Dictionary <string, object>()
                {
                    { nameof(ProgressText), "Combining..." }
                });

                if (_ffmpegLogger == null)
                {
                    _ffmpegLogger = OperationLogger.Create(OperationLogger.FFmpegDLogFile);
                }

                if (_ffmpeg == null)
                {
                    _ffmpeg = new FFmpegProcess(_ffmpegLogger);
                }

                result = _ffmpeg.Combine(video, audio, output, delegate(int percentage)
                {
                    // Combine progress
                    this.ReportProgress(percentage, null);
                });

                // Save errors if combining failed
                if (!result.Value)
                {
                    var sb = new StringBuilder();

                    sb.AppendLine(this.Title);

                    foreach (string error in result.Errors)
                    {
                        sb.AppendLine($" - {error}");
                    }

                    this.ErrorsInternal.Add(sb.ToString());
                }

                // Cleanup the separate audio and video files
                Helper.DeleteFiles(audio, video);
            }
            catch (Exception ex)
            {
                Common.SaveException(ex);
                return(false);
            }
            finally
            {
                // Raise events on main thread
                this.ReportProgress(-1, new Dictionary <string, object>()
                {
                    { nameof(ProgressText), null }
                });
            }

            return(result.Value);
        }
        protected override void WorkerDoWork(DoWorkEventArgs e)
        {
            /* ToDo:
             *
             * [ ] Handle TimeoutException from PlaylistReader in 'GetPlaylistInfoAsync' somehow.
             *     Can't catch it here, needs to catch it inside 'GetPlaylistInfoAsync'.
             */

            this.GetPlaylistInfoAsync();

            try
            {
                int count = 0;

                while (count < this.Videos.Count || _queryingVideos)
                {
                    if (this.CancellationPending)
                    {
                        break;
                    }

                    // Wait for more videos?
                    while (count == this.Videos.Count)
                    {
                        Thread.Sleep(200);
                        continue;
                    }

                    // Reset variable(s)
                    _downloaderSuccessful = null;
                    _downloader.Files.Clear();

                    count++;

                    var video = this.Videos[count - 1];

                    if (video.Failure)
                    {
                        // Something failed retrieving video info
                        _failures++;
                        continue;
                    }

                    VideoFormat format = Helper.GetPreferredFormat(video, _preferredQuality);

                    // Update properties for new video
                    this.ReportProgress(-1, new Dictionary <string, object>()
                    {
                        { nameof(Title), $"({count}/{_selectedVideosCount}) {video.Title}" },
                        { nameof(Duration), video.Duration },
                        { nameof(FileSize), format.FileSize }
                    });

                    string prefix    = _indexPrefix ? (_downloads + 1) + ". " : string.Empty;
                    string finalFile = Path.Combine(this.Output,
                                                    $"{prefix}{Helper.FormatTitle(format.VideoInfo.Title)}.{format.Extension}");

                    // Overwrite if finalFile already exists
                    Helper.DeleteFiles(finalFile);

                    this.DownloadedFiles.Add(finalFile);

                    if (format.AudioOnly)
                    {
                        _downloader.Files.Add(new FileDownload(finalFile, format.DownloadUrl));
                    }
                    else
                    {
                        VideoFormat audioFormat = Helper.GetAudioFormat(format);
                        // Add '_audio' & '_video' to end of filename. Only get filename, not full path.
                        string audioFile = Regex.Replace(finalFile, @"^(.*)(\..*)$", "$1_audio$2");
                        string videoFile = Regex.Replace(finalFile, @"^(.*)(\..*)$", "$1_video$2");

                        // Download audio and video
                        _downloader.Files.Add(new FileDownload(audioFile, audioFormat.DownloadUrl));
                        _downloader.Files.Add(new FileDownload(videoFile, format.DownloadUrl));

                        // Delete _audio and _video files in case they exists from a previous attempt
                        Helper.DeleteFiles(_downloader.Files[0].Path,
                                           _downloader.Files[1].Path);
                    }

                    _downloader.Start();

                    // Wait for downloader to finish
                    while (_downloader.IsBusy || _downloader.IsPaused)
                    {
                        if (this.CancellationPending)
                        {
                            _downloader.Stop();
                            break;
                        }

                        Thread.Sleep(200);
                    }

                    if (_downloaderSuccessful == true)
                    {
                        // Combine video and audio if necessary
                        if (!format.AudioOnly)
                        {
                            this.ReportProgress(-1, new Dictionary <string, object>()
                            {
                                { nameof(Progress), 0 }
                            });
                            this.ReportProgress(ProgressMax, null);

                            if (!this.Combine())
                            {
                                _failures++;
                            }

                            this.ReportProgress(-1, new Dictionary <string, object>()
                            {
                                { nameof(Progress), 0 }
                            });
                        }

                        _downloads++;
                        this.ReportProgress(EventFileDownloadComplete, finalFile);
                    }
                    else if (_downloaderSuccessful == false)
                    {
                        // Download failed, cleanup and continue
                        _failures++;
                        // Delete all related files. Helper method will check if it exists, throwing no errors
                        Helper.DeleteFiles(_downloader.Files.Select(x => x.Path).ToArray());
                    }

                    // Reset before starting new download.
                    this.ReportProgress(ProgressMin, null);
                }

                // Throw stored exception if it exists. For example TimeoutException from 'GetPlaylistInfoAsync'
                if (_operationException != null)
                {
                    throw _operationException;
                }

                e.Result = this.CancellationPending ? OperationStatus.Canceled : OperationStatus.Success;
            }
            catch (Exception ex)
            {
                Common.SaveException(ex);
                e.Result            = OperationStatus.Failed;
                _operationException = ex;
            }
            finally
            {
                _ffmpegLogger?.Close();
                _ffmpegLogger = null;
                _ffmpeg       = null;
            }
        }
예제 #25
0
 /// <summary>
 /// Writes log footer to log.
 /// </summary>
 private static void LogFooter(OperationLogger logger)
 {
     // Write log footer to stream.
     // Possibly write elapsed time and/or error in future.
     logger?.LogLine(Environment.NewLine);
 }
예제 #26
0
 public FFmpegProcess(OperationLogger logger)
 {
     _logger = logger;
 }
 private void Optimize(OperationLogger logger, string tsFile)
 {
     FFmpeg.FixM3U8(tsFile, this.Output, logger);
     Helper.DeleteFiles(tsFile);
 }
 private void Optimize(OperationLogger logger, string tsFile)
 {
     new FFmpegProcess(logger).FixM3U8(tsFile, this.Output);
     Helper.DeleteFiles(tsFile);
 }
 public YoutubeDlProcess(OperationLogger logger, YTDAuthentication authentication)
 {
     _logger         = logger;
     _authentication = authentication;
 }