-
-
Notifications
You must be signed in to change notification settings - Fork 579
Add IsVideoUpscaled property to IVideoStreamInfo to detect AI-upscaled streams
#950
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ae347a7
9977318
16096f9
5f7bbee
0e150f2
fa0fe12
d83b7ad
33d426b
8852ed6
22d6ef4
71f0907
5ec2327
d346d11
d4bcbc1
f4ec180
95a43d5
9ed7b5c
aa6032f
3a76e4e
ff35cf0
94539a8
53207b7
bccda04
f55b58a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Text; | ||
|
|
||
| namespace YoutubeExplode.Utils; | ||
|
|
||
| internal static class Protobuf | ||
| { | ||
| private static bool IsLenField(ulong tag) => (tag & 0x7) == 2; | ||
|
|
||
| private static ulong? TryReadVarint(byte[] data, ref int i) | ||
| { | ||
| var value = 0UL; | ||
| var shift = 0; | ||
| while (i < data.Length) | ||
| { | ||
| var b = data[i++]; | ||
| value |= (ulong)(b & 0x7F) << shift; | ||
|
|
||
| if ((b & 0x80) == 0) | ||
| return value; | ||
|
|
||
| shift += 7; | ||
| if (shift >= 64) | ||
| break; | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
|
|
||
| private static string? TryReadString(byte[] data, ref int i) | ||
| { | ||
| var length = TryReadVarint(data, ref i); | ||
| if (length is null) | ||
| return null; | ||
|
|
||
| if (length.Value > int.MaxValue) | ||
| return null; | ||
|
|
||
| var result = Encoding.UTF8.GetString(data, i, (int)length); | ||
| i += (int)length; | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| // Deserializes a protobuf-encoded map<string, string> payload into a dictionary. | ||
| // Each top-level LEN field (wire type 2) is treated as a map entry submessage where | ||
| // field 1 is the string key and field 2 is the string value. | ||
| // Returns null if the data cannot be parsed. | ||
| public static IReadOnlyDictionary<string, string?>? TryDeserializeMap(byte[] data) | ||
| { | ||
| var result = new Dictionary<string, string?>(StringComparer.Ordinal); | ||
|
|
||
| var i = 0; | ||
| while (i < data.Length) | ||
| { | ||
| var outerTag = TryReadVarint(data, ref i); | ||
| if (outerTag is null) | ||
| return null; | ||
|
|
||
| // Only process LEN-encoded fields (wire type 2) as map entries | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to support other types of fields? @copilot
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No — the only field type present in |
||
| if (!IsLenField(outerTag.Value)) | ||
| return null; | ||
|
|
||
| var entryLen = TryReadVarint(data, ref i); | ||
| if (entryLen is null) | ||
| return null; | ||
|
|
||
| var entryEnd = i + (int)entryLen.Value; | ||
| if (entryEnd > data.Length) | ||
| return null; | ||
|
Tyrrrz marked this conversation as resolved.
|
||
|
|
||
| // Parse the map entry submessage: field 1 = key (string), field 2 = value (string) | ||
| var key = default(string); | ||
| var value = default(string); | ||
|
Tyrrrz marked this conversation as resolved.
|
||
| var j = i; | ||
|
Tyrrrz marked this conversation as resolved.
|
||
| while (j < entryEnd) | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe move this to a helper method @copilot
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in d346d11. Extracted |
||
| { | ||
| var fieldTag = TryReadVarint(data, ref j); | ||
| if (fieldTag is null) | ||
| break; | ||
|
|
||
| // Only handle LEN-encoded (string) fields | ||
| if (!IsLenField(fieldTag.Value)) | ||
| break; | ||
|
|
||
| var fieldNum = (int)(fieldTag.Value >> 3); | ||
|
|
||
| var str = TryReadString(data, ref j); | ||
| if (str is null) | ||
| break; | ||
|
|
||
| if (fieldNum == 1) | ||
| key = str; | ||
| else if (fieldNum == 2) | ||
| value = str; | ||
| } | ||
|
|
||
| if (key is not null) | ||
| result[key] = value; | ||
|
|
||
| i = entryEnd; | ||
| } | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| // Decodes a base64-encoded protobuf map<string, string> payload into a dictionary. | ||
| // Returns null if the string is not valid base64 or cannot be parsed. | ||
| public static IReadOnlyDictionary<string, string?>? TryDeserializeMap(string base64) | ||
| { | ||
| try | ||
| { | ||
| var bytes = Convert.FromBase64String(base64); | ||
| return TryDeserializeMap(bytes); | ||
| } | ||
| catch (FormatException) | ||
| { | ||
| return null; | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -17,9 +17,40 @@ public class MuxedStreamInfo( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bool? isAudioLanguageDefault, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| string videoCodec, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| VideoQuality videoQuality, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Resolution videoResolution | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Resolution videoResolution, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bool isVideoUpscaled | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) : IAudioStreamInfo, IVideoStreamInfo | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | |
| { | |
| public MuxedStreamInfo( | |
| string url, | |
| Container container, | |
| FileSize size, | |
| Bitrate bitrate, | |
| string audioCodec, | |
| Language? audioLanguage, | |
| bool? isAudioLanguageDefault, | |
| string videoCodec, | |
| VideoQuality videoQuality, | |
| Resolution videoResolution | |
| ) | |
| : this( | |
| url, | |
| container, | |
| size, | |
| bitrate, | |
| audioCodec, | |
| audioLanguage, | |
| isAudioLanguageDefault, | |
| videoCodec, | |
| videoQuality, | |
| videoResolution, | |
| false | |
| ) { } |
Uh oh!
There was an error while loading. Please reload this page.