From abada680f40d677033c3c8ae419a985cadc54a88 Mon Sep 17 00:00:00 2001 From: Onionware Date: Sat, 15 Apr 2023 14:00:32 +0200 Subject: [PATCH] Fixed missing metadata, added auto-update search On some sites like southpark, metadata like the video duration is in an entry and not in the main JSON information. When information is empty, OnionMedia looks into the entries if metadata is available there. The Microsoft Store version of OnionMedia checks now for updates on app start (Package.appinstaller) --- .../OnionMedia (Package).wapproj | 1 + .../OnionMedia (Package)/Package.appinstaller | 16 + .../YoutubeDLSharp/YoutubeDL.cs | 663 +++++++++--------- 3 files changed, 365 insertions(+), 315 deletions(-) create mode 100644 OnionMedia/OnionMedia (Package)/Package.appinstaller diff --git a/OnionMedia/OnionMedia (Package)/OnionMedia (Package).wapproj b/OnionMedia/OnionMedia (Package)/OnionMedia (Package).wapproj index 96acd08..97dbe35 100644 --- a/OnionMedia/OnionMedia (Package)/OnionMedia (Package).wapproj +++ b/OnionMedia/OnionMedia (Package)/OnionMedia (Package).wapproj @@ -133,6 +133,7 @@ + diff --git a/OnionMedia/OnionMedia (Package)/Package.appinstaller b/OnionMedia/OnionMedia (Package)/Package.appinstaller new file mode 100644 index 0000000..40ad033 --- /dev/null +++ b/OnionMedia/OnionMedia (Package)/Package.appinstaller @@ -0,0 +1,16 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/YoutubeDLSharp-master/YoutubeDLSharp/YoutubeDL.cs b/YoutubeDLSharp-master/YoutubeDLSharp/YoutubeDL.cs index ae70718..e9345a9 100644 --- a/YoutubeDLSharp-master/YoutubeDLSharp/YoutubeDL.cs +++ b/YoutubeDLSharp-master/YoutubeDLSharp/YoutubeDL.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; @@ -12,342 +13,374 @@ namespace YoutubeDLSharp { - /// - /// A class providing methods for downloading videos using youtube-dl. - /// - public class YoutubeDL - { - private static Regex rgxFile = new Regex("echo\\s\\\"?(.*)\\\"?", RegexOptions.Compiled); + /// + /// A class providing methods for downloading videos using youtube-dl. + /// + public class YoutubeDL + { + private static Regex rgxFile = new Regex("echo\\s\\\"?(.*)\\\"?", RegexOptions.Compiled); - protected ProcessRunner runner; + protected ProcessRunner runner; - /// - /// Path to the youtube-dl executable. - /// - public string YoutubeDLPath { get; set; } = "youtube-dl.exe"; - /// - /// Path to the FFmpeg executable. - /// - public string FFmpegPath { get; set; } = "ffmpeg.exe"; - /// - /// Path of the folder where items will be downloaded to. - /// - public string OutputFolder { get; set; } = Environment.CurrentDirectory; - /// - /// Template of the name of the downloaded file on youtube-dl style. - /// See https://github.com/ytdl-org/youtube-dl#output-template. - /// - public string OutputFileTemplate { get; set; } = "%(title)s.%(ext)s"; - /// - /// If set to true, file names a re restricted to ASCII characters. - /// - public bool RestrictFilenames { get; set; } = false; + /// + /// Path to the youtube-dl executable. + /// + public string YoutubeDLPath { get; set; } = "youtube-dl.exe"; + /// + /// Path to the FFmpeg executable. + /// + public string FFmpegPath { get; set; } = "ffmpeg.exe"; + /// + /// Path of the folder where items will be downloaded to. + /// + public string OutputFolder { get; set; } = Environment.CurrentDirectory; + /// + /// Template of the name of the downloaded file on youtube-dl style. + /// See https://github.com/ytdl-org/youtube-dl#output-template. + /// + public string OutputFileTemplate { get; set; } = "%(title)s.%(ext)s"; + /// + /// If set to true, file names a re restricted to ASCII characters. + /// + public bool RestrictFilenames { get; set; } = false; - /* TODO IMPORTANT This flag does not work fully as expected: + /* TODO IMPORTANT This flag does not work fully as expected: * Does not guarantee overwrites (see https://github.com/ytdl-org/youtube-dl/pull/20405) * Always overwrites post-processed files * (see https://github.com/ytdl-org/youtube-dl/issues/5173, https://github.com/ytdl-org/youtube-dl/issues/333) */ - public bool OverwriteFiles { get; set; } = true; + public bool OverwriteFiles { get; set; } = true; - /// - /// If set to true, download errors are ignored and downloading is continued. - /// - public bool IgnoreDownloadErrors { get; set; } = true; + /// + /// If set to true, download errors are ignored and downloading is continued. + /// + public bool IgnoreDownloadErrors { get; set; } = true; - /// - /// Gets the product version of the youtube-dl executable file. - /// - public string Version - => FileVersionInfo.GetVersionInfo(Utils.GetFullPath(YoutubeDLPath)).FileVersion; + /// + /// Gets the product version of the youtube-dl executable file. + /// + public string Version + => FileVersionInfo.GetVersionInfo(Utils.GetFullPath(YoutubeDLPath)).FileVersion; - /// - /// Creates a new instance of the YoutubeDL class. - /// - /// The maximum number of concurrent youtube-dl processes. - public YoutubeDL(byte maxNumberOfProcesses = 4) - { - runner = new ProcessRunner(maxNumberOfProcesses); - } + /// + /// Creates a new instance of the YoutubeDL class. + /// + /// The maximum number of concurrent youtube-dl processes. + public YoutubeDL(byte maxNumberOfProcesses = 4) + { + runner = new ProcessRunner(maxNumberOfProcesses); + } - /// - /// Sets the maximal number of parallel download processes. - /// - /// - /// - public async Task SetMaxNumberOfProcesses(byte count) => await runner.SetTotalCount(count); + /// + /// Sets the maximal number of parallel download processes. + /// + /// + /// + public async Task SetMaxNumberOfProcesses(byte count) => await runner.SetTotalCount(count); - #region Process methods + #region Process methods - /// - /// Runs youtube-dl with the given option set. - /// - /// The video URLs passed to youtube-dl. - /// The OptionSet of youtube-dl options. - /// A CancellationToken used to cancel the process. - /// A RunResult object containing the output of youtube-dl as an array of string. - public async Task> RunWithOptions(string[] urls, OptionSet options, CancellationToken ct) - { - var output = new List(); - var process = new YoutubeDLProcess(YoutubeDLPath); - process.OutputReceived += (o, e) => output.Add(e.Data); - (int code, string[] errors) = await runner.RunThrottled(process, urls, options, ct); - return new RunResult(code == 0, errors, output.ToArray()); - } + /// + /// Runs youtube-dl with the given option set. + /// + /// The video URLs passed to youtube-dl. + /// The OptionSet of youtube-dl options. + /// A CancellationToken used to cancel the process. + /// A RunResult object containing the output of youtube-dl as an array of string. + public async Task> RunWithOptions(string[] urls, OptionSet options, CancellationToken ct) + { + var output = new List(); + var process = new YoutubeDLProcess(YoutubeDLPath); + process.OutputReceived += (o, e) => output.Add(e.Data); + (int code, string[] errors) = await runner.RunThrottled(process, urls, options, ct); + return new RunResult(code == 0, errors, output.ToArray()); + } - /// - /// Runs an update of youtube-dl. - /// - /// The output of youtube-dl as string. - public async Task RunUpdate() - { - string output = String.Empty; - var process = new YoutubeDLProcess(YoutubeDLPath); - process.OutputReceived += (o, e) => output = e.Data; - await process.RunAsync(null, new OptionSet() { Update = true }); - return output; - } + /// + /// Runs an update of youtube-dl. + /// + /// The output of youtube-dl as string. + public async Task RunUpdate() + { + string output = String.Empty; + var process = new YoutubeDLProcess(YoutubeDLPath); + process.OutputReceived += (o, e) => output = e.Data; + await process.RunAsync(null, new OptionSet() { Update = true }); + return output; + } - /// - /// Runs a fetch of information for the given video without downloading the video. - /// - /// The URL of the video to fetch information for. - /// A CancellationToken used to cancel the process. - /// If set to true, does not extract information for each video in a playlist. - /// Override options of the default option set for this run. - /// A RunResult object containing a VideoData object with the requested video information. - public async Task> RunVideoDataFetch(string url, - CancellationToken ct = default, bool flat = true, OptionSet overrideOptions = null) - { - var opts = GetDownloadOptions(); - if (overrideOptions != null) - { - opts = opts.OverrideOptions(overrideOptions); - } - opts.DumpSingleJson = true; - opts.FlatPlaylist = flat; - VideoData videoData = null; - var process = new YoutubeDLProcess(YoutubeDLPath); - process.OutputReceived += (o, e) => videoData = JsonConvert.DeserializeObject(e.Data); - (int code, string[] errors) = await runner.RunThrottled(process, new[] { url }, opts, ct); - return new RunResult(code == 0, errors, videoData); - } + /// + /// Runs a fetch of information for the given video without downloading the video. + /// + /// The URL of the video to fetch information for. + /// A CancellationToken used to cancel the process. + /// If set to true, does not extract information for each video in a playlist. + /// Override options of the default option set for this run. + /// A RunResult object containing a VideoData object with the requested video information. + public async Task> RunVideoDataFetch(string url, + CancellationToken ct = default, bool flat = true, OptionSet overrideOptions = null) + { + var opts = GetDownloadOptions(); + if (overrideOptions != null) + { + opts = opts.OverrideOptions(overrideOptions); + } + opts.DumpSingleJson = true; + opts.FlatPlaylist = flat; + VideoData videoData = null; + var process = new YoutubeDLProcess(YoutubeDLPath); + process.OutputReceived += (o, e) => + { + Debug.WriteLine($"---DATA START---\n{e.Data}\n---DATA END---"); + videoData = JsonConvert.DeserializeObject(e.Data); + videoData = ApplyMissingDataFromEntries(videoData); + }; + (int code, string[] errors) = await runner.RunThrottled(process, new[] { url }, opts, ct); + return new RunResult(code == 0, errors, videoData); + } - /// - /// Runs a download of the specified video with an optional conversion afterwards. - /// - /// The URL of the video to be downloaded. - /// A format selection string in youtube-dl style. - /// If a merge is required, the container format of the merged downloads. - /// The video format the output will be recoded to after download. - /// A CancellationToken used to cancel the download. - /// A progress provider used to get download progress information. - /// A progress provider used to capture the standard output. - /// Override options of the default option set for this run. - /// A RunResult object containing the path to the downloaded and converted video. - public async Task> RunVideoDownload(string url, - string format = "bestvideo+bestaudio/best", - DownloadMergeFormat mergeFormat = DownloadMergeFormat.Unspecified, - VideoRecodeFormat recodeFormat = VideoRecodeFormat.None, - CancellationToken ct = default, IProgress progress = null, - IProgress output = null, OptionSet overrideOptions = null) - { - var opts = GetDownloadOptions(); - if (overrideOptions != null) - { - opts = opts.OverrideOptions(overrideOptions); - } - opts.Format = format; - opts.MergeOutputFormat = mergeFormat; - opts.RecodeVideo = recodeFormat; - string outputFile = String.Empty; - var process = new YoutubeDLProcess(YoutubeDLPath); - // Report the used ytdl args - output?.Report($"Arguments: {process.ConvertToArgs(new[] { url }, opts)}\n"); - process.OutputReceived += (o, e) => - { - var match = rgxFile.Match(e.Data); - if (match.Success) - { - outputFile = match.Groups[1].ToString().Trim('"'); - progress?.Report(new DownloadProgress(DownloadState.Success, data: outputFile)); - } - output?.Report(e.Data); - }; - (int code, string[] errors) = await runner.RunThrottled(process, new[] { url }, opts, ct, progress); - return new RunResult(code == 0, errors, outputFile); - } + static VideoData ApplyMissingDataFromEntries(VideoData videoData) + { + if (videoData is null) return videoData; - /// - /// Runs a download of the specified video playlist with an optional conversion afterwards. - /// - /// The URL of the playlist to be downloaded. - /// The index of the first playlist video to download (starting at 1). - /// The index of the last playlist video to dowload (if null, download to end). - /// An array of indices of playlist video to download. - /// A format selection string in youtube-dl style. - /// The video format the output will be recoded to after download. - /// A CancellationToken used to cancel the download. - /// A progress provider used to get download progress information. - /// A progress provider used to capture the standard output. - /// Override options of the default option set for this run. - /// A RunResult object containing the paths to the downloaded and converted videos. - public async Task> RunVideoPlaylistDownload(string url, - int? start = 1, int? end = null, - int[] items = null, - string format = "bestvideo+bestaudio/best", - VideoRecodeFormat recodeFormat = VideoRecodeFormat.None, - CancellationToken ct = default, IProgress progress = null, - IProgress output = null, OptionSet overrideOptions = null) - { - var opts = GetDownloadOptions(); - if (overrideOptions != null) - { - opts = opts.OverrideOptions(overrideOptions); - } - opts.NoPlaylist = false; - opts.PlaylistStart = start; - opts.PlaylistEnd = end; - if (items != null) - opts.PlaylistItems = String.Join(",", items); - opts.Format = format; - opts.RecodeVideo = recodeFormat; - var outputFiles = new List(); - var process = new YoutubeDLProcess(YoutubeDLPath); - // Report the used ytdl args - output?.Report($"Arguments: {process.ConvertToArgs(new[] { url }, opts)}\n"); - process.OutputReceived += (o, e) => - { - var match = rgxFile.Match(e.Data); - if (match.Success) - { - var file = match.Groups[1].ToString().Trim('"'); - outputFiles.Add(file); - progress?.Report(new DownloadProgress(DownloadState.Success, data: file)); - } - output?.Report(e.Data); - }; - (int code, string[] errors) = await runner.RunThrottled(process, new[] { url }, opts, ct, progress); - return new RunResult(code == 0, errors, outputFiles.ToArray()); - } + if (videoData.Duration is null) + { + //Get the first entry with a duration + var entry = videoData.Entries?.FirstOrDefault(vdE => vdE.Duration != null); + videoData.Duration = entry?.Duration; + } - /// - /// Runs a download of the specified video with and converts it to an audio format afterwards. - /// - /// The URL of the video to be downloaded. - /// The audio format the video will be converted to after downloaded. - /// A CancellationToken used to cancel the download. - /// A progress provider used to get download progress information. - /// A progress provider used to capture the standard output. - /// Override options of the default option set for this run. - /// A RunResult object containing the path to the downloaded and converted video. - public async Task> RunAudioDownload(string url, AudioConversionFormat format, - CancellationToken ct = default, IProgress progress = null, - IProgress output = null, OptionSet overrideOptions = null) - { - var opts = GetDownloadOptions(); - if (overrideOptions != null) - { - opts = opts.OverrideOptions(overrideOptions); - } - opts.Format = "bestaudio/best"; - opts.ExtractAudio = true; - opts.AudioFormat = format; - string outputFile = String.Empty; - var error = new List(); - var process = new YoutubeDLProcess(YoutubeDLPath); - // Report the used ytdl args - output?.Report($"Arguments: {process.ConvertToArgs(new[] { url }, opts)}\n"); - process.OutputReceived += (o, e) => - { - var match = rgxFile.Match(e.Data); - if (match.Success) - { - outputFile = match.Groups[1].ToString().Trim('"'); - progress?.Report(new DownloadProgress(DownloadState.Success, data: outputFile)); - } - output?.Report(e.Data); - }; - (int code, string[] errors) = await runner.RunThrottled(process, new[] { url }, opts, ct, progress); - return new RunResult(code == 0, errors, outputFile); - } + if (videoData.Thumbnail is null) + { + //Get the first entry with a thumbnail url + var entry = videoData.Entries?.FirstOrDefault(vdE => !string.IsNullOrEmpty(vdE.Thumbnail)); + videoData.Thumbnail = entry?.Thumbnail; + } - /// - /// Runs a download of the specified video playlist and converts all videos to an audio format afterwards. - /// - /// The URL of the playlist to be downloaded. - /// The index of the first playlist video to download (starting at 1). - /// The index of the last playlist video to dowload (if null, download to end). - /// An array of indices of playlist video to download. - /// The audio format the videos will be converted to after downloaded. - /// A CancellationToken used to cancel the download. - /// A progress provider used to get download progress information. - /// A progress provider used to capture the standard output. - /// Override options of the default option set for this run. - /// A RunResult object containing the paths to the downloaded and converted videos. - public async Task> RunAudioPlaylistDownload(string url, - int? start = 1, int? end = null, - int[] items = null, AudioConversionFormat format = AudioConversionFormat.Best, - CancellationToken ct = default, IProgress progress = null, - IProgress output = null, OptionSet overrideOptions = null) - { - var outputFiles = new List(); - var opts = GetDownloadOptions(); - if (overrideOptions != null) - { - opts = opts.OverrideOptions(overrideOptions); - } - opts.NoPlaylist = false; - opts.PlaylistStart = start; - opts.PlaylistEnd = end; - if (items != null) - opts.PlaylistItems = String.Join(",", items); - opts.Format = "bestaudio/best"; - opts.ExtractAudio = true; - opts.AudioFormat = format; - var process = new YoutubeDLProcess(YoutubeDLPath); - // Report the used ytdl args - output?.Report($"Arguments: {process.ConvertToArgs(new[] { url }, opts)}\n"); - process.OutputReceived += (o, e) => - { - var match = rgxFile.Match(e.Data); - if (match.Success) - { - var file = match.Groups[1].ToString().Trim('"'); - outputFiles.Add(file); - progress?.Report(new DownloadProgress(DownloadState.Success, data: file)); - } - output?.Report(e.Data); - }; - (int code, string[] errors) = await runner.RunThrottled(process, new[] { url }, opts, ct, progress); - return new RunResult(code == 0, errors, outputFiles.ToArray()); - } + if (videoData.UploadDate is null) + { + //Get the first entry with an uploaddate + var entry = videoData.Entries?.FirstOrDefault(vdE => vdE.UploadDate != null); + videoData.UploadDate = entry?.UploadDate; + } + return videoData; + } - /// - /// Returns an option set with default options used for most downloading operations. - /// - protected virtual OptionSet GetDownloadOptions() - { - return new OptionSet() - { - IgnoreErrors = this.IgnoreDownloadErrors, - IgnoreConfig = true, - NoPlaylist = true, - HlsPreferNative = true, - ExternalDownloaderArgs = "-nostats -loglevel 0", - Output = Path.Combine(OutputFolder, OutputFileTemplate), - RestrictFilenames = this.RestrictFilenames, - NoContinue = this.OverwriteFiles, - NoOverwrites = !this.OverwriteFiles, - NoPart = true, - FfmpegLocation = Utils.GetFullPath(this.FFmpegPath), - /* TODO This is used to retrieve the final file path. + /// + /// Runs a download of the specified video with an optional conversion afterwards. + /// + /// The URL of the video to be downloaded. + /// A format selection string in youtube-dl style. + /// If a merge is required, the container format of the merged downloads. + /// The video format the output will be recoded to after download. + /// A CancellationToken used to cancel the download. + /// A progress provider used to get download progress information. + /// A progress provider used to capture the standard output. + /// Override options of the default option set for this run. + /// A RunResult object containing the path to the downloaded and converted video. + public async Task> RunVideoDownload(string url, + string format = "bestvideo+bestaudio/best", + DownloadMergeFormat mergeFormat = DownloadMergeFormat.Unspecified, + VideoRecodeFormat recodeFormat = VideoRecodeFormat.None, + CancellationToken ct = default, IProgress progress = null, + IProgress output = null, OptionSet overrideOptions = null) + { + var opts = GetDownloadOptions(); + if (overrideOptions != null) + { + opts = opts.OverrideOptions(overrideOptions); + } + opts.Format = format; + opts.MergeOutputFormat = mergeFormat; + opts.RecodeVideo = recodeFormat; + string outputFile = String.Empty; + var process = new YoutubeDLProcess(YoutubeDLPath); + // Report the used ytdl args + output?.Report($"Arguments: {process.ConvertToArgs(new[] { url }, opts)}\n"); + process.OutputReceived += (o, e) => + { + var match = rgxFile.Match(e.Data); + if (match.Success) + { + outputFile = match.Groups[1].ToString().Trim('"'); + progress?.Report(new DownloadProgress(DownloadState.Success, data: outputFile)); + } + output?.Report(e.Data); + }; + (int code, string[] errors) = await runner.RunThrottled(process, new[] { url }, opts, ct, progress); + return new RunResult(code == 0, errors, outputFile); + } + + /// + /// Runs a download of the specified video playlist with an optional conversion afterwards. + /// + /// The URL of the playlist to be downloaded. + /// The index of the first playlist video to download (starting at 1). + /// The index of the last playlist video to dowload (if null, download to end). + /// An array of indices of playlist video to download. + /// A format selection string in youtube-dl style. + /// The video format the output will be recoded to after download. + /// A CancellationToken used to cancel the download. + /// A progress provider used to get download progress information. + /// A progress provider used to capture the standard output. + /// Override options of the default option set for this run. + /// A RunResult object containing the paths to the downloaded and converted videos. + public async Task> RunVideoPlaylistDownload(string url, + int? start = 1, int? end = null, + int[] items = null, + string format = "bestvideo+bestaudio/best", + VideoRecodeFormat recodeFormat = VideoRecodeFormat.None, + CancellationToken ct = default, IProgress progress = null, + IProgress output = null, OptionSet overrideOptions = null) + { + var opts = GetDownloadOptions(); + if (overrideOptions != null) + { + opts = opts.OverrideOptions(overrideOptions); + } + opts.NoPlaylist = false; + opts.PlaylistStart = start; + opts.PlaylistEnd = end; + if (items != null) + opts.PlaylistItems = String.Join(",", items); + opts.Format = format; + opts.RecodeVideo = recodeFormat; + var outputFiles = new List(); + var process = new YoutubeDLProcess(YoutubeDLPath); + // Report the used ytdl args + output?.Report($"Arguments: {process.ConvertToArgs(new[] { url }, opts)}\n"); + process.OutputReceived += (o, e) => + { + var match = rgxFile.Match(e.Data); + if (match.Success) + { + var file = match.Groups[1].ToString().Trim('"'); + outputFiles.Add(file); + progress?.Report(new DownloadProgress(DownloadState.Success, data: file)); + } + output?.Report(e.Data); + }; + (int code, string[] errors) = await runner.RunThrottled(process, new[] { url }, opts, ct, progress); + return new RunResult(code == 0, errors, outputFiles.ToArray()); + } + + /// + /// Runs a download of the specified video with and converts it to an audio format afterwards. + /// + /// The URL of the video to be downloaded. + /// The audio format the video will be converted to after downloaded. + /// A CancellationToken used to cancel the download. + /// A progress provider used to get download progress information. + /// A progress provider used to capture the standard output. + /// Override options of the default option set for this run. + /// A RunResult object containing the path to the downloaded and converted video. + public async Task> RunAudioDownload(string url, AudioConversionFormat format, + CancellationToken ct = default, IProgress progress = null, + IProgress output = null, OptionSet overrideOptions = null) + { + var opts = GetDownloadOptions(); + if (overrideOptions != null) + { + opts = opts.OverrideOptions(overrideOptions); + } + opts.Format = "bestaudio/best"; + opts.ExtractAudio = true; + opts.AudioFormat = format; + string outputFile = String.Empty; + var error = new List(); + var process = new YoutubeDLProcess(YoutubeDLPath); + // Report the used ytdl args + output?.Report($"Arguments: {process.ConvertToArgs(new[] { url }, opts)}\n"); + process.OutputReceived += (o, e) => + { + var match = rgxFile.Match(e.Data); + if (match.Success) + { + outputFile = match.Groups[1].ToString().Trim('"'); + progress?.Report(new DownloadProgress(DownloadState.Success, data: outputFile)); + } + output?.Report(e.Data); + }; + (int code, string[] errors) = await runner.RunThrottled(process, new[] { url }, opts, ct, progress); + return new RunResult(code == 0, errors, outputFile); + } + + /// + /// Runs a download of the specified video playlist and converts all videos to an audio format afterwards. + /// + /// The URL of the playlist to be downloaded. + /// The index of the first playlist video to download (starting at 1). + /// The index of the last playlist video to dowload (if null, download to end). + /// An array of indices of playlist video to download. + /// The audio format the videos will be converted to after downloaded. + /// A CancellationToken used to cancel the download. + /// A progress provider used to get download progress information. + /// A progress provider used to capture the standard output. + /// Override options of the default option set for this run. + /// A RunResult object containing the paths to the downloaded and converted videos. + public async Task> RunAudioPlaylistDownload(string url, + int? start = 1, int? end = null, + int[] items = null, AudioConversionFormat format = AudioConversionFormat.Best, + CancellationToken ct = default, IProgress progress = null, + IProgress output = null, OptionSet overrideOptions = null) + { + var outputFiles = new List(); + var opts = GetDownloadOptions(); + if (overrideOptions != null) + { + opts = opts.OverrideOptions(overrideOptions); + } + opts.NoPlaylist = false; + opts.PlaylistStart = start; + opts.PlaylistEnd = end; + if (items != null) + opts.PlaylistItems = String.Join(",", items); + opts.Format = "bestaudio/best"; + opts.ExtractAudio = true; + opts.AudioFormat = format; + var process = new YoutubeDLProcess(YoutubeDLPath); + // Report the used ytdl args + output?.Report($"Arguments: {process.ConvertToArgs(new[] { url }, opts)}\n"); + process.OutputReceived += (o, e) => + { + var match = rgxFile.Match(e.Data); + if (match.Success) + { + var file = match.Groups[1].ToString().Trim('"'); + outputFiles.Add(file); + progress?.Report(new DownloadProgress(DownloadState.Success, data: file)); + } + output?.Report(e.Data); + }; + (int code, string[] errors) = await runner.RunThrottled(process, new[] { url }, opts, ct, progress); + return new RunResult(code == 0, errors, outputFiles.ToArray()); + } + + /// + /// Returns an option set with default options used for most downloading operations. + /// + protected virtual OptionSet GetDownloadOptions() + { + return new OptionSet() + { + IgnoreErrors = this.IgnoreDownloadErrors, + IgnoreConfig = true, + NoPlaylist = true, + HlsPreferNative = true, + ExternalDownloaderArgs = "-nostats -loglevel 0", + Output = Path.Combine(OutputFolder, OutputFileTemplate), + RestrictFilenames = this.RestrictFilenames, + NoContinue = this.OverwriteFiles, + NoOverwrites = !this.OverwriteFiles, + NoPart = true, + FfmpegLocation = Utils.GetFullPath(this.FFmpegPath), + /* TODO This is used to retrieve the final file path. * Could be replaced by https://github.com/ytdl-org/youtube-dl/pull/22769. */ - Exec = "echo {}" - }; - } + Exec = "echo {}" + }; + } - #endregion - } + #endregion + } }