From 908d6198679c1c8f1afcd18110ec506e72d2b886 Mon Sep 17 00:00:00 2001
From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>
Date: Mon, 2 Dec 2024 16:20:54 +0200
Subject: [PATCH 1/7] Identify audio stream languages
Closes #845
---
YoutubeExplode.Tests/StreamSpecs.cs | 56 +++++++++++++++++++
YoutubeExplode.Tests/TestData/VideoIds.cs | 1 +
YoutubeExplode/Bridge/DashManifest.cs | 6 ++
YoutubeExplode/Bridge/IStreamData.cs | 6 ++
YoutubeExplode/Bridge/PlayerResponse.cs | 28 +++++++++-
.../ClosedCaptions => Common}/Language.cs | 2 +
.../Utils/Extensions/JsonExtensions.cs | 8 +++
.../Videos/Streams/AudioOnlyStreamInfo.cs | 16 +++++-
.../Videos/Streams/IAudioStreamInfo.cs | 18 ++++++
.../Videos/Streams/MuxedStreamInfo.cs | 9 +++
YoutubeExplode/Videos/Streams/StreamClient.cs | 11 +++-
11 files changed, 155 insertions(+), 6 deletions(-)
rename YoutubeExplode/{Videos/ClosedCaptions => Common}/Language.cs (94%)
diff --git a/YoutubeExplode.Tests/StreamSpecs.cs b/YoutubeExplode.Tests/StreamSpecs.cs
index 25ffc4de..ac357ab9 100644
--- a/YoutubeExplode.Tests/StreamSpecs.cs
+++ b/YoutubeExplode.Tests/StreamSpecs.cs
@@ -1,3 +1,4 @@
+using System;
using System.Buffers;
using System.IO;
using System.Linq;
@@ -57,6 +58,61 @@ public async Task I_can_get_the_list_of_available_streams_of_a_video()
.Contain(s => s.VideoQuality.MaxHeight == 144 && !s.VideoQuality.IsHighDefinition);
}
+ [Fact]
+ public async Task I_can_get_the_list_of_available_streams_of_a_video_with_multiple_audio_languages()
+ {
+ // Arrange
+ var youtube = new YoutubeClient();
+
+ // Act
+ var manifest = await youtube.Videos.Streams.GetManifestAsync(
+ VideoIds.WithMultipleAUdioLanguages
+ );
+
+ // Assert
+ manifest.Streams.Should().NotBeEmpty();
+
+ manifest
+ .GetAudioStreams()
+ .Should()
+ .Contain(t =>
+ t.AudioLanguage != null
+ && t.AudioLanguage.Value.Code == "en-US"
+ && t.AudioLanguage.Value.Name == "English (United States) original"
+ && t.IsAudioLanguageDefault == true
+ );
+
+ manifest
+ .GetAudioStreams()
+ .Should()
+ .Contain(t =>
+ t.AudioLanguage != null
+ && t.AudioLanguage.Value.Code == "fr-FR"
+ && t.AudioLanguage.Value.Name == "French (France)"
+ && t.IsAudioLanguageDefault == false
+ );
+
+ manifest
+ .GetAudioStreams()
+ .Should()
+ .Contain(t =>
+ t.AudioLanguage != null
+ && t.AudioLanguage.Value.Code == "it"
+ && t.AudioLanguage.Value.Name == "Italian"
+ && t.IsAudioLanguageDefault == false
+ );
+
+ manifest
+ .GetAudioStreams()
+ .Should()
+ .Contain(t =>
+ t.AudioLanguage != null
+ && t.AudioLanguage.Value.Code == "pt-BR"
+ && t.AudioLanguage.Value.Name == "Portuguese (Brazil)"
+ && t.IsAudioLanguageDefault == false
+ );
+ }
+
[Theory]
[InlineData(VideoIds.Normal)]
[InlineData(VideoIds.Unlisted)]
diff --git a/YoutubeExplode.Tests/TestData/VideoIds.cs b/YoutubeExplode.Tests/TestData/VideoIds.cs
index 902256c9..b79682eb 100644
--- a/YoutubeExplode.Tests/TestData/VideoIds.cs
+++ b/YoutubeExplode.Tests/TestData/VideoIds.cs
@@ -21,4 +21,5 @@ internal static class VideoIds
public const string WithHighDynamicRangeStreams = "vX2vsvdq8nw";
public const string WithClosedCaptions = "YltHGKX80Y8";
public const string WithBrokenClosedCaptions = "1VKIIw05JnE";
+ public const string WithMultipleAUdioLanguages = "ngqcjXfggHQ";
}
diff --git a/YoutubeExplode/Bridge/DashManifest.cs b/YoutubeExplode/Bridge/DashManifest.cs
index 1892cd67..cb24589b 100644
--- a/YoutubeExplode/Bridge/DashManifest.cs
+++ b/YoutubeExplode/Bridge/DashManifest.cs
@@ -70,6 +70,12 @@ public class StreamData(XElement content) : IStreamData
[Lazy]
public string? AudioCodec => IsAudioOnly ? (string?)content.Attribute("codecs") : null;
+ public string? AudioLanguageCode => null;
+
+ public string? AudioLanguageName => null;
+
+ public bool? IsAudioLanguageDefault => null;
+
[Lazy]
public string? VideoCodec => IsAudioOnly ? null : (string?)content.Attribute("codecs");
diff --git a/YoutubeExplode/Bridge/IStreamData.cs b/YoutubeExplode/Bridge/IStreamData.cs
index f2385bab..067e91e1 100644
--- a/YoutubeExplode/Bridge/IStreamData.cs
+++ b/YoutubeExplode/Bridge/IStreamData.cs
@@ -18,6 +18,12 @@ internal interface IStreamData
string? AudioCodec { get; }
+ string? AudioLanguageCode { get; }
+
+ string? AudioLanguageName { get; }
+
+ bool? IsAudioLanguageDefault { get; }
+
string? VideoCodec { get; }
string? VideoQualityLabel { get; }
diff --git a/YoutubeExplode/Bridge/PlayerResponse.cs b/YoutubeExplode/Bridge/PlayerResponse.cs
index 8462bbb8..094b3964 100644
--- a/YoutubeExplode/Bridge/PlayerResponse.cs
+++ b/YoutubeExplode/Bridge/PlayerResponse.cs
@@ -236,16 +236,38 @@ public class StreamData(JsonElement content) : IStreamData
public string? Container => MimeType?.SubstringUntil(";").SubstringAfter("/");
[Lazy]
- private bool IsAudioOnly =>
- MimeType?.StartsWith("audio/", StringComparison.OrdinalIgnoreCase) ?? false;
+ public string? Codecs => MimeType?.SubstringAfter("codecs=\"").SubstringUntil("\"");
[Lazy]
- public string? Codecs => MimeType?.SubstringAfter("codecs=\"").SubstringUntil("\"");
+ private bool IsAudioOnly =>
+ MimeType?.StartsWith("audio/", StringComparison.OrdinalIgnoreCase) ?? false;
[Lazy]
public string? AudioCodec =>
IsAudioOnly ? Codecs : Codecs?.SubstringAfter(", ").NullIfWhiteSpace();
+ [Lazy]
+ public string? AudioLanguageCode =>
+ content
+ .GetPropertyOrNull("audioTrack")
+ ?.GetPropertyOrNull("id")
+ ?.GetStringOrNull()
+ ?.SubstringUntil(".");
+
+ [Lazy]
+ public string? AudioLanguageName =>
+ content
+ .GetPropertyOrNull("audioTrack")
+ ?.GetPropertyOrNull("displayName")
+ ?.GetStringOrNull();
+
+ [Lazy]
+ public bool? IsAudioLanguageDefault =>
+ content
+ .GetPropertyOrNull("audioTrack")
+ ?.GetPropertyOrNull("audioIsDefault")
+ ?.GetBooleanOrNull();
+
[Lazy]
public string? VideoCodec
{
diff --git a/YoutubeExplode/Videos/ClosedCaptions/Language.cs b/YoutubeExplode/Common/Language.cs
similarity index 94%
rename from YoutubeExplode/Videos/ClosedCaptions/Language.cs
rename to YoutubeExplode/Common/Language.cs
index 4d25af38..d7b552ec 100644
--- a/YoutubeExplode/Videos/ClosedCaptions/Language.cs
+++ b/YoutubeExplode/Common/Language.cs
@@ -1,6 +1,8 @@
using System;
using System.Diagnostics.CodeAnalysis;
+// TODO: breaking change: update the namespace
+// ReSharper disable once CheckNamespace
namespace YoutubeExplode.Videos.ClosedCaptions;
///
diff --git a/YoutubeExplode/Utils/Extensions/JsonExtensions.cs b/YoutubeExplode/Utils/Extensions/JsonExtensions.cs
index 70628a7b..501707f8 100644
--- a/YoutubeExplode/Utils/Extensions/JsonExtensions.cs
+++ b/YoutubeExplode/Utils/Extensions/JsonExtensions.cs
@@ -25,6 +25,14 @@ internal static class JsonExtensions
return null;
}
+ public static bool? GetBooleanOrNull(this JsonElement element) =>
+ element.ValueKind switch
+ {
+ JsonValueKind.True => true,
+ JsonValueKind.False => false,
+ _ => null,
+ };
+
public static string? GetStringOrNull(this JsonElement element) =>
element.ValueKind == JsonValueKind.String ? element.GetString() : null;
diff --git a/YoutubeExplode/Videos/Streams/AudioOnlyStreamInfo.cs b/YoutubeExplode/Videos/Streams/AudioOnlyStreamInfo.cs
index 59cb8e55..26ec59f5 100644
--- a/YoutubeExplode/Videos/Streams/AudioOnlyStreamInfo.cs
+++ b/YoutubeExplode/Videos/Streams/AudioOnlyStreamInfo.cs
@@ -1,4 +1,5 @@
using System.Diagnostics.CodeAnalysis;
+using YoutubeExplode.Videos.ClosedCaptions;
namespace YoutubeExplode.Videos.Streams;
@@ -10,7 +11,9 @@ public class AudioOnlyStreamInfo(
Container container,
FileSize size,
Bitrate bitrate,
- string audioCodec
+ string audioCodec,
+ Language? audioLanguage,
+ bool? isAudioLanguageDefault
) : IAudioStreamInfo
{
///
@@ -28,7 +31,16 @@ string audioCodec
///
public string AudioCodec { get; } = audioCodec;
+ ///
+ public Language? AudioLanguage { get; } = audioLanguage;
+
+ ///
+ public bool? IsAudioLanguageDefault { get; } = isAudioLanguageDefault;
+
///
[ExcludeFromCodeCoverage]
- public override string ToString() => $"Audio-only ({Container})";
+ public override string ToString() =>
+ AudioLanguage is not null
+ ? $"Audio-only ({Container} | {AudioLanguage})"
+ : $"Audio-only ({Container})";
}
diff --git a/YoutubeExplode/Videos/Streams/IAudioStreamInfo.cs b/YoutubeExplode/Videos/Streams/IAudioStreamInfo.cs
index 9de2853a..7bad3d0c 100644
--- a/YoutubeExplode/Videos/Streams/IAudioStreamInfo.cs
+++ b/YoutubeExplode/Videos/Streams/IAudioStreamInfo.cs
@@ -1,3 +1,5 @@
+using YoutubeExplode.Videos.ClosedCaptions;
+
namespace YoutubeExplode.Videos.Streams;
///
@@ -9,4 +11,20 @@ public interface IAudioStreamInfo : IStreamInfo
/// Audio codec.
///
string AudioCodec { get; }
+
+ ///
+ /// Audio language.
+ ///
+ ///
+ /// May be null if the audio stream does not have language information.
+ ///
+ Language? AudioLanguage { get; }
+
+ ///
+ /// Whether the audio track language corresponds to the default language of the video.
+ ///
+ ///
+ /// May be null if the audio stream does not have language information.
+ ///
+ bool? IsAudioLanguageDefault { get; }
}
diff --git a/YoutubeExplode/Videos/Streams/MuxedStreamInfo.cs b/YoutubeExplode/Videos/Streams/MuxedStreamInfo.cs
index 0983038b..88258441 100644
--- a/YoutubeExplode/Videos/Streams/MuxedStreamInfo.cs
+++ b/YoutubeExplode/Videos/Streams/MuxedStreamInfo.cs
@@ -1,5 +1,6 @@
using System.Diagnostics.CodeAnalysis;
using YoutubeExplode.Common;
+using YoutubeExplode.Videos.ClosedCaptions;
namespace YoutubeExplode.Videos.Streams;
@@ -12,6 +13,8 @@ public class MuxedStreamInfo(
FileSize size,
Bitrate bitrate,
string audioCodec,
+ Language? audioLanguage,
+ bool? isAudioLanguageDefault,
string videoCodec,
VideoQuality videoQuality,
Resolution videoResolution
@@ -32,6 +35,12 @@ Resolution videoResolution
///
public string AudioCodec { get; } = audioCodec;
+ ///
+ public Language? AudioLanguage { get; } = audioLanguage;
+
+ ///
+ public bool? IsAudioLanguageDefault { get; } = isAudioLanguageDefault;
+
///
public string VideoCodec { get; } = videoCodec;
diff --git a/YoutubeExplode/Videos/Streams/StreamClient.cs b/YoutubeExplode/Videos/Streams/StreamClient.cs
index d6db8c58..ced02674 100644
--- a/YoutubeExplode/Videos/Streams/StreamClient.cs
+++ b/YoutubeExplode/Videos/Streams/StreamClient.cs
@@ -13,6 +13,7 @@
using YoutubeExplode.Exceptions;
using YoutubeExplode.Utils;
using YoutubeExplode.Utils.Extensions;
+using YoutubeExplode.Videos.ClosedCaptions;
namespace YoutubeExplode.Videos.Streams;
@@ -123,6 +124,10 @@ private async IAsyncEnumerable GetStreamInfosAsync(
streamData.Bitrate?.Pipe(s => new Bitrate(s))
?? throw new YoutubeExplodeException("Failed to extract the stream bitrate.");
+ var audioLanguage = !string.IsNullOrWhiteSpace(streamData.AudioLanguageCode)
+ ? new Language(streamData.AudioLanguageCode, streamData.AudioLanguageName!)
+ : (Language?)null;
+
// Muxed or video-only stream
if (!string.IsNullOrWhiteSpace(streamData.VideoCodec))
{
@@ -146,6 +151,8 @@ streamData.VideoWidth is not null && streamData.VideoHeight is not null
new FileSize(contentLength.Value),
bitrate,
streamData.AudioCodec,
+ audioLanguage,
+ streamData.IsAudioLanguageDefault,
streamData.VideoCodec,
videoQuality,
videoResolution
@@ -177,7 +184,9 @@ streamData.VideoWidth is not null && streamData.VideoHeight is not null
container,
new FileSize(contentLength.Value),
bitrate,
- streamData.AudioCodec
+ streamData.AudioCodec,
+ audioLanguage,
+ streamData.IsAudioLanguageDefault
);
yield return streamInfo;
From 77d03ba4a81b84bc154581784709885699efb4d9 Mon Sep 17 00:00:00 2001
From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>
Date: Mon, 2 Dec 2024 16:24:25 +0200
Subject: [PATCH 2/7] asd
---
YoutubeExplode/Bridge/PlayerResponse.cs | 6 +++---
YoutubeExplode/Videos/Streams/IAudioStreamInfo.cs | 2 +-
YoutubeExplode/Videos/Streams/StreamClient.cs | 5 ++++-
3 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/YoutubeExplode/Bridge/PlayerResponse.cs b/YoutubeExplode/Bridge/PlayerResponse.cs
index 094b3964..caaa1948 100644
--- a/YoutubeExplode/Bridge/PlayerResponse.cs
+++ b/YoutubeExplode/Bridge/PlayerResponse.cs
@@ -235,13 +235,13 @@ public class StreamData(JsonElement content) : IStreamData
[Lazy]
public string? Container => MimeType?.SubstringUntil(";").SubstringAfter("/");
- [Lazy]
- public string? Codecs => MimeType?.SubstringAfter("codecs=\"").SubstringUntil("\"");
-
[Lazy]
private bool IsAudioOnly =>
MimeType?.StartsWith("audio/", StringComparison.OrdinalIgnoreCase) ?? false;
+ [Lazy]
+ public string? Codecs => MimeType?.SubstringAfter("codecs=\"").SubstringUntil("\"");
+
[Lazy]
public string? AudioCodec =>
IsAudioOnly ? Codecs : Codecs?.SubstringAfter(", ").NullIfWhiteSpace();
diff --git a/YoutubeExplode/Videos/Streams/IAudioStreamInfo.cs b/YoutubeExplode/Videos/Streams/IAudioStreamInfo.cs
index 7bad3d0c..4b77dad7 100644
--- a/YoutubeExplode/Videos/Streams/IAudioStreamInfo.cs
+++ b/YoutubeExplode/Videos/Streams/IAudioStreamInfo.cs
@@ -21,7 +21,7 @@ public interface IAudioStreamInfo : IStreamInfo
Language? AudioLanguage { get; }
///
- /// Whether the audio track language corresponds to the default language of the video.
+ /// Whether the audio stream's language corresponds to the default language of the video.
///
///
/// May be null if the audio stream does not have language information.
diff --git a/YoutubeExplode/Videos/Streams/StreamClient.cs b/YoutubeExplode/Videos/Streams/StreamClient.cs
index ced02674..aeb3d66c 100644
--- a/YoutubeExplode/Videos/Streams/StreamClient.cs
+++ b/YoutubeExplode/Videos/Streams/StreamClient.cs
@@ -125,7 +125,10 @@ private async IAsyncEnumerable GetStreamInfosAsync(
?? throw new YoutubeExplodeException("Failed to extract the stream bitrate.");
var audioLanguage = !string.IsNullOrWhiteSpace(streamData.AudioLanguageCode)
- ? new Language(streamData.AudioLanguageCode, streamData.AudioLanguageName!)
+ ? new Language(
+ streamData.AudioLanguageCode,
+ streamData.AudioLanguageName ?? streamData.AudioLanguageCode
+ )
: (Language?)null;
// Muxed or video-only stream
From 8869b92dc5d4306bf759f998b11096ce32b9b259 Mon Sep 17 00:00:00 2001
From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>
Date: Mon, 2 Dec 2024 16:28:48 +0200
Subject: [PATCH 3/7] Better FFmpeg integration
---
YoutubeExplode.Converter/Converter.cs | 29 +++++++++++++++++++++++----
1 file changed, 25 insertions(+), 4 deletions(-)
diff --git a/YoutubeExplode.Converter/Converter.cs b/YoutubeExplode.Converter/Converter.cs
index fc13d457..e8dd5249 100644
--- a/YoutubeExplode.Converter/Converter.cs
+++ b/YoutubeExplode.Converter/Converter.cs
@@ -116,18 +116,39 @@ private async ValueTask ProcessAsync(
if (streamInput.Info is IAudioStreamInfo audioStreamInfo)
{
- arguments
- .Add($"-metadata:s:a:{lastAudioStreamIndex++}")
- .Add($"title={audioStreamInfo.Bitrate}");
+ // Contains language information
+ if (audioStreamInfo.AudioLanguage is { } language)
+ {
+ // 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 = language.TryGetThreeLetterCode() ?? language.Code;
+
+ arguments
+ .Add($"-metadata:s:a:{lastAudioStreamIndex}")
+ .Add($"language={languageCode}")
+ .Add($"-metadata:s:a:{lastAudioStreamIndex}")
+ .Add($"title={language.Name} | {audioStreamInfo.Bitrate}");
+ }
+ // Does not contain language information
+ else
+ {
+ arguments
+ .Add($"-metadata:s:a:{lastAudioStreamIndex}")
+ .Add($"title={audioStreamInfo.Bitrate}");
+ }
+
+ lastAudioStreamIndex++;
}
if (streamInput.Info is IVideoStreamInfo videoStreamInfo)
{
arguments
- .Add($"-metadata:s:v:{lastVideoStreamIndex++}")
+ .Add($"-metadata:s:v:{lastVideoStreamIndex}")
.Add(
$"title={videoStreamInfo.VideoQuality.Label} | {videoStreamInfo.Bitrate}"
);
+
+ lastVideoStreamIndex++;
}
}
}
From b9f6a859418dc789b1de5dcc985a08518a47954c Mon Sep 17 00:00:00 2001
From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>
Date: Mon, 2 Dec 2024 16:30:35 +0200
Subject: [PATCH 4/7] asd
---
YoutubeExplode.Tests/StreamSpecs.cs | 2 +-
YoutubeExplode.Tests/TestData/VideoIds.cs | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/YoutubeExplode.Tests/StreamSpecs.cs b/YoutubeExplode.Tests/StreamSpecs.cs
index ac357ab9..02d9ddfe 100644
--- a/YoutubeExplode.Tests/StreamSpecs.cs
+++ b/YoutubeExplode.Tests/StreamSpecs.cs
@@ -66,7 +66,7 @@ public async Task I_can_get_the_list_of_available_streams_of_a_video_with_multip
// Act
var manifest = await youtube.Videos.Streams.GetManifestAsync(
- VideoIds.WithMultipleAUdioLanguages
+ VideoIds.WithMultipleAudioLanguages
);
// Assert
diff --git a/YoutubeExplode.Tests/TestData/VideoIds.cs b/YoutubeExplode.Tests/TestData/VideoIds.cs
index b79682eb..b822e5a5 100644
--- a/YoutubeExplode.Tests/TestData/VideoIds.cs
+++ b/YoutubeExplode.Tests/TestData/VideoIds.cs
@@ -21,5 +21,5 @@ internal static class VideoIds
public const string WithHighDynamicRangeStreams = "vX2vsvdq8nw";
public const string WithClosedCaptions = "YltHGKX80Y8";
public const string WithBrokenClosedCaptions = "1VKIIw05JnE";
- public const string WithMultipleAUdioLanguages = "ngqcjXfggHQ";
+ public const string WithMultipleAudioLanguages = "ngqcjXfggHQ";
}
From c6c10f272f95f62ab144000879de85a18c2a6992 Mon Sep 17 00:00:00 2001
From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>
Date: Mon, 2 Dec 2024 16:31:16 +0200
Subject: [PATCH 5/7] asd
---
YoutubeExplode/Videos/Streams/IAudioStreamInfo.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/YoutubeExplode/Videos/Streams/IAudioStreamInfo.cs b/YoutubeExplode/Videos/Streams/IAudioStreamInfo.cs
index 4b77dad7..e8103f84 100644
--- a/YoutubeExplode/Videos/Streams/IAudioStreamInfo.cs
+++ b/YoutubeExplode/Videos/Streams/IAudioStreamInfo.cs
@@ -16,7 +16,7 @@ public interface IAudioStreamInfo : IStreamInfo
/// Audio language.
///
///
- /// May be null if the audio stream does not have language information.
+ /// May be null if the audio stream does not contain language information.
///
Language? AudioLanguage { get; }
@@ -24,7 +24,7 @@ public interface IAudioStreamInfo : IStreamInfo
/// Whether the audio stream's language corresponds to the default language of the video.
///
///
- /// May be null if the audio stream does not have language information.
+ /// May be null if the audio stream does not contain language information.
///
bool? IsAudioLanguageDefault { get; }
}
From bd415594269f3b185358b37688e0e05fe1434e5e Mon Sep 17 00:00:00 2001
From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>
Date: Mon, 2 Dec 2024 16:53:00 +0200
Subject: [PATCH 6/7] asd
---
.../GeneralSpecs.cs | 30 ++++++++++++++++++-
YoutubeExplode.Converter/Converter.cs | 10 +++++--
2 files changed, 36 insertions(+), 4 deletions(-)
diff --git a/YoutubeExplode.Converter.Tests/GeneralSpecs.cs b/YoutubeExplode.Converter.Tests/GeneralSpecs.cs
index 7657193f..63e9bf45 100644
--- a/YoutubeExplode.Converter.Tests/GeneralSpecs.cs
+++ b/YoutubeExplode.Converter.Tests/GeneralSpecs.cs
@@ -92,7 +92,7 @@ public async Task I_can_download_a_video_as_a_single_mp4_file_with_multiple_stre
var filePath = Path.Combine(dir.Path, "video.mp4");
// Act
- var manifest = await youtube.Videos.Streams.GetManifestAsync("9bZkp7q19f0");
+ var manifest = await youtube.Videos.Streams.GetManifestAsync("ngqcjXfggHQ");
var audioStreamInfos = manifest
.GetAudioOnlyStreams()
@@ -117,6 +117,20 @@ await youtube.Videos.DownloadAsync(
// Assert
MediaFormat.IsMp4File(filePath).Should().BeTrue();
+ foreach (var streamInfo in audioStreamInfos)
+ {
+ if (streamInfo.AudioLanguage is not null)
+ {
+ FileEx
+ .ContainsBytes(
+ filePath,
+ Encoding.ASCII.GetBytes(streamInfo.AudioLanguage.Value.Name)
+ )
+ .Should()
+ .BeTrue();
+ }
+ }
+
foreach (var streamInfo in videoStreamInfos)
{
FileEx
@@ -161,6 +175,20 @@ await youtube.Videos.DownloadAsync(
// Assert
MediaFormat.IsWebMFile(filePath).Should().BeTrue();
+ foreach (var streamInfo in audioStreamInfos)
+ {
+ if (streamInfo.AudioLanguage is not null)
+ {
+ FileEx
+ .ContainsBytes(
+ filePath,
+ Encoding.ASCII.GetBytes(streamInfo.AudioLanguage.Value.Name)
+ )
+ .Should()
+ .BeTrue();
+ }
+ }
+
foreach (var streamInfo in videoStreamInfos)
{
FileEx
diff --git a/YoutubeExplode.Converter/Converter.cs b/YoutubeExplode.Converter/Converter.cs
index e8dd5249..d63d3c2b 100644
--- a/YoutubeExplode.Converter/Converter.cs
+++ b/YoutubeExplode.Converter/Converter.cs
@@ -117,17 +117,21 @@ private async ValueTask ProcessAsync(
if (streamInput.Info is IAudioStreamInfo audioStreamInfo)
{
// Contains language information
- if (audioStreamInfo.AudioLanguage is { } language)
+ if (audioStreamInfo.AudioLanguage is not null)
{
// 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 = language.TryGetThreeLetterCode() ?? language.Code;
+ var languageCode =
+ audioStreamInfo.AudioLanguage.Value.TryGetThreeLetterCode()
+ ?? audioStreamInfo.AudioLanguage.Value.Code;
arguments
.Add($"-metadata:s:a:{lastAudioStreamIndex}")
.Add($"language={languageCode}")
.Add($"-metadata:s:a:{lastAudioStreamIndex}")
- .Add($"title={language.Name} | {audioStreamInfo.Bitrate}");
+ .Add(
+ $"title={audioStreamInfo.AudioLanguage.Value.Name} | {audioStreamInfo.Bitrate}"
+ );
}
// Does not contain language information
else
From 3d08bc244b24c4b1077edbeb56f01fa55f634c23 Mon Sep 17 00:00:00 2001
From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>
Date: Mon, 2 Dec 2024 16:56:39 +0200
Subject: [PATCH 7/7] zxc
---
YoutubeExplode.Converter.Tests/GeneralSpecs.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/YoutubeExplode.Converter.Tests/GeneralSpecs.cs b/YoutubeExplode.Converter.Tests/GeneralSpecs.cs
index 63e9bf45..6c161937 100644
--- a/YoutubeExplode.Converter.Tests/GeneralSpecs.cs
+++ b/YoutubeExplode.Converter.Tests/GeneralSpecs.cs
@@ -150,7 +150,7 @@ public async Task I_can_download_a_video_as_a_single_webm_file_with_multiple_str
var filePath = Path.Combine(dir.Path, "video.webm");
// Act
- var manifest = await youtube.Videos.Streams.GetManifestAsync("9bZkp7q19f0");
+ var manifest = await youtube.Videos.Streams.GetManifestAsync("ngqcjXfggHQ");
var audioStreamInfos = manifest
.GetAudioOnlyStreams()