diff --git a/Directory.Packages.props b/Directory.Packages.props index 346bc415..a6247567 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -5,7 +5,7 @@ - + @@ -24,7 +24,9 @@ - + + + diff --git a/YoutubeExplode.Converter.Tests/EnvironmentSpecs.cs b/YoutubeExplode.Converter.Tests/EnvironmentSpecs.cs index d6880e15..5645380d 100644 --- a/YoutubeExplode.Converter.Tests/EnvironmentSpecs.cs +++ b/YoutubeExplode.Converter.Tests/EnvironmentSpecs.cs @@ -1,6 +1,7 @@ using System.IO; using System.Threading.Tasks; using FluentAssertions; +using PowerKit; using Xunit; using Xunit.Abstractions; using YoutubeExplode.Converter.Tests.Utils; @@ -19,7 +20,7 @@ public async Task I_can_download_a_video_with_custom_environment_variables_passe // Arrange using var youtube = new YoutubeClient(); - using var dir = TempDir.Create(); + using var dir = TempDirectory.Create(); var filePath = Path.Combine(dir.Path, "video.mp4"); var logFilePath = Path.Combine(dir.Path, "ffreport.log"); diff --git a/YoutubeExplode.Converter.Tests/GeneralSpecs.cs b/YoutubeExplode.Converter.Tests/GeneralSpecs.cs index 11c770b6..9cb68e95 100644 --- a/YoutubeExplode.Converter.Tests/GeneralSpecs.cs +++ b/YoutubeExplode.Converter.Tests/GeneralSpecs.cs @@ -5,10 +5,11 @@ using System.Threading.Tasks; using FluentAssertions; using Gress; +using PowerKit; +using PowerKit.Extensions; using Xunit; using Xunit.Abstractions; using YoutubeExplode.Converter.Tests.Utils; -using YoutubeExplode.Converter.Tests.Utils.Extensions; using YoutubeExplode.Videos.Streams; namespace YoutubeExplode.Converter.Tests; @@ -25,7 +26,7 @@ public async Task I_can_download_a_video_as_a_single_mp4_file() // Arrange using var youtube = new YoutubeClient(); - using var dir = TempDir.Create(); + using var dir = TempDirectory.Create(); var filePath = Path.Combine(dir.Path, "video.mp4"); // Act @@ -41,7 +42,7 @@ public async Task I_can_download_a_video_as_a_single_webm_file() // Arrange using var youtube = new YoutubeClient(); - using var dir = TempDir.Create(); + using var dir = TempDirectory.Create(); var filePath = Path.Combine(dir.Path, "video.webm"); // Act @@ -57,7 +58,7 @@ public async Task I_can_download_a_video_as_a_single_mp3_file() // Arrange using var youtube = new YoutubeClient(); - using var dir = TempDir.Create(); + using var dir = TempDirectory.Create(); var filePath = Path.Combine(dir.Path, "video.mp3"); // Act @@ -73,7 +74,7 @@ public async Task I_can_download_a_video_as_a_single_ogg_file() // Arrange using var youtube = new YoutubeClient(); - using var dir = TempDir.Create(); + using var dir = TempDirectory.Create(); var filePath = Path.Combine(dir.Path, "video.ogg"); // Act @@ -89,7 +90,7 @@ public async Task I_can_download_a_video_as_a_single_mp4_file_with_multiple_stre // Arrange using var youtube = new YoutubeClient(); - using var dir = TempDir.Create(); + using var dir = TempDirectory.Create(); var filePath = Path.Combine(dir.Path, "video.mp4"); // Act @@ -122,7 +123,7 @@ await youtube.Videos.DownloadAsync( { if (streamInfo.AudioLanguage is not null) { - File.ContainsBytes( + File.Contains( filePath, Encoding.ASCII.GetBytes(streamInfo.AudioLanguage.Value.Name) ) @@ -133,7 +134,7 @@ await youtube.Videos.DownloadAsync( foreach (var streamInfo in videoStreamInfos) { - File.ContainsBytes(filePath, Encoding.ASCII.GetBytes(streamInfo.VideoQuality.Label)) + File.Contains(filePath, Encoding.ASCII.GetBytes(streamInfo.VideoQuality.Label)) .Should() .BeTrue(); } @@ -145,7 +146,7 @@ public async Task I_can_download_a_video_as_a_single_webm_file_with_multiple_str // Arrange using var youtube = new YoutubeClient(); - using var dir = TempDir.Create(); + using var dir = TempDirectory.Create(); var filePath = Path.Combine(dir.Path, "video.webm"); // Act @@ -178,7 +179,7 @@ await youtube.Videos.DownloadAsync( { if (streamInfo.AudioLanguage is not null) { - File.ContainsBytes( + File.Contains( filePath, Encoding.ASCII.GetBytes(streamInfo.AudioLanguage.Value.Name) ) @@ -189,7 +190,7 @@ await youtube.Videos.DownloadAsync( foreach (var streamInfo in videoStreamInfos) { - File.ContainsBytes(filePath, Encoding.ASCII.GetBytes(streamInfo.VideoQuality.Label)) + File.Contains(filePath, Encoding.ASCII.GetBytes(streamInfo.VideoQuality.Label)) .Should() .BeTrue(); } @@ -201,7 +202,7 @@ public async Task I_can_download_a_video_with_custom_conversion_settings() // Arrange using var youtube = new YoutubeClient(); - using var dir = TempDir.Create(); + using var dir = TempDirectory.Create(); var filePath = Path.Combine(dir.Path, "video.mp3"); // Act @@ -224,7 +225,7 @@ public async Task I_can_try_to_download_a_video_and_get_an_error_if_the_conversi // Arrange using var youtube = new YoutubeClient(); - using var dir = TempDir.Create(); + using var dir = TempDirectory.Create(); var filePath = Path.Combine(dir.Path, "video.mp4"); // Act @@ -252,7 +253,7 @@ public async Task I_can_download_a_video_while_tracking_progress() // Arrange using var youtube = new YoutubeClient(); - using var dir = TempDir.Create(); + using var dir = TempDirectory.Create(); var filePath = Path.Combine(dir.Path, "video.mp3"); var progress = new ProgressCollector(); diff --git a/YoutubeExplode.Converter.Tests/SubtitleSpecs.cs b/YoutubeExplode.Converter.Tests/SubtitleSpecs.cs index 60b8a909..b124d6ae 100644 --- a/YoutubeExplode.Converter.Tests/SubtitleSpecs.cs +++ b/YoutubeExplode.Converter.Tests/SubtitleSpecs.cs @@ -3,9 +3,10 @@ using System.Text; using System.Threading.Tasks; using FluentAssertions; +using PowerKit; +using PowerKit.Extensions; using Xunit; using YoutubeExplode.Converter.Tests.Utils; -using YoutubeExplode.Converter.Tests.Utils.Extensions; using YoutubeExplode.Videos.Streams; namespace YoutubeExplode.Converter.Tests; @@ -22,7 +23,7 @@ public async Task I_can_download_a_video_as_a_single_mp4_file_with_subtitles() // Arrange using var youtube = new YoutubeClient(); - using var dir = TempDir.Create(); + using var dir = TempDirectory.Create(); var filePath = Path.Combine(dir.Path, "video.mp4"); var streamManifest = await youtube.Videos.Streams.GetManifestAsync("NtQkz0aRDe8"); @@ -48,7 +49,7 @@ await youtube.Videos.DownloadAsync( foreach (var trackInfo in trackInfos) { - File.ContainsBytes(filePath, Encoding.ASCII.GetBytes(trackInfo.Language.Name)) + File.Contains(filePath, Encoding.ASCII.GetBytes(trackInfo.Language.Name)) .Should() .BeTrue(); } @@ -60,7 +61,7 @@ public async Task I_can_download_a_video_as_a_single_webm_file_with_subtitles() // Arrange using var youtube = new YoutubeClient(); - using var dir = TempDir.Create(); + using var dir = TempDirectory.Create(); var filePath = Path.Combine(dir.Path, "video.webm"); var streamManifest = await youtube.Videos.Streams.GetManifestAsync("NtQkz0aRDe8"); @@ -86,7 +87,7 @@ await youtube.Videos.DownloadAsync( foreach (var trackInfo in trackInfos) { - File.ContainsBytes(filePath, Encoding.ASCII.GetBytes(trackInfo.Language.Name)) + File.Contains(filePath, Encoding.ASCII.GetBytes(trackInfo.Language.Name)) .Should() .BeTrue(); } diff --git a/YoutubeExplode.Converter.Tests/Utils/Extensions/FileExtensions.cs b/YoutubeExplode.Converter.Tests/Utils/Extensions/FileExtensions.cs deleted file mode 100644 index 7d91c6f9..00000000 --- a/YoutubeExplode.Converter.Tests/Utils/Extensions/FileExtensions.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.IO; - -namespace YoutubeExplode.Converter.Tests.Utils.Extensions; - -internal static class FileExtensions -{ - extension(File) - { - public static bool ContainsBytes(string filePath, ReadOnlySpan data) - { - using var stream = File.OpenRead(filePath); - using var reader = new BinaryReader(stream); - - var referenceIndex = 0; - - while (stream.Position < stream.Length) - { - if (reader.ReadByte() == data[referenceIndex]) - { - referenceIndex++; - } - else - { - referenceIndex = 0; - } - - if (referenceIndex >= data.Length) - return true; - } - - return false; - } - } -} diff --git a/YoutubeExplode.Converter.Tests/Utils/Extensions/HttpExtensions.cs b/YoutubeExplode.Converter.Tests/Utils/Extensions/HttpExtensions.cs deleted file mode 100644 index 5da6d20a..00000000 --- a/YoutubeExplode.Converter.Tests/Utils/Extensions/HttpExtensions.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.IO; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; - -namespace YoutubeExplode.Converter.Tests.Utils.Extensions; - -internal static class HttpExtensions -{ - extension(HttpClient http) - { - public async Task DownloadAsync( - string url, - string filePath, - CancellationToken cancellationToken = default - ) - { - using var response = await http.GetAsync( - url, - HttpCompletionOption.ResponseHeadersRead, - cancellationToken - ); - - response.EnsureSuccessStatusCode(); - - await using var source = await response.Content.ReadAsStreamAsync(cancellationToken); - await using var destination = File.Create(filePath); - - await source.CopyToAsync(destination, cancellationToken); - } - } -} diff --git a/YoutubeExplode.Converter.Tests/Utils/FFmpeg.cs b/YoutubeExplode.Converter.Tests/Utils/FFmpeg.cs index 4335c2ae..f1029956 100644 --- a/YoutubeExplode.Converter.Tests/Utils/FFmpeg.cs +++ b/YoutubeExplode.Converter.Tests/Utils/FFmpeg.cs @@ -6,7 +6,8 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; -using YoutubeExplode.Converter.Tests.Utils.Extensions; +using PowerKit; +using PowerKit.Extensions; namespace YoutubeExplode.Converter.Tests.Utils; diff --git a/YoutubeExplode.Converter.Tests/Utils/TempDir.cs b/YoutubeExplode.Converter.Tests/Utils/TempDir.cs deleted file mode 100644 index e294ce9b..00000000 --- a/YoutubeExplode.Converter.Tests/Utils/TempDir.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.IO; -using System.Reflection; - -namespace YoutubeExplode.Converter.Tests.Utils; - -internal partial class TempDir(string path) : IDisposable -{ - public string Path { get; } = path; - - public void Dispose() - { - try - { - Directory.Delete(Path, true); - } - catch (DirectoryNotFoundException) { } - } -} - -internal partial class TempDir -{ - public static TempDir Create() - { - var dirPath = System.IO.Path.Combine( - System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) - ?? Directory.GetCurrentDirectory(), - "Temp", - Guid.NewGuid().ToString() - ); - - Directory.CreateDirectory(dirPath); - - return new TempDir(dirPath); - } -} diff --git a/YoutubeExplode.Converter.Tests/Utils/TempFile.cs b/YoutubeExplode.Converter.Tests/Utils/TempFile.cs deleted file mode 100644 index 5e04e1c8..00000000 --- a/YoutubeExplode.Converter.Tests/Utils/TempFile.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.IO; -using System.Reflection; - -namespace YoutubeExplode.Converter.Tests.Utils; - -internal partial class TempFile(string path) : IDisposable -{ - public string Path { get; } = path; - - public void Dispose() - { - try - { - File.Delete(Path); - } - catch (FileNotFoundException) { } - } -} - -internal partial class TempFile -{ - public static TempFile Create() - { - var dirPath = System.IO.Path.Combine( - System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) - ?? Directory.GetCurrentDirectory(), - "Temp" - ); - - Directory.CreateDirectory(dirPath); - - var filePath = System.IO.Path.Combine(dirPath, Guid.NewGuid() + ".tmp"); - - return new TempFile(filePath); - } -} diff --git a/YoutubeExplode.Converter.Tests/YoutubeExplode.Converter.Tests.csproj b/YoutubeExplode.Converter.Tests/YoutubeExplode.Converter.Tests.csproj index ece322cf..7b3e105c 100644 --- a/YoutubeExplode.Converter.Tests/YoutubeExplode.Converter.Tests.csproj +++ b/YoutubeExplode.Converter.Tests/YoutubeExplode.Converter.Tests.csproj @@ -14,6 +14,7 @@ + diff --git a/YoutubeExplode.Converter/ConversionExtensions.cs b/YoutubeExplode.Converter/ConversionExtensions.cs index 68feaf3c..2cc384f1 100644 --- a/YoutubeExplode.Converter/ConversionExtensions.cs +++ b/YoutubeExplode.Converter/ConversionExtensions.cs @@ -5,7 +5,7 @@ using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; -using YoutubeExplode.Converter.Utils.Extensions; +using PowerKit.Extensions; using YoutubeExplode.Videos; using YoutubeExplode.Videos.ClosedCaptions; using YoutubeExplode.Videos.Streams; diff --git a/YoutubeExplode.Converter/ConversionRequestBuilder.cs b/YoutubeExplode.Converter/ConversionRequestBuilder.cs index f534dfc6..8d02b224 100644 --- a/YoutubeExplode.Converter/ConversionRequestBuilder.cs +++ b/YoutubeExplode.Converter/ConversionRequestBuilder.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; -using YoutubeExplode.Converter.Utils.Extensions; +using PowerKit.Extensions; using YoutubeExplode.Videos.Streams; namespace YoutubeExplode.Converter; diff --git a/YoutubeExplode.Converter/Converter.cs b/YoutubeExplode.Converter/Converter.cs index beffcd9f..efc4fc61 100644 --- a/YoutubeExplode.Converter/Converter.cs +++ b/YoutubeExplode.Converter/Converter.cs @@ -5,7 +5,8 @@ using System.Threading; using System.Threading.Tasks; using CliWrap.Builders; -using YoutubeExplode.Converter.Utils; +using PowerKit; +using PowerKit.Extensions; using YoutubeExplode.Converter.Utils.Extensions; using YoutubeExplode.Videos; using YoutubeExplode.Videos.ClosedCaptions; @@ -155,10 +156,8 @@ private async ValueTask ProcessAsync( // Language codes can be stored in any format, but most players expect // three-letter codes, so we'll try to convert to that first. var languageCode = - subtitleInput.Info.Language.TryGetThreeLetterCode() ?? subtitleInput - .Info - .Language - .Code; + subtitleInput.Info.Language.TryGetThreeLetterCode() + ?? subtitleInput.Info.Language.Code; arguments .Add($"-metadata:s:s:{i}") diff --git a/YoutubeExplode.Converter/FFmpeg.cs b/YoutubeExplode.Converter/FFmpeg.cs index 49ab6f47..c1ddd5ce 100644 --- a/YoutubeExplode.Converter/FFmpeg.cs +++ b/YoutubeExplode.Converter/FFmpeg.cs @@ -9,7 +9,7 @@ using System.Threading.Tasks; using CliWrap; using CliWrap.Exceptions; -using YoutubeExplode.Converter.Utils.Extensions; +using PowerKit.Extensions; namespace YoutubeExplode.Converter; @@ -135,11 +135,9 @@ private static PipeTarget CreateProgressRouter(IProgress progress) + TimeSpan.FromSeconds(seconds); progress.Report( - Math.Clamp( - processedDuration.TotalMilliseconds / totalDuration.Value.TotalMilliseconds, - 0, - 1 - ) + ( + processedDuration.TotalMilliseconds / totalDuration.Value.TotalMilliseconds + ).Clamp(0, 1) ); } }); diff --git a/YoutubeExplode.Converter/Utils/Extensions/AsyncCollectionExtensions.cs b/YoutubeExplode.Converter/Utils/Extensions/AsyncCollectionExtensions.cs deleted file mode 100644 index c268115a..00000000 --- a/YoutubeExplode.Converter/Utils/Extensions/AsyncCollectionExtensions.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; - -namespace YoutubeExplode.Converter.Utils.Extensions; - -internal static class AsyncCollectionExtensions -{ - extension(IAsyncEnumerable source) - { - public async ValueTask> ToListAsync() - { - var list = new List(); - - await foreach (var i in source) - list.Add(i); - - return list; - } - - public ValueTaskAwaiter> GetAwaiter() => source.ToListAsync().GetAwaiter(); - } -} diff --git a/YoutubeExplode.Converter/Utils/Extensions/GenericExtensions.cs b/YoutubeExplode.Converter/Utils/Extensions/GenericExtensions.cs deleted file mode 100644 index 1eb20167..00000000 --- a/YoutubeExplode.Converter/Utils/Extensions/GenericExtensions.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace YoutubeExplode.Converter.Utils.Extensions; - -internal static class GenericExtensions -{ - extension(TIn input) - { - public TOut Pipe(Func transform) => transform(input); - } -} diff --git a/YoutubeExplode.Converter/Utils/Extensions/LanguageExtensions.cs b/YoutubeExplode.Converter/Utils/Extensions/LanguageExtensions.cs index 1ff28e49..d9fb23d9 100644 --- a/YoutubeExplode.Converter/Utils/Extensions/LanguageExtensions.cs +++ b/YoutubeExplode.Converter/Utils/Extensions/LanguageExtensions.cs @@ -1,4 +1,5 @@ using System; +using PowerKit.Extensions; using YoutubeExplode.Videos.ClosedCaptions; namespace YoutubeExplode.Converter.Utils.Extensions; diff --git a/YoutubeExplode.Converter/Utils/Extensions/StringExtensions.cs b/YoutubeExplode.Converter/Utils/Extensions/StringExtensions.cs deleted file mode 100644 index afb1e753..00000000 --- a/YoutubeExplode.Converter/Utils/Extensions/StringExtensions.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; - -namespace YoutubeExplode.Converter.Utils.Extensions; - -internal static class StringExtensions -{ - extension(string str) - { - public string? NullIfWhiteSpace() => !string.IsNullOrWhiteSpace(str) ? str : null; - - public string SubstringUntil( - string sub, - StringComparison comparison = StringComparison.Ordinal - ) => - str.IndexOf(sub, comparison) switch - { - >= 0 and var index => str[..index], - _ => str, - }; - } -} diff --git a/YoutubeExplode.Converter/Utils/ProgressMuxer.cs b/YoutubeExplode.Converter/Utils/ProgressMuxer.cs deleted file mode 100644 index 46c04a42..00000000 --- a/YoutubeExplode.Converter/Utils/ProgressMuxer.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; - -namespace YoutubeExplode.Converter.Utils; - -internal class ProgressMuxer(IProgress target) -{ - private readonly Lock _lock = new(); - private readonly Dictionary _splitWeights = new(); - private readonly Dictionary _splitValues = new(); - - public IProgress CreateInput(double weight = 1) - { - using (_lock.EnterScope()) - { - var index = _splitWeights.Count; - - _splitWeights[index] = weight; - _splitValues[index] = 0; - - return new Progress(p => - { - using (_lock.EnterScope()) - { - _splitValues[index] = p; - - var weightedSum = 0.0; - var weightedMax = 0.0; - - for (var i = 0; i < _splitWeights.Count; i++) - { - weightedSum += _splitWeights[i] * _splitValues[i]; - weightedMax += _splitWeights[i]; - } - - target.Report(weightedSum / weightedMax); - } - }); - } - } -} diff --git a/YoutubeExplode.Converter/YoutubeExplode.Converter.csproj b/YoutubeExplode.Converter/YoutubeExplode.Converter.csproj index 0b214e2c..592c8d7f 100644 --- a/YoutubeExplode.Converter/YoutubeExplode.Converter.csproj +++ b/YoutubeExplode.Converter/YoutubeExplode.Converter.csproj @@ -27,6 +27,7 @@ + diff --git a/YoutubeExplode.Demo.Gui/Utils/DelegateProgress.cs b/YoutubeExplode.Demo.Gui/Utils/DelegateProgress.cs deleted file mode 100644 index 90bfaf7f..00000000 --- a/YoutubeExplode.Demo.Gui/Utils/DelegateProgress.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace YoutubeExplode.Demo.Gui.Utils; - -// Straightforward implementation of IProgress that simply invokes a delegate -// without using any synchronization (unlike the built-in Progress class). -// This is required in Avalonia because the built-in Progress class causes race conditions. -internal class DelegateProgress(Action report) : IProgress -{ - public void Report(T value) => report(value); -} diff --git a/YoutubeExplode.Demo.Gui/Utils/Extensions/PathExtensions.cs b/YoutubeExplode.Demo.Gui/Utils/Extensions/PathExtensions.cs deleted file mode 100644 index 7baf46c6..00000000 --- a/YoutubeExplode.Demo.Gui/Utils/Extensions/PathExtensions.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.IO; - -namespace YoutubeExplode.Demo.Gui.Utils.Extensions; - -internal static class PathExtensions -{ - extension(Path) - { - public static string SanitizeFileName(string fileName, char replacement = '_') - { - foreach (var invalidChar in Path.GetInvalidFileNameChars()) - fileName = fileName.Replace(invalidChar, replacement); - - return fileName; - } - } -} diff --git a/YoutubeExplode.Demo.Gui/ViewModels/MainViewModel.cs b/YoutubeExplode.Demo.Gui/ViewModels/MainViewModel.cs index 7b10ca32..d615efdf 100644 --- a/YoutubeExplode.Demo.Gui/ViewModels/MainViewModel.cs +++ b/YoutubeExplode.Demo.Gui/ViewModels/MainViewModel.cs @@ -7,9 +7,10 @@ using Avalonia.Platform.Storage; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; +using PowerKit; +using PowerKit.Extensions; using YoutubeExplode.Channels; using YoutubeExplode.Common; -using YoutubeExplode.Demo.Gui.Utils; using YoutubeExplode.Demo.Gui.Utils.Extensions; using YoutubeExplode.Videos; using YoutubeExplode.Videos.ClosedCaptions; @@ -182,9 +183,7 @@ private async Task DownloadStreamAsync(IStreamInfo? streamInfo) Progress = 0; // Generate a default file name - var defaultFileName = Path.SanitizeFileName( - $"{Video.Title}.{streamInfo.Container.Name}" - ); + var defaultFileName = Path.EscapeFileName($"{Video.Title}.{streamInfo.Container.Name}"); // Prompt for file path var filePath = await PromptSaveFilePathAsync( @@ -225,7 +224,7 @@ private async Task DownloadClosedCaptionTrackAsync(ClosedCaptionTrackInfo? track Progress = 0; // Generate a default file name - var defaultFileName = Path.SanitizeFileName( + var defaultFileName = Path.EscapeFileName( $"{Video.Title}.{trackInfo.Language.Name}.srt" ); diff --git a/YoutubeExplode.Demo.Gui/YoutubeExplode.Demo.Gui.csproj b/YoutubeExplode.Demo.Gui/YoutubeExplode.Demo.Gui.csproj index 2ba3eeda..63d1389d 100644 --- a/YoutubeExplode.Demo.Gui/YoutubeExplode.Demo.Gui.csproj +++ b/YoutubeExplode.Demo.Gui/YoutubeExplode.Demo.Gui.csproj @@ -17,6 +17,7 @@ + diff --git a/YoutubeExplode.Tests/ClosedCaptionSpecs.cs b/YoutubeExplode.Tests/ClosedCaptionSpecs.cs index 1a6680fc..fb976c5a 100644 --- a/YoutubeExplode.Tests/ClosedCaptionSpecs.cs +++ b/YoutubeExplode.Tests/ClosedCaptionSpecs.cs @@ -2,9 +2,9 @@ using System.IO; using System.Threading.Tasks; using FluentAssertions; +using PowerKit; using Xunit; using YoutubeExplode.Tests.TestData; -using YoutubeExplode.Tests.Utils; namespace YoutubeExplode.Tests; diff --git a/YoutubeExplode.Tests/StreamSpecs.cs b/YoutubeExplode.Tests/StreamSpecs.cs index c657fa91..72e24383 100644 --- a/YoutubeExplode.Tests/StreamSpecs.cs +++ b/YoutubeExplode.Tests/StreamSpecs.cs @@ -3,11 +3,11 @@ using System.Linq; using System.Threading.Tasks; using FluentAssertions; +using PowerKit; using Xunit; using Xunit.Abstractions; using YoutubeExplode.Exceptions; using YoutubeExplode.Tests.TestData; -using YoutubeExplode.Tests.Utils; using YoutubeExplode.Videos.Streams; namespace YoutubeExplode.Tests; diff --git a/YoutubeExplode.Tests/Utils/TempFile.cs b/YoutubeExplode.Tests/Utils/TempFile.cs deleted file mode 100644 index 18793874..00000000 --- a/YoutubeExplode.Tests/Utils/TempFile.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.IO; -using System.Reflection; - -namespace YoutubeExplode.Tests.Utils; - -internal partial class TempFile(string path) : IDisposable -{ - public string Path { get; } = path; - - public void Dispose() - { - try - { - File.Delete(Path); - } - catch (FileNotFoundException) { } - } -} - -internal partial class TempFile -{ - public static TempFile Create() - { - var dirPath = System.IO.Path.Combine( - System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) - ?? Directory.GetCurrentDirectory(), - "Temp" - ); - - Directory.CreateDirectory(dirPath); - - var filePath = System.IO.Path.Combine(dirPath, Guid.NewGuid() + ".tmp"); - - return new TempFile(filePath); - } -} diff --git a/YoutubeExplode.Tests/YoutubeExplode.Tests.csproj b/YoutubeExplode.Tests/YoutubeExplode.Tests.csproj index 0845d734..7fd231aa 100644 --- a/YoutubeExplode.Tests/YoutubeExplode.Tests.csproj +++ b/YoutubeExplode.Tests/YoutubeExplode.Tests.csproj @@ -17,6 +17,7 @@ + diff --git a/YoutubeExplode/Bridge/ChannelPage.cs b/YoutubeExplode/Bridge/ChannelPage.cs index af0adf4d..22d290c6 100644 --- a/YoutubeExplode/Bridge/ChannelPage.cs +++ b/YoutubeExplode/Bridge/ChannelPage.cs @@ -1,8 +1,8 @@ using System; using AngleSharp.Html.Dom; using Lazy; +using PowerKit.Extensions; using YoutubeExplode.Utils; -using YoutubeExplode.Utils.Extensions; namespace YoutubeExplode.Bridge; diff --git a/YoutubeExplode/Bridge/Cipher/ReverseCipherOperation.cs b/YoutubeExplode/Bridge/Cipher/ReverseCipherOperation.cs index 18c2bbf3..8d0bf891 100644 --- a/YoutubeExplode/Bridge/Cipher/ReverseCipherOperation.cs +++ b/YoutubeExplode/Bridge/Cipher/ReverseCipherOperation.cs @@ -1,5 +1,5 @@ using System.Diagnostics.CodeAnalysis; -using YoutubeExplode.Utils.Extensions; +using PowerKit.Extensions; namespace YoutubeExplode.Bridge.Cipher; diff --git a/YoutubeExplode/Bridge/Cipher/SwapCipherOperation.cs b/YoutubeExplode/Bridge/Cipher/SwapCipherOperation.cs index ede39089..c29ed139 100644 --- a/YoutubeExplode/Bridge/Cipher/SwapCipherOperation.cs +++ b/YoutubeExplode/Bridge/Cipher/SwapCipherOperation.cs @@ -1,11 +1,12 @@ using System.Diagnostics.CodeAnalysis; -using YoutubeExplode.Utils.Extensions; +using System.Text; namespace YoutubeExplode.Bridge.Cipher; internal class SwapCipherOperation(int index) : ICipherOperation { - public string Decipher(string input) => input.SwapChars(0, index); + public string Decipher(string input) => + new StringBuilder(input) { [0] = input[index], [index] = input[0] }.ToString(); [ExcludeFromCodeCoverage] public override string ToString() => $"Swap ({index})"; diff --git a/YoutubeExplode/Bridge/ClosedCaptionTrackResponse.cs b/YoutubeExplode/Bridge/ClosedCaptionTrackResponse.cs index f890c795..7b902a46 100644 --- a/YoutubeExplode/Bridge/ClosedCaptionTrackResponse.cs +++ b/YoutubeExplode/Bridge/ClosedCaptionTrackResponse.cs @@ -3,8 +3,8 @@ using System.Linq; using System.Xml.Linq; using Lazy; +using PowerKit.Extensions; using YoutubeExplode.Utils; -using YoutubeExplode.Utils.Extensions; namespace YoutubeExplode.Bridge; diff --git a/YoutubeExplode/Bridge/DashManifest.cs b/YoutubeExplode/Bridge/DashManifest.cs index 8e31fb55..2228f92b 100644 --- a/YoutubeExplode/Bridge/DashManifest.cs +++ b/YoutubeExplode/Bridge/DashManifest.cs @@ -5,8 +5,8 @@ using System.Text.RegularExpressions; using System.Xml.Linq; using Lazy; +using PowerKit.Extensions; using YoutubeExplode.Utils; -using YoutubeExplode.Utils.Extensions; namespace YoutubeExplode.Bridge; @@ -54,11 +54,7 @@ public class StreamData(XElement content) : IStreamData (long?)content.Attribute("contentLength") ?? Url?.Pipe(s => Regex.Match(s, @"[/\?]clen[/=](\d+)").Groups[1].Value) .NullIfWhiteSpace() - ?.Pipe(s => - long.TryParse(s, CultureInfo.InvariantCulture, out var result) - ? result - : (long?)null - ); + ?.Pipe(s => long.ParseOrNull(s, CultureInfo.InvariantCulture)); [Lazy] public long? Bitrate => (long?)content.Attribute("bandwidth"); diff --git a/YoutubeExplode/Bridge/PlayerResponse.cs b/YoutubeExplode/Bridge/PlayerResponse.cs index 4ca4feec..35131cb1 100644 --- a/YoutubeExplode/Bridge/PlayerResponse.cs +++ b/YoutubeExplode/Bridge/PlayerResponse.cs @@ -5,9 +5,10 @@ using System.Text; using System.Text.Json; using System.Text.RegularExpressions; +using JsonExtensions.Reading; using Lazy; +using PowerKit.Extensions; using YoutubeExplode.Utils; -using YoutubeExplode.Utils.Extensions; namespace YoutubeExplode.Bridge; @@ -57,11 +58,7 @@ internal partial class PlayerResponse(JsonElement content) Details ?.GetPropertyOrNull("lengthSeconds") ?.GetStringOrNull() - ?.Pipe(s => - double.TryParse(s, CultureInfo.InvariantCulture, out var result) - ? result - : (double?)null - ) + ?.Pipe(s => double.ParseOrNull(s, CultureInfo.InvariantCulture)) ?.Pipe(TimeSpan.FromSeconds); [Lazy] @@ -71,7 +68,8 @@ internal partial class PlayerResponse(JsonElement content) ?.GetPropertyOrNull("thumbnails") ?.EnumerateArrayOrNull() ?.Select(j => new ThumbnailData(j)) - .ToArray() ?? []; + .ToArray() + ?? []; public IReadOnlyList Keywords => Details @@ -79,7 +77,8 @@ internal partial class PlayerResponse(JsonElement content) ?.EnumerateArrayOrNull() ?.Select(j => j.GetStringOrNull()) .WhereNotNull() - .ToArray() ?? []; + .ToArray() + ?? []; [Lazy] public string? Description => Details?.GetPropertyOrNull("shortDescription")?.GetStringOrNull(); @@ -89,11 +88,7 @@ internal partial class PlayerResponse(JsonElement content) Details ?.GetPropertyOrNull("viewCount") ?.GetStringOrNull() - ?.Pipe(s => - long.TryParse(s, CultureInfo.InvariantCulture, out var result) - ? result - : (long?)null - ); + ?.Pipe(s => long.ParseOrNull(s, CultureInfo.InvariantCulture)); [Lazy] public string? PreviewVideoId => @@ -172,7 +167,8 @@ public IReadOnlyList Streams ?.GetPropertyOrNull("captionTracks") ?.EnumerateArrayOrNull() ?.Select(j => new ClosedCaptionTrackData(j)) - .ToArray() ?? []; + .ToArray() + ?? []; } internal partial class PlayerResponse @@ -201,7 +197,8 @@ public class ClosedCaptionTrackData(JsonElement content) content .GetPropertyOrNull("vssId") ?.GetStringOrNull() - ?.StartsWith("a.", StringComparison.OrdinalIgnoreCase) ?? false; + ?.StartsWith("a.", StringComparison.OrdinalIgnoreCase) + ?? false; } } @@ -236,18 +233,10 @@ public class StreamData(JsonElement content) : IStreamData content .GetPropertyOrNull("contentLength") ?.GetStringOrNull() - ?.Pipe(s => - long.TryParse(s, CultureInfo.InvariantCulture, out var result) - ? result - : (long?)null - ) + ?.Pipe(s => long.ParseOrNull(s, CultureInfo.InvariantCulture)) ?? Url?.Pipe(s => UrlEx.TryGetQueryParameterValue(s, "clen")) ?.NullIfWhiteSpace() - ?.Pipe(s => - long.TryParse(s, CultureInfo.InvariantCulture, out var result) - ? result - : (long?)null - ); + ?.Pipe(s => long.ParseOrNull(s, CultureInfo.InvariantCulture)); [Lazy] public long? Bitrate => content.GetPropertyOrNull("bitrate")?.GetInt64OrNull(); diff --git a/YoutubeExplode/Bridge/PlayerSource.cs b/YoutubeExplode/Bridge/PlayerSource.cs index 4c486740..7d3d76e8 100644 --- a/YoutubeExplode/Bridge/PlayerSource.cs +++ b/YoutubeExplode/Bridge/PlayerSource.cs @@ -3,8 +3,8 @@ using System.Globalization; using System.Text.RegularExpressions; using Lazy; +using PowerKit.Extensions; using YoutubeExplode.Bridge.Cipher; -using YoutubeExplode.Utils.Extensions; namespace YoutubeExplode.Bridge; diff --git a/YoutubeExplode/Bridge/PlaylistBrowseResponse.cs b/YoutubeExplode/Bridge/PlaylistBrowseResponse.cs index 781660ab..ec2a8dbd 100644 --- a/YoutubeExplode/Bridge/PlaylistBrowseResponse.cs +++ b/YoutubeExplode/Bridge/PlaylistBrowseResponse.cs @@ -2,9 +2,10 @@ using System.Globalization; using System.Linq; using System.Text.Json; +using JsonExtensions.Reading; using Lazy; +using PowerKit.Extensions; using YoutubeExplode.Utils; -using YoutubeExplode.Utils.Extensions; namespace YoutubeExplode.Bridge; @@ -113,9 +114,7 @@ internal partial class PlaylistBrowseResponse(JsonElement content) : IPlaylistDa ?.FirstOrNull() ?.GetPropertyOrNull("text") ?.GetStringOrNull() - ?.Pipe(s => - int.TryParse(s, CultureInfo.InvariantCulture, out var result) ? result : (int?)null - ) + ?.Pipe(s => int.ParseOrNull(s, CultureInfo.InvariantCulture)) ?? SidebarPrimary ?.GetPropertyOrNull("stats") ?.EnumerateArrayOrNull() @@ -124,9 +123,7 @@ internal partial class PlaylistBrowseResponse(JsonElement content) : IPlaylistDa ?.GetStringOrNull() ?.Split(' ') ?.FirstOrDefault() - ?.Pipe(s => - int.TryParse(s, CultureInfo.InvariantCulture, out var result) ? result : (int?)null - ); + ?.Pipe(s => int.ParseOrNull(s, CultureInfo.InvariantCulture)); [Lazy] public IReadOnlyList Thumbnails => diff --git a/YoutubeExplode/Bridge/PlaylistNextResponse.cs b/YoutubeExplode/Bridge/PlaylistNextResponse.cs index 7497d540..447c245e 100644 --- a/YoutubeExplode/Bridge/PlaylistNextResponse.cs +++ b/YoutubeExplode/Bridge/PlaylistNextResponse.cs @@ -2,9 +2,10 @@ using System.Globalization; using System.Linq; using System.Text.Json; +using JsonExtensions.Reading; using Lazy; +using PowerKit.Extensions; using YoutubeExplode.Utils; -using YoutubeExplode.Utils.Extensions; namespace YoutubeExplode.Bridge; @@ -44,9 +45,7 @@ internal partial class PlaylistNextResponse(JsonElement content) : IPlaylistData ?.FirstOrNull() ?.GetPropertyOrNull("text") ?.GetStringOrNull() - ?.Pipe(s => - int.TryParse(s, CultureInfo.InvariantCulture, out var result) ? result : (int?)null - ) + ?.Pipe(s => int.ParseOrNull(s, CultureInfo.InvariantCulture)) ?? ContentRoot ?.GetPropertyOrNull("videoCountText") ?.GetPropertyOrNull("runs") @@ -54,9 +53,7 @@ internal partial class PlaylistNextResponse(JsonElement content) : IPlaylistData ?.ElementAtOrNull(2) ?.GetPropertyOrNull("text") ?.GetStringOrNull() - ?.Pipe(s => - int.TryParse(s, CultureInfo.InvariantCulture, out var result) ? result : (int?)null - ); + ?.Pipe(s => int.ParseOrNull(s, CultureInfo.InvariantCulture)); [Lazy] public IReadOnlyList Thumbnails => Videos.FirstOrDefault()?.Thumbnails ?? []; @@ -69,7 +66,8 @@ internal partial class PlaylistNextResponse(JsonElement content) : IPlaylistData ?.Select(j => j.GetPropertyOrNull("playlistPanelVideoRenderer")) .WhereNotNull() .Select(j => new PlaylistVideoData(j)) - .ToArray() ?? []; + .ToArray() + ?? []; [Lazy] public string? VisitorData => diff --git a/YoutubeExplode/Bridge/PlaylistVideoData.cs b/YoutubeExplode/Bridge/PlaylistVideoData.cs index f386dd1e..0f95019f 100644 --- a/YoutubeExplode/Bridge/PlaylistVideoData.cs +++ b/YoutubeExplode/Bridge/PlaylistVideoData.cs @@ -3,8 +3,9 @@ using System.Globalization; using System.Linq; using System.Text.Json; +using JsonExtensions.Reading; using Lazy; -using YoutubeExplode.Utils.Extensions; +using PowerKit.Extensions; namespace YoutubeExplode.Bridge; @@ -82,11 +83,7 @@ internal class PlaylistVideoData(JsonElement content) content .GetPropertyOrNull("lengthSeconds") ?.GetStringOrNull() - ?.Pipe(s => - double.TryParse(s, CultureInfo.InvariantCulture, out var result) - ? result - : (double?)null - ) + ?.Pipe(s => double.ParseOrNull(s, CultureInfo.InvariantCulture)) ?.Pipe(TimeSpan.FromSeconds) ?? content .GetPropertyOrNull("lengthText") @@ -127,5 +124,6 @@ out var result ?.GetPropertyOrNull("thumbnails") ?.EnumerateArrayOrNull() ?.Select(j => new ThumbnailData(j)) - .ToArray() ?? []; + .ToArray() + ?? []; } diff --git a/YoutubeExplode/Bridge/SearchResponse.cs b/YoutubeExplode/Bridge/SearchResponse.cs index af645bab..79011f68 100644 --- a/YoutubeExplode/Bridge/SearchResponse.cs +++ b/YoutubeExplode/Bridge/SearchResponse.cs @@ -3,7 +3,9 @@ using System.Globalization; using System.Linq; using System.Text.Json; +using JsonExtensions.Reading; using Lazy; +using PowerKit.Extensions; using YoutubeExplode.Utils; using YoutubeExplode.Utils.Extensions; @@ -24,7 +26,8 @@ internal partial class SearchResponse(JsonElement content) ContentRoot ?.EnumerateDescendantProperties("videoRenderer") .Select(j => new VideoData(j)) - .ToArray() ?? []; + .ToArray() + ?? []; [Lazy] public IReadOnlyList Playlists => @@ -43,7 +46,8 @@ internal partial class SearchResponse(JsonElement content) ContentRoot ?.EnumerateDescendantProperties("channelRenderer") .Select(j => new ChannelData(j)) - .ToArray() ?? []; + .ToArray() + ?? []; [Lazy] public string? ContinuationToken => @@ -144,7 +148,8 @@ out var result ?.GetPropertyOrNull("thumbnails") ?.EnumerateArrayOrNull() ?.Select(j => new ThumbnailData(j)) - .ToArray() ?? []; + .ToArray() + ?? []; } } @@ -259,7 +264,8 @@ public class ChannelData(JsonElement content) ?.GetPropertyOrNull("thumbnails") ?.EnumerateArrayOrNull() ?.Select(j => new ThumbnailData(j)) - .ToArray() ?? []; + .ToArray() + ?? []; } } diff --git a/YoutubeExplode/Bridge/ThumbnailData.cs b/YoutubeExplode/Bridge/ThumbnailData.cs index 71193e91..7f6ec1a6 100644 --- a/YoutubeExplode/Bridge/ThumbnailData.cs +++ b/YoutubeExplode/Bridge/ThumbnailData.cs @@ -1,6 +1,6 @@ using System.Text.Json; +using JsonExtensions.Reading; using Lazy; -using YoutubeExplode.Utils.Extensions; namespace YoutubeExplode.Bridge; diff --git a/YoutubeExplode/Bridge/VideoWatchPage.cs b/YoutubeExplode/Bridge/VideoWatchPage.cs index 6f2af388..086faecd 100644 --- a/YoutubeExplode/Bridge/VideoWatchPage.cs +++ b/YoutubeExplode/Bridge/VideoWatchPage.cs @@ -5,7 +5,9 @@ using System.Text.RegularExpressions; using AngleSharp.Dom; using AngleSharp.Html.Dom; +using JsonExtensions.Reading; using Lazy; +using PowerKit.Extensions; using YoutubeExplode.Utils; using YoutubeExplode.Utils.Extensions; @@ -23,18 +25,14 @@ internal partial class VideoWatchPage(IHtmlDocument content) ?.GetAttribute("content") ?.NullIfWhiteSpace() ?.Pipe(s => - DateTimeOffset.TryParse(s, CultureInfo.InvariantCulture, out var result) - ? result - : (DateTimeOffset?)null + DateTimeOffset.ParseOrNull(s, CultureInfo.InvariantCulture, DateTimeStyles.None) ) ?? content .QuerySelector("meta[itemprop=\"datePublished\"]") ?.GetAttribute("content") ?.NullIfWhiteSpace() ?.Pipe(s => - DateTimeOffset.TryParse(s, CultureInfo.InvariantCulture, out var result) - ? result - : (DateTimeOffset?)null + DateTimeOffset.ParseOrNull(s, CultureInfo.InvariantCulture, DateTimeStyles.None) ); [Lazy] @@ -53,11 +51,7 @@ internal partial class VideoWatchPage(IHtmlDocument content) ) .NullIfWhiteSpace() ?.StripNonDigit() - .Pipe(s => - long.TryParse(s, CultureInfo.InvariantCulture, out var result) - ? result - : (long?)null - ) + .Pipe(s => long.ParseOrNull(s, CultureInfo.InvariantCulture)) ?? content .Source.Text.Pipe(s => Regex @@ -72,11 +66,7 @@ along with ([\d,\.]+) other people" ) .NullIfWhiteSpace() ?.StripNonDigit() - .Pipe(s => - long.TryParse(s, CultureInfo.InvariantCulture, out var result) - ? result - : (long?)null - ); + .Pipe(s => long.ParseOrNull(s, CultureInfo.InvariantCulture)); [Lazy] public long? DislikeCount => @@ -94,11 +84,7 @@ along with ([\d,\.]+) other people" ) .NullIfWhiteSpace() ?.StripNonDigit() - .Pipe(s => - long.TryParse(s, CultureInfo.InvariantCulture, out var result) - ? result - : (long?)null - ); + .Pipe(s => long.ParseOrNull(s, CultureInfo.InvariantCulture)); [Lazy] private JsonElement? PlayerConfig => @@ -106,8 +92,8 @@ along with ([\d,\.]+) other people" .GetElementsByTagName("script") .Select(e => e.Text()) .Select(s => Regex.Match(s, @"ytplayer\.config\s*=\s*(\{.*\})").Groups[1].Value) - .FirstOrDefault(s => !string.IsNullOrWhiteSpace(s)) - ?.NullIfWhiteSpace() + .WhereNotNullOrWhiteSpace() + .FirstOrDefault() ?.Pipe(Json.Extract) .Pipe(Json.TryParse); @@ -119,8 +105,8 @@ along with ([\d,\.]+) other people" .Select(s => Regex.Match(s, @"var\s+ytInitialPlayerResponse\s*=\s*(\{.*\})").Groups[1].Value ) - .FirstOrDefault(s => !string.IsNullOrWhiteSpace(s)) - ?.NullIfWhiteSpace() + .WhereNotNullOrWhiteSpace() + .FirstOrDefault() ?.Pipe(Json.Extract) .Pipe(Json.TryParse) ?.Pipe(j => new PlayerResponse(j)) diff --git a/YoutubeExplode/Channels/ChannelClient.cs b/YoutubeExplode/Channels/ChannelClient.cs index 8cf531c2..2ba4424e 100644 --- a/YoutubeExplode/Channels/ChannelClient.cs +++ b/YoutubeExplode/Channels/ChannelClient.cs @@ -5,11 +5,11 @@ using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; +using PowerKit.Extensions; using YoutubeExplode.Bridge; using YoutubeExplode.Common; using YoutubeExplode.Exceptions; using YoutubeExplode.Playlists; -using YoutubeExplode.Utils.Extensions; namespace YoutubeExplode.Channels; @@ -41,11 +41,7 @@ private Channel Get(ChannelPage channelPage) .LastOrDefault() ?.Groups[1] .Value.NullIfWhiteSpace() - ?.Pipe(s => - int.TryParse(s, CultureInfo.InvariantCulture, out var result) - ? result - : (int?)null - ) + ?.Pipe(s => int.ParseOrNull(s, CultureInfo.InvariantCulture)) ?? 100; var thumbnails = new[] { new Thumbnail(logoUrl, new Resolution(logoSize, logoSize)) }; diff --git a/YoutubeExplode/Channels/ChannelHandle.cs b/YoutubeExplode/Channels/ChannelHandle.cs index a5b97350..9c69c73a 100644 --- a/YoutubeExplode/Channels/ChannelHandle.cs +++ b/YoutubeExplode/Channels/ChannelHandle.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Net; using System.Text.RegularExpressions; -using YoutubeExplode.Utils.Extensions; +using PowerKit.Extensions; namespace YoutubeExplode.Channels; diff --git a/YoutubeExplode/Channels/ChannelId.cs b/YoutubeExplode/Channels/ChannelId.cs index 769728fe..0698ac91 100644 --- a/YoutubeExplode/Channels/ChannelId.cs +++ b/YoutubeExplode/Channels/ChannelId.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Net; using System.Text.RegularExpressions; -using YoutubeExplode.Utils.Extensions; +using PowerKit.Extensions; namespace YoutubeExplode.Channels; diff --git a/YoutubeExplode/Channels/ChannelSlug.cs b/YoutubeExplode/Channels/ChannelSlug.cs index f0879dfa..ba6f8104 100644 --- a/YoutubeExplode/Channels/ChannelSlug.cs +++ b/YoutubeExplode/Channels/ChannelSlug.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Net; using System.Text.RegularExpressions; -using YoutubeExplode.Utils.Extensions; +using PowerKit.Extensions; namespace YoutubeExplode.Channels; diff --git a/YoutubeExplode/Channels/UserName.cs b/YoutubeExplode/Channels/UserName.cs index 10fbfcfd..56ff0ce9 100644 --- a/YoutubeExplode/Channels/UserName.cs +++ b/YoutubeExplode/Channels/UserName.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Net; using System.Text.RegularExpressions; -using YoutubeExplode.Utils.Extensions; +using PowerKit.Extensions; namespace YoutubeExplode.Channels; diff --git a/YoutubeExplode/Common/Batch.cs b/YoutubeExplode/Common/Batch.cs index 46bd87b3..01632417 100644 --- a/YoutubeExplode/Common/Batch.cs +++ b/YoutubeExplode/Common/Batch.cs @@ -1,5 +1,5 @@ using System.Collections.Generic; -using YoutubeExplode.Utils.Extensions; +using PowerKit.Extensions; namespace YoutubeExplode.Common; diff --git a/YoutubeExplode/Common/IBatchItem.cs b/YoutubeExplode/Common/IBatchItem.cs index d8ebfea8..584ea279 100644 --- a/YoutubeExplode/Common/IBatchItem.cs +++ b/YoutubeExplode/Common/IBatchItem.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Threading.Tasks; -using YoutubeExplode.Utils.Extensions; +using PowerKit.Extensions; namespace YoutubeExplode.Common; diff --git a/YoutubeExplode/Playlists/PlaylistId.cs b/YoutubeExplode/Playlists/PlaylistId.cs index 5cb2d75e..b3f36fb5 100644 --- a/YoutubeExplode/Playlists/PlaylistId.cs +++ b/YoutubeExplode/Playlists/PlaylistId.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Net; using System.Text.RegularExpressions; -using YoutubeExplode.Utils.Extensions; +using PowerKit.Extensions; namespace YoutubeExplode.Playlists; diff --git a/YoutubeExplode/Search/SearchClient.cs b/YoutubeExplode/Search/SearchClient.cs index 04bbbb1b..eb0ad827 100644 --- a/YoutubeExplode/Search/SearchClient.cs +++ b/YoutubeExplode/Search/SearchClient.cs @@ -5,10 +5,10 @@ using System.Net.Http; using System.Runtime.CompilerServices; using System.Threading; +using PowerKit.Extensions; using YoutubeExplode.Channels; using YoutubeExplode.Common; using YoutubeExplode.Exceptions; -using YoutubeExplode.Utils.Extensions; namespace YoutubeExplode.Search; diff --git a/YoutubeExplode/Utils/ClientDelegatingHandler.cs b/YoutubeExplode/Utils/ClientDelegatingHandler.cs deleted file mode 100644 index 4b2d82ae..00000000 --- a/YoutubeExplode/Utils/ClientDelegatingHandler.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using YoutubeExplode.Utils.Extensions; - -namespace YoutubeExplode.Utils; - -// Like DelegatingHandler, but wraps an HttpClient instead of an HttpMessageHandler. -// Used to extend an externally provided HttpClient with additional behavior. -internal abstract class ClientDelegatingHandler(HttpClient http, bool disposeClient = false) - : HttpMessageHandler -{ - protected override async Task SendAsync( - HttpRequestMessage request, - CancellationToken cancellationToken - ) - { - // Clone the request to reset its completion status, which is required - // in order to pass the request from one HttpClient to another. - using var clonedRequest = request.Clone(); - - return await http.SendAsync( - clonedRequest, - HttpCompletionOption.ResponseHeadersRead, - cancellationToken - ); - } - - protected override void Dispose(bool disposing) - { - if (disposing && disposeClient) - http.Dispose(); - - base.Dispose(disposing); - } -} diff --git a/YoutubeExplode/Utils/Extensions/AsyncCollectionExtensions.cs b/YoutubeExplode/Utils/Extensions/AsyncCollectionExtensions.cs deleted file mode 100644 index 6f9e23a9..00000000 --- a/YoutubeExplode/Utils/Extensions/AsyncCollectionExtensions.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; - -namespace YoutubeExplode.Utils.Extensions; - -internal static class AsyncCollectionExtensions -{ - extension(IAsyncEnumerable source) - { - public async IAsyncEnumerable TakeAsync(int count) - { - var currentCount = 0; - - await foreach (var i in source) - { - if (currentCount >= count) - yield break; - - yield return i; - currentCount++; - } - } - - public async IAsyncEnumerable SelectManyAsync(Func> transform) - { - await foreach (var i in source) - { - foreach (var j in transform(i)) - yield return j; - } - } - - public async ValueTask> ToListAsync() - { - var list = new List(); - - await foreach (var i in source) - list.Add(i); - - return list; - } - - public ValueTaskAwaiter> GetAwaiter() => source.ToListAsync().GetAwaiter(); - } - - extension(IAsyncEnumerable source) - { - public async IAsyncEnumerable OfTypeAsync() - { - await foreach (var i in source) - { - if (i is T match) - yield return match; - } - } - } -} diff --git a/YoutubeExplode/Utils/Extensions/CollectionExtensions.cs b/YoutubeExplode/Utils/Extensions/CollectionExtensions.cs deleted file mode 100644 index f4208b73..00000000 --- a/YoutubeExplode/Utils/Extensions/CollectionExtensions.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System.Collections.Generic; -using System.Linq; - -namespace YoutubeExplode.Utils.Extensions; - -internal static class CollectionExtensions -{ - extension(IEnumerable source) - where T : class - { - public IEnumerable WhereNotNull() - { - foreach (var i in source) - { - if (i is not null) - yield return i; - } - } - } - - extension(IEnumerable source) - where T : struct - { - public IEnumerable WhereNotNull() - { - foreach (var i in source) - { - if (i is not null) - yield return i.Value; - } - } - } - - extension(IEnumerable source) - where T : struct - { - public T? ElementAtOrNull(int index) - { - var sourceAsList = source as IReadOnlyList ?? source.ToArray(); - return index < sourceAsList.Count ? sourceAsList[index] : null; - } - - public T? FirstOrNull() - { - foreach (var i in source) - return i; - - return null; - } - } -} diff --git a/YoutubeExplode/Utils/Extensions/GenericExtensions.cs b/YoutubeExplode/Utils/Extensions/GenericExtensions.cs deleted file mode 100644 index 0b84224a..00000000 --- a/YoutubeExplode/Utils/Extensions/GenericExtensions.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace YoutubeExplode.Utils.Extensions; - -internal static class GenericExtensions -{ - extension(TIn input) - { - public TOut Pipe(Func transform) => transform(input); - } -} diff --git a/YoutubeExplode/Utils/Extensions/HttpExtensions.cs b/YoutubeExplode/Utils/Extensions/HttpExtensions.cs deleted file mode 100644 index ee468d9a..00000000 --- a/YoutubeExplode/Utils/Extensions/HttpExtensions.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System.IO; -using System.Net; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; - -namespace YoutubeExplode.Utils.Extensions; - -internal static class HttpExtensions -{ - private class NonDisposableHttpContent(HttpContent content) : HttpContent - { - protected override async Task SerializeToStreamAsync( - Stream stream, - TransportContext? context - ) => await content.CopyToAsync(stream); - - protected override bool TryComputeLength(out long length) - { - length = 0; - return false; - } - } - - extension(HttpRequestMessage request) - { - public HttpRequestMessage Clone() - { - var clonedRequest = new HttpRequestMessage(request.Method, request.RequestUri) - { - Version = request.Version, - // Don't dispose the original request's content - Content = request.Content is not null - ? new NonDisposableHttpContent(request.Content) - : null, - }; - - foreach (var (key, value) in request.Headers) - clonedRequest.Headers.TryAddWithoutValidation(key, value); - - if (request.Content is not null && clonedRequest.Content is not null) - { - foreach (var (key, value) in request.Content.Headers) - clonedRequest.Content.Headers.TryAddWithoutValidation(key, value); - } - - return clonedRequest; - } - } - - extension(HttpClient http) - { - public async ValueTask HeadAsync( - string requestUri, - CancellationToken cancellationToken = default - ) - { - using var request = new HttpRequestMessage(HttpMethod.Head, requestUri); - - return await http.SendAsync( - request, - HttpCompletionOption.ResponseHeadersRead, - cancellationToken - ); - } - } -} diff --git a/YoutubeExplode/Utils/Extensions/JsonExtensions.cs b/YoutubeExplode/Utils/Extensions/JsonExtensions.cs index f8139c1a..06c531cf 100644 --- a/YoutubeExplode/Utils/Extensions/JsonExtensions.cs +++ b/YoutubeExplode/Utils/Extensions/JsonExtensions.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using System.Text.Json; +using JsonExtensions.Reading; namespace YoutubeExplode.Utils.Extensions; @@ -8,58 +9,6 @@ internal static class JsonExtensions { extension(JsonElement element) { - public JsonElement? GetPropertyOrNull(string propertyName) - { - if (element.ValueKind != JsonValueKind.Object) - { - return null; - } - - if ( - element.TryGetProperty(propertyName, out var result) - && result.ValueKind != JsonValueKind.Null - && result.ValueKind != JsonValueKind.Undefined - ) - { - return result; - } - - return null; - } - - public bool? GetBooleanOrNull() => - element.ValueKind switch - { - JsonValueKind.True => true, - JsonValueKind.False => false, - _ => null, - }; - - public string? GetStringOrNull() => - element.ValueKind == JsonValueKind.String ? element.GetString() : null; - - public int? GetInt32OrNull() => - element.ValueKind == JsonValueKind.Number && element.TryGetInt32(out var result) - ? result - : null; - - public long? GetInt64OrNull() => - element.ValueKind == JsonValueKind.Number && element.TryGetInt64(out var result) - ? result - : null; - - public JsonElement.ArrayEnumerator? EnumerateArrayOrNull() => - element.ValueKind == JsonValueKind.Array ? element.EnumerateArray() : null; - - public JsonElement.ArrayEnumerator EnumerateArrayOrEmpty() => - element.EnumerateArrayOrNull() ?? default; - - public JsonElement.ObjectEnumerator? EnumerateObjectOrNull() => - element.ValueKind == JsonValueKind.Object ? element.EnumerateObject() : null; - - public JsonElement.ObjectEnumerator EnumerateObjectOrEmpty() => - element.EnumerateObjectOrNull() ?? default; - public IEnumerable EnumerateDescendantProperties(string propertyName) { // Check if this property exists on the current object diff --git a/YoutubeExplode/Utils/Extensions/StreamExtensions.cs b/YoutubeExplode/Utils/Extensions/StreamExtensions.cs deleted file mode 100644 index 68439efc..00000000 --- a/YoutubeExplode/Utils/Extensions/StreamExtensions.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Buffers; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace YoutubeExplode.Utils.Extensions; - -internal static class StreamExtensions -{ - extension(Stream source) - { - public async ValueTask CopyToAsync( - Stream destination, - IProgress? progress = null, - CancellationToken cancellationToken = default - ) - { - using var buffer = MemoryPool.Shared.Rent(81920); - - var totalBytesRead = 0L; - while (true) - { - var bytesRead = await source.ReadAsync(buffer.Memory, cancellationToken); - if (bytesRead <= 0) - break; - - await destination.WriteAsync(buffer.Memory[..bytesRead], cancellationToken); - - totalBytesRead += bytesRead; - progress?.Report(1.0 * totalBytesRead / source.Length); - } - } - } -} diff --git a/YoutubeExplode/Utils/Extensions/StringExtensions.cs b/YoutubeExplode/Utils/Extensions/StringExtensions.cs index e0c9eb54..5c8245c2 100644 --- a/YoutubeExplode/Utils/Extensions/StringExtensions.cs +++ b/YoutubeExplode/Utils/Extensions/StringExtensions.cs @@ -1,4 +1,3 @@ -using System; using System.Linq; using System.Text; @@ -8,28 +7,6 @@ internal static class StringExtensions { extension(string str) { - public string? NullIfWhiteSpace() => !string.IsNullOrWhiteSpace(str) ? str : null; - - public string SubstringUntil( - string sub, - StringComparison comparison = StringComparison.Ordinal - ) => - str.IndexOf(sub, comparison) switch - { - >= 0 and var index => str[..index], - _ => str, - }; - - public string SubstringAfter( - string sub, - StringComparison comparison = StringComparison.Ordinal - ) => - str.IndexOf(sub, comparison) switch - { - >= 0 and var index => str[(index + sub.Length)..], - _ => "", - }; - public string StripNonDigit() { var buffer = new StringBuilder(); @@ -39,22 +16,5 @@ public string StripNonDigit() return buffer.ToString(); } - - public string Reverse() - { - var buffer = new StringBuilder(str.Length); - - for (var i = str.Length - 1; i >= 0; i--) - buffer.Append(str[i]); - - return buffer.ToString(); - } - - public string SwapChars(int firstCharIndex, int secondCharIndex) => - new StringBuilder(str) - { - [firstCharIndex] = str[secondCharIndex], - [secondCharIndex] = str[firstCharIndex], - }.ToString(); } } diff --git a/YoutubeExplode/Utils/Extensions/UriExtensions.cs b/YoutubeExplode/Utils/Extensions/UriExtensions.cs deleted file mode 100644 index 718f7b7f..00000000 --- a/YoutubeExplode/Utils/Extensions/UriExtensions.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace YoutubeExplode.Utils.Extensions; - -internal static class UriExtensions -{ - extension(Uri uri) - { - public string Domain => uri.Scheme + Uri.SchemeDelimiter + uri.Host; - } -} diff --git a/YoutubeExplode/Utils/Extensions/XElementExtensions.cs b/YoutubeExplode/Utils/Extensions/XElementExtensions.cs deleted file mode 100644 index 2c646fb9..00000000 --- a/YoutubeExplode/Utils/Extensions/XElementExtensions.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Linq; -using System.Xml.Linq; - -namespace YoutubeExplode.Utils.Extensions; - -internal static class XElementExtensions -{ - extension(XElement element) - { - public XElement StripNamespaces() - { - // Adapted from http://stackoverflow.com/a/1147012 - - var result = new XElement(element); - - foreach (var descendantElement in result.DescendantsAndSelf()) - { - descendantElement.Name = XNamespace.None.GetName(descendantElement.Name.LocalName); - - descendantElement.ReplaceAttributes( - descendantElement - .Attributes() - .Where(a => !a.IsNamespaceDeclaration) - .Where(a => - a.Name.Namespace != XNamespace.Xml - && a.Name.Namespace != XNamespace.Xmlns - ) - .Select(a => new XAttribute( - XNamespace.None.GetName(a.Name.LocalName), - a.Value - )) - ); - } - - return result; - } - } -} diff --git a/YoutubeExplode/Utils/Hash.cs b/YoutubeExplode/Utils/Hash.cs deleted file mode 100644 index 4512190c..00000000 --- a/YoutubeExplode/Utils/Hash.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Security.Cryptography; - -namespace YoutubeExplode.Utils; - -internal static class Hash -{ - public static byte[] Compute(HashAlgorithm algorithm, byte[] data) - { - using (algorithm) - return algorithm.ComputeHash(data); - } -} diff --git a/YoutubeExplode/Utils/UrlEx.cs b/YoutubeExplode/Utils/UrlEx.cs index 3b6424bf..215c637d 100644 --- a/YoutubeExplode/Utils/UrlEx.cs +++ b/YoutubeExplode/Utils/UrlEx.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Net; using System.Text; -using YoutubeExplode.Utils.Extensions; +using PowerKit.Extensions; namespace YoutubeExplode.Utils; diff --git a/YoutubeExplode/Utils/Xml.cs b/YoutubeExplode/Utils/Xml.cs index 1e8f770a..70959804 100644 --- a/YoutubeExplode/Utils/Xml.cs +++ b/YoutubeExplode/Utils/Xml.cs @@ -1,5 +1,5 @@ using System.Xml.Linq; -using YoutubeExplode.Utils.Extensions; +using PowerKit.Extensions; namespace YoutubeExplode.Utils; diff --git a/YoutubeExplode/Videos/ClosedCaptions/ClosedCaptionClient.cs b/YoutubeExplode/Videos/ClosedCaptions/ClosedCaptionClient.cs index 94a57b5b..64d44465 100644 --- a/YoutubeExplode/Videos/ClosedCaptions/ClosedCaptionClient.cs +++ b/YoutubeExplode/Videos/ClosedCaptions/ClosedCaptionClient.cs @@ -8,8 +8,8 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using PowerKit.Extensions; using YoutubeExplode.Exceptions; -using YoutubeExplode.Utils.Extensions; namespace YoutubeExplode.Videos.ClosedCaptions; diff --git a/YoutubeExplode/Videos/ClosedCaptions/ClosedCaptionController.cs b/YoutubeExplode/Videos/ClosedCaptions/ClosedCaptionController.cs index 1a17b8ac..846f86f4 100644 --- a/YoutubeExplode/Videos/ClosedCaptions/ClosedCaptionController.cs +++ b/YoutubeExplode/Videos/ClosedCaptions/ClosedCaptionController.cs @@ -1,9 +1,9 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using PowerKit.Extensions; using YoutubeExplode.Bridge; using YoutubeExplode.Utils; -using YoutubeExplode.Utils.Extensions; namespace YoutubeExplode.Videos.ClosedCaptions; diff --git a/YoutubeExplode/Videos/Streams/StreamClient.cs b/YoutubeExplode/Videos/Streams/StreamClient.cs index 634dadd5..2976ab18 100644 --- a/YoutubeExplode/Videos/Streams/StreamClient.cs +++ b/YoutubeExplode/Videos/Streams/StreamClient.cs @@ -7,12 +7,12 @@ using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; +using PowerKit.Extensions; using YoutubeExplode.Bridge; using YoutubeExplode.Bridge.Cipher; using YoutubeExplode.Common; using YoutubeExplode.Exceptions; using YoutubeExplode.Utils; -using YoutubeExplode.Utils.Extensions; using YoutubeExplode.Videos.ClosedCaptions; namespace YoutubeExplode.Videos.Streams; diff --git a/YoutubeExplode/Videos/Streams/VideoQuality.cs b/YoutubeExplode/Videos/Streams/VideoQuality.cs index c5087741..85d983da 100644 --- a/YoutubeExplode/Videos/Streams/VideoQuality.cs +++ b/YoutubeExplode/Videos/Streams/VideoQuality.cs @@ -1,8 +1,8 @@ using System; using System.Globalization; using System.Text.RegularExpressions; +using PowerKit.Extensions; using YoutubeExplode.Common; -using YoutubeExplode.Utils.Extensions; namespace YoutubeExplode.Videos.Streams; @@ -88,9 +88,7 @@ internal static VideoQuality FromLabel(string label, int framerateFallback) var framerate = match .Groups[2] .Value.NullIfWhiteSpace() - ?.Pipe(s => - int.TryParse(s, CultureInfo.InvariantCulture, out var result) ? result : (int?)null - ); + ?.Pipe(s => int.ParseOrNull(s, CultureInfo.InvariantCulture)); return new VideoQuality(label, maxHeight, framerate ?? framerateFallback); } diff --git a/YoutubeExplode/Videos/VideoController.cs b/YoutubeExplode/Videos/VideoController.cs index 6fcfd0da..8132a89e 100644 --- a/YoutubeExplode/Videos/VideoController.cs +++ b/YoutubeExplode/Videos/VideoController.cs @@ -2,10 +2,10 @@ using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; +using JsonExtensions.Reading; using YoutubeExplode.Bridge; using YoutubeExplode.Exceptions; using YoutubeExplode.Utils; -using YoutubeExplode.Utils.Extensions; namespace YoutubeExplode.Videos; diff --git a/YoutubeExplode/Videos/VideoId.cs b/YoutubeExplode/Videos/VideoId.cs index 64a6c311..1ced91ba 100644 --- a/YoutubeExplode/Videos/VideoId.cs +++ b/YoutubeExplode/Videos/VideoId.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Net; using System.Text.RegularExpressions; -using YoutubeExplode.Utils.Extensions; +using PowerKit.Extensions; namespace YoutubeExplode.Videos; diff --git a/YoutubeExplode/YoutubeExplode.csproj b/YoutubeExplode/YoutubeExplode.csproj index 40b76dfa..976f3fd1 100644 --- a/YoutubeExplode/YoutubeExplode.csproj +++ b/YoutubeExplode/YoutubeExplode.csproj @@ -25,12 +25,14 @@ + + diff --git a/YoutubeExplode/YoutubeHttpHandler.cs b/YoutubeExplode/YoutubeHttpHandler.cs index 60b1ba04..43cdb19a 100644 --- a/YoutubeExplode/YoutubeHttpHandler.cs +++ b/YoutubeExplode/YoutubeHttpHandler.cs @@ -7,9 +7,10 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using PowerKit; +using PowerKit.Extensions; using YoutubeExplode.Exceptions; using YoutubeExplode.Utils; -using YoutubeExplode.Utils.Extensions; namespace YoutubeExplode; @@ -64,7 +65,7 @@ public YoutubeHttpHandler( var timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); var token = $"{timestamp} {sessionId} {uri.Domain}"; var tokenHash = Convert.ToHexString( - Hash.Compute(SHA1.Create(), Encoding.UTF8.GetBytes(token)) + HashAlgorithm.ComputeHash(SHA1.Create(), Encoding.UTF8.GetBytes(token)) ); return $"SAPISIDHASH {timestamp}_{tokenHash}";