Skip to content

Commit

Permalink
Extract visitor data from YouTube API (#870)
Browse files Browse the repository at this point in the history
  • Loading branch information
Ales315 authored Feb 3, 2025
1 parent 0cad7e6 commit 84e29bb
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 2 deletions.
55 changes: 54 additions & 1 deletion YoutubeExplode/Bridge/VisitorData.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using System;
using System.Linq;
using System.Text;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text.Json;
using System.Threading.Tasks;
using YoutubeExplode.Utils;
using YoutubeExplode.Utils.Extensions;

Expand All @@ -11,6 +14,7 @@ internal static class VisitorData
private static readonly Random Random = new();
private static readonly char[] SessionIdAllowedChars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".ToCharArray();
private static string _visitorData = string.Empty;

public static string Generate() =>
new ProtoBuilder()
Expand All @@ -36,4 +40,53 @@ public static string Generate() =>
.ToBytes()
)
.ToUrlEncodedBase64();

public static async Task<string> ExtractFromYoutube(HttpClient http)
{
//avoid fetching visitor data for each request (?) could slow down the app
if (!string.IsNullOrEmpty(_visitorData))
return _visitorData;

//use the same iOS user agent (?) idk if it's necessary
http.DefaultRequestHeaders.UserAgent.ParseAdd(
"com.google.ios.youtube/19.45.4 (iPhone16,2; U; CPU iOS 18_1_0 like Mac OS X; US)"
);

//request JSON format
http.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json")
);

var url = "https://www.youtube.com/sw.js_data";
HttpResponseMessage response = await http.GetAsync(url);
response.EnsureSuccessStatusCode();
string jsonString = await response.Content.ReadAsStringAsync();

//Remove the prefix ")]}'"
if (jsonString.StartsWith(")]}'"))
jsonString = jsonString.Substring(4);

using (JsonDocument doc = JsonDocument.Parse(jsonString))
{
JsonElement root = doc.RootElement;

//jsonArray[0][2][0][0][13]
var value = root[0]
.EnumerateArray()
.ElementAt(2)
.EnumerateArray()
.ElementAt(0)
.EnumerateArray()
.ElementAt(0)
.EnumerateArray()
.ElementAt(13)
.GetString();

if (value == null)
throw new Exception("Failed to fetch visitor data");

_visitorData = value;
}
return _visitorData;
}
}
2 changes: 1 addition & 1 deletion YoutubeExplode/Videos/VideoController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public async ValueTask<PlayerResponse> GetPlayerResponseAsync(
"platform": "MOBILE",
"osName": "IOS",
"osVersion": "18.1.0.22B83",
"visitorData": {{Json.Serialize(VisitorData.Generate())}},
"visitorData": {{Json.Serialize(await VisitorData.ExtractFromYoutube(Http))}},
"hl": "en",
"gl": "US",
"utcOffsetMinutes": 0
Expand Down

0 comments on commit 84e29bb

Please sign in to comment.