Decompiled source of BoomboxYouTube v1.3.0
plugins/BoomboxYouTube.dll
Decompiled 8 minutes agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using Unity.Netcode; using UnityEngine; using UnityEngine.Networking; using YoutubeExplode; using YoutubeExplode.Videos; using YoutubeExplode.Videos.Streams; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("BoomboxYouTube")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("BoomboxYouTube")] [assembly: AssemblyCopyright("Copyright © 2025")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("1fb03771-856b-48ae-8838-d36f13ee393c")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.0.0.0")] [RequireComponent(typeof(NetworkObject))] [RequireComponent(typeof(AudioSource))] public class BoomboxNetwork : NetworkBehaviour { [CompilerGenerated] private sealed class <>c__DisplayClass7_0 { public Task<StreamManifest> manifestTask; public CancellationToken token; internal bool <Stream>b__0() { return manifestTask.IsCompleted || token.IsCancellationRequested; } } [CompilerGenerated] private sealed class <Stream>d__7 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public string url; public CancellationToken token; public BoomboxNetwork <>4__this; private <>c__DisplayClass7_0 <>8__1; private StreamManifest <manifest>5__2; private IAudioStreamInfo <streamInfo>5__3; private Exception <ex>5__4; private IEnumerable<AudioOnlyStreamInfo> <audioStreams>5__5; private string <codec>5__6; private string <container>5__7; private double <bitrateKbps>5__8; private Exception <ex>5__9; private UnityWebRequest <www>5__10; private UnityWebRequestAsyncOperation <req>5__11; private AudioClip <clip>5__12; private Exception <ex>5__13; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <Stream>d__7(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 2) { try { } finally { <>m__Finally1(); } } <>8__1 = null; <manifest>5__2 = null; <streamInfo>5__3 = null; <ex>5__4 = null; <audioStreams>5__5 = null; <codec>5__6 = null; <container>5__7 = null; <ex>5__9 = null; <www>5__10 = null; <req>5__11 = null; <clip>5__12 = null; <ex>5__13 = null; <>1__state = -2; } private bool MoveNext() { //IL_047b: Unknown result type (might be due to invalid IL or missing references) //IL_0481: Invalid comparison between Unknown and I4 //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Expected O, but got Unknown //IL_0307: Unknown result type (might be due to invalid IL or missing references) //IL_030c: Unknown result type (might be due to invalid IL or missing references) //IL_033d: Unknown result type (might be due to invalid IL or missing references) //IL_0342: Unknown result type (might be due to invalid IL or missing references) bool result; try { switch (<>1__state) { default: result = false; goto end_IL_0000; case 0: <>1__state = -1; <>8__1 = new <>c__DisplayClass7_0(); <>8__1.token = token; if (string.IsNullOrWhiteSpace(url)) { Debug.LogWarning((object)"[BoomboxNetwork] Provided URL is empty or null."); result = false; } else if ((Object)(object)BoomboxYouTube.Instance == (Object)null || BoomboxYouTube.Instance.Youtube == null) { Debug.LogError((object)"[BoomboxNetwork] Plugin/YouTube client not initialized yet."); result = false; } else { if (!((Object)(object)<>4__this._source == (Object)null)) { goto IL_0102; } <>4__this._source = ((Component)<>4__this).GetComponent<AudioSource>(); if (!((Object)(object)<>4__this._source == (Object)null)) { goto IL_0102; } Debug.LogError((object)"[BoomboxNetwork] AudioSource is missing—cannot play audio."); result = false; } goto end_IL_0000; case 1: <>1__state = -1; if (<>8__1.token.IsCancellationRequested) { result = false; } else if (<>8__1.manifestTask.IsFaulted) { Debug.LogError((object)$"[BoomboxNetwork] GetManifestAsync faulted: {<>8__1.manifestTask.Exception}"); result = false; } else { <manifest>5__2 = <>8__1.manifestTask.Result; if (<manifest>5__2 == null) { Debug.LogError((object)"[BoomboxNetwork] Manifest result is null."); result = false; } else { try { <audioStreams>5__5 = <manifest>5__2.GetAudioOnlyStreams(); <streamInfo>5__3 = (IAudioStreamInfo)(object)((from s in <audioStreams>5__5.Where(delegate(AudioOnlyStreamInfo s) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) Container container2 = s.Container; return ((object)(Container)(ref container2)).ToString().IndexOf("aac", StringComparison.OrdinalIgnoreCase) >= 0; }) orderby s.Bitrate descending select s).FirstOrDefault() ?? <audioStreams>5__5.OrderByDescending((AudioOnlyStreamInfo s) => s.Bitrate).FirstOrDefault()); IAudioStreamInfo obj = <streamInfo>5__3; <codec>5__6 = ((obj != null) ? obj.AudioCodec.ToString() : null) ?? "<unknown>"; IAudioStreamInfo obj2 = <streamInfo>5__3; object obj3; if (obj2 == null) { obj3 = null; } else { Container container = ((IStreamInfo)obj2).Container; obj3 = ((Container)(ref container)).Name; } if (obj3 == null) { obj3 = "<unknown>"; } <container>5__7 = (string)obj3; double num; if (<streamInfo>5__3 == null) { num = 0.0; } else { Bitrate bitrate = ((IStreamInfo)<streamInfo>5__3).Bitrate; num = (double)((Bitrate)(ref bitrate)).BitsPerSecond / 1000.0; } <bitrateKbps>5__8 = num; Debug.Log((object)$"[BoomboxNetwork] Selected stream: codec={<codec>5__6}, container={<container>5__7}, bitrate={<bitrateKbps>5__8:F1} kbps"); <audioStreams>5__5 = null; <codec>5__6 = null; <container>5__7 = null; } catch (Exception ex) { <ex>5__9 = ex; Debug.LogError((object)$"[BoomboxNetwork] Failed to choose audio stream: {<ex>5__9}"); result = false; goto end_IL_0000; } if (<streamInfo>5__3 != null) { <www>5__10 = UnityWebRequestMultimedia.GetAudioClip(((IStreamInfo)<streamInfo>5__3).Url, (AudioType)13); <>1__state = -3; <req>5__11 = <www>5__10.SendWebRequest(); break; } Debug.LogError((object)"[BoomboxNetwork] No audio-only streams available for the provided URL."); result = false; } } goto end_IL_0000; case 2: { <>1__state = -3; break; } IL_0102: try { <>8__1.manifestTask = BoomboxYouTube.Instance.Youtube.Videos.Streams.GetManifestAsync(VideoId.op_Implicit(url), default(CancellationToken)).AsTask(); } catch (Exception ex) { <ex>5__4 = ex; Debug.LogError((object)$"[BoomboxNetwork] Failed to start GetManifestAsync: {<ex>5__4}"); result = false; goto end_IL_0000; } <>2__current = (object)new WaitUntil((Func<bool>)(() => <>8__1.manifestTask.IsCompleted || <>8__1.token.IsCancellationRequested)); <>1__state = 1; result = true; goto end_IL_0000; } if (!((AsyncOperation)<req>5__11).isDone) { if (<>8__1.token.IsCancellationRequested) { <www>5__10.Abort(); result = false; goto IL_058c; } <>2__current = null; <>1__state = 2; result = true; } else { if ((int)<www>5__10.result != 1) { Debug.LogError((object)("[BoomboxNetwork] Audio download failed: " + <www>5__10.error + ". Hint: If the stream is m4a/webm (AAC/Opus), Unity may not parse it without a decoder.")); result = false; goto IL_058c; } try { <clip>5__12 = DownloadHandlerAudioClip.GetContent(<www>5__10); } catch (Exception ex) { <ex>5__13 = ex; Debug.LogError((object)($"[BoomboxNetwork] Failed to parse audio clip: {<ex>5__13}. " + "This often happens when the container/codec isn't supported by Unity.")); result = false; goto IL_058c; } if ((Object)(object)<clip>5__12 == (Object)null) { Debug.LogError((object)"[BoomboxNetwork] Downloaded audio clip is null."); result = false; goto IL_058c; } <>4__this._source.Stop(); <>4__this._source.loop = true; <>4__this._source.clip = <clip>5__12; <>4__this._source.Play(); Debug.Log((object)"[BoomboxNetwork] Audio playback started."); <req>5__11 = null; <clip>5__12 = null; <>m__Finally1(); <www>5__10 = null; result = false; } goto end_IL_0000; IL_058c: <>m__Finally1(); end_IL_0000:; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } return result; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (<www>5__10 != null) { ((IDisposable)<www>5__10).Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private AudioSource _source; private Coroutine _currentStreamRoutine; private CancellationTokenSource _cts; static BoomboxNetwork() { try { Environment.SetEnvironmentVariable("SLAVA_UKRAINI", "1", EnvironmentVariableTarget.Process); } catch { } } private void Awake() { try { Environment.SetEnvironmentVariable("SLAVA_UKRAINI", "1", EnvironmentVariableTarget.Process); string environmentVariable = Environment.GetEnvironmentVariable("SLAVA_UKRAINI", EnvironmentVariableTarget.Process); Debug.Log((object)("[BoomboxNetwork] Set 'SLAVA_UKRAINI'='" + (environmentVariable ?? "<null>") + "' (Process).")); } catch (Exception arg) { Debug.LogError((object)$"[BoomboxNetwork] Failed to set environment variable: {arg}"); } _source = ((Component)this).GetComponent<AudioSource>(); if ((Object)(object)_source == (Object)null) { Debug.LogWarning((object)"[BoomboxNetwork] No AudioSource found on GameObject. Audio playback will fail."); return; } _source.playOnAwake = false; _source.loop = false; } [ServerRpc(RequireOwnership = false)] public void PlayYouTubeServerRpc(string url) { NetworkObject component = ((Component)this).GetComponent<NetworkObject>(); if ((Object)(object)component == (Object)null || !component.IsSpawned) { Debug.LogError((object)"[BoomboxNetwork] NetworkObject not spawned—cannot route RPC."); } else { PlayYouTubeClientRpc(url); } } [ClientRpc] private void PlayYouTubeClientRpc(string url) { if (_currentStreamRoutine != null) { ((MonoBehaviour)this).StopCoroutine(_currentStreamRoutine); _currentStreamRoutine = null; } _cts?.Cancel(); _cts = new CancellationTokenSource(); _currentStreamRoutine = ((MonoBehaviour)this).StartCoroutine(Stream(url, _cts.Token)); } [IteratorStateMachine(typeof(<Stream>d__7))] private IEnumerator Stream(string url, CancellationToken token) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <Stream>d__7(0) { <>4__this = this, url = url, token = token }; } public void StopPlayback() { if (_currentStreamRoutine != null) { ((MonoBehaviour)this).StopCoroutine(_currentStreamRoutine); _currentStreamRoutine = null; } _cts?.Cancel(); if ((Object)(object)_source != (Object)null && _source.isPlaying) { _source.Stop(); _source.clip = null; } Debug.Log((object)"[BoomboxNetwork] Playback stopped."); } } [HarmonyPatch(typeof(BoomboxItem))] internal class BoomboxPatch { [HarmonyPatch("StartMusic")] [HarmonyPrefix] private static bool DisableVanillaMusic() { return false; } [HarmonyPatch("Awake")] [HarmonyPostfix] private static void InjectNetworkEarly(BoomboxItem __instance) { try { GameObject val = ((__instance != null) ? ((Component)__instance).gameObject : null); BoomboxNetwork boomboxNetwork = default(BoomboxNetwork); if (!((Object)(object)val == (Object)null) && (!val.TryGetComponent<BoomboxNetwork>(ref boomboxNetwork) || (Object)(object)boomboxNetwork == (Object)null)) { boomboxNetwork = val.AddComponent<BoomboxNetwork>(); Debug.Log((object)"[BoomboxPatch] Injected BoomboxNetwork in Awake (pre-spawn)."); } } catch (Exception arg) { Debug.LogError((object)$"[BoomboxPatch] Failed to inject BoomboxNetwork in Awake: {arg}"); } } [HarmonyPatch("EquipItem")] [HarmonyPostfix] private static void InjectNetworkOnEquip(BoomboxItem __instance) { try { if ((Object)(object)__instance == (Object)null) { Debug.LogWarning((object)"[BoomboxPatch] EquipItem postfix: __instance is null."); return; } GameObject gameObject = ((Component)__instance).gameObject; BoomboxNetwork boomboxNetwork = default(BoomboxNetwork); if ((Object)(object)gameObject == (Object)null) { Debug.LogWarning((object)"[BoomboxPatch] EquipItem postfix: BoomboxItem GameObject is null."); } else if (!gameObject.TryGetComponent<BoomboxNetwork>(ref boomboxNetwork) || (Object)(object)boomboxNetwork == (Object)null) { boomboxNetwork = gameObject.AddComponent<BoomboxNetwork>(); Debug.Log((object)"[BoomboxPatch] Injected BoomboxNetwork on EquipItem (fallback)."); } } catch (Exception arg) { Debug.LogError((object)$"[BoomboxPatch] Failed to inject BoomboxNetwork on Equip: {arg}"); } } } public class BoomboxUI : MonoBehaviour { private Rect _window = new Rect(20f, 20f, 420f, 180f); private bool _showWindow = true; private string _ytUrl = string.Empty; private BoomboxNetwork _activeBox; private static readonly Regex YoutubeUrlRegex = new Regex("^(https?:\\/\\/)?(www\\.)?(youtube\\.com\\/watch\\?v=|youtu\\.be\\/)[\\w\\-]{6,}$", RegexOptions.IgnoreCase | RegexOptions.Compiled); private void Awake() { BoomboxUI[] array = Object.FindObjectsOfType<BoomboxUI>(true); if (array.Length > 1) { Object.Destroy((Object)(object)((Component)this).gameObject); } else { Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); } } public void ToggleWindow() { _showWindow = !_showWindow; Debug.Log((object)("[BoomboxUI] Window toggled: " + (_showWindow ? "shown" : "hidden"))); } public void ShowWindow() { _showWindow = true; } public void HideWindow() { _showWindow = false; } private void OnGUI() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) if (_showWindow) { _window = GUI.Window(9999, _window, new WindowFunction(DrawWindow), "Boombox YouTube"); } } private void DrawWindow(int id) { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Expected O, but got Unknown GUILayout.BeginVertical(Array.Empty<GUILayoutOption>()); GUILayout.Label("YouTube URL:", Array.Empty<GUILayoutOption>()); _ytUrl = GUILayout.TextField(_ytUrl, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MinWidth(280f) }); GUILayout.Space(6f); if ((Object)(object)_activeBox == (Object)null) { GUI.color = Color.yellow; GUILayout.Label("No Boombox selected. Hold a Boombox to link.", Array.Empty<GUILayoutOption>()); GUI.color = Color.white; } else { GUILayout.Label("Boombox connected.", Array.Empty<GUILayoutOption>()); } GUILayout.Space(4f); HorizontalScope val = new HorizontalScope(Array.Empty<GUILayoutOption>()); try { if (GUILayout.Button("Play", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(26f) })) { TryPlayUrl(); } if (GUILayout.Button(_showWindow ? "Hide" : "Show", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(80f), GUILayout.Height(26f) })) { ToggleWindow(); } } finally { ((IDisposable)val)?.Dispose(); } GUILayout.EndVertical(); GUI.DragWindow(); } private void Update() { ResolveActiveBoombox(); } private void ResolveActiveBoombox() { try { PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; if ((Object)(object)val == (Object)null) { _activeBox = null; return; } GrabbableObject val2 = val.currentlyHeldObjectServer ?? val.currentlyHeldObject; BoomboxItem val3 = (BoomboxItem)(object)((val2 is BoomboxItem) ? val2 : null); if (val3 != null) { BoomboxNetwork boomboxNetwork = default(BoomboxNetwork); if (!((Component)val3).gameObject.TryGetComponent<BoomboxNetwork>(ref boomboxNetwork) || (Object)(object)boomboxNetwork == (Object)null) { boomboxNetwork = ((Component)val3).gameObject.AddComponent<BoomboxNetwork>(); Debug.Log((object)"[BoomboxUI] Added BoomboxNetwork to BoomboxItem."); } _activeBox = boomboxNetwork; } else { _activeBox = null; } } catch (Exception ex) { Debug.LogWarning((object)("[BoomboxUI] Failed to resolve active boombox: " + ex.Message)); _activeBox = null; } } private void TryPlayUrl() { if ((Object)(object)_activeBox == (Object)null) { Debug.LogWarning((object)"[BoomboxUI] No active boombox to play on."); return; } string text = _ytUrl?.Trim(); if (string.IsNullOrEmpty(text)) { Debug.LogWarning((object)"[BoomboxUI] URL is empty."); return; } if (!YoutubeUrlRegex.IsMatch(text)) { Debug.LogWarning((object)"[BoomboxUI] URL does not look like a valid YouTube link."); } try { _activeBox.PlayYouTubeServerRpc(text); Debug.Log((object)("[BoomboxUI] Requested playback for URL: " + text)); } catch (Exception arg) { Debug.LogError((object)$"[BoomboxUI] Failed to send PlayYouTubeServerRpc: {arg}"); } } } [BepInPlugin("blaide.boomboxyoutube", "Boombox YouTube", "1.2.0")] public class BoomboxYouTube : BaseUnityPlugin { internal static ManualLogSource Log; private ConfigEntry<KeyCode> _toggleKey; private BoomboxUI _ui; public static BoomboxYouTube Instance { get; private set; } public YoutubeClient Youtube { get; private set; } static BoomboxYouTube() { try { Environment.SetEnvironmentVariable("SLAVA_UKRAINI", "1", EnvironmentVariableTarget.Process); } catch { } } private void Awake() { //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Expected O, but got Unknown //IL_011f: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; try { Environment.SetEnvironmentVariable("SLAVA_UKRAINI", "1", EnvironmentVariableTarget.Process); string environmentVariable = Environment.GetEnvironmentVariable("SLAVA_UKRAINI", EnvironmentVariableTarget.Process); Log.LogInfo((object)("[BoomboxYouTube] Set 'SLAVA_UKRAINI'='" + (environmentVariable ?? "<null>") + "' at process scope.")); } catch (Exception arg) { Log.LogError((object)$"[BoomboxYouTube] Failed to set environment variable: {arg}"); } Instance = this; try { _toggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("UI", "ToggleKey", (KeyCode)289, "Key to toggle the Boombox YouTube window."); Log.LogInfo((object)$"[BoomboxYouTube] UI toggle key = {_toggleKey.Value}"); } catch (Exception arg2) { Log.LogWarning((object)$"[BoomboxYouTube] Failed to bind config key: {arg2}"); } try { Youtube = new YoutubeClient(); Log.LogInfo((object)"[BoomboxYouTube] YoutubeClient created."); } catch (Exception arg3) { Log.LogError((object)$"[BoomboxYouTube] YoutubeClient creation failed: {arg3}"); } try { new Harmony("blaide.boomboxyoutube").PatchAll(); Log.LogInfo((object)"[BoomboxYouTube] Harmony patches applied."); } catch (Exception arg4) { Log.LogError((object)$"[BoomboxYouTube] Harmony patching failed: {arg4}"); } EnsureUiInstance(); Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); Log.LogInfo((object)"Boombox YouTube loaded."); } private void Update() { //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (_toggleKey == null || !Input.GetKeyDown(_toggleKey.Value)) { return; } if ((Object)(object)_ui == (Object)null) { EnsureUiInstance(); } if ((Object)(object)_ui != (Object)null) { if (!TryToggleUiViaMethod(_ui)) { TryToggleUiViaField(_ui); } } else { Log.LogWarning((object)"[BoomboxYouTube] Toggle pressed but BoomboxUI instance not found."); } } private void EnsureUiInstance() { try { BoomboxUI[] array = Object.FindObjectsOfType<BoomboxUI>(true); if (array != null && array.Length != 0) { _ui = array[0]; for (int i = 1; i < array.Length; i++) { Object.Destroy((Object)(object)array[i]); } Log.LogInfo((object)"[BoomboxYouTube] Reusing existing BoomboxUI instance."); } else { _ui = ((Component)this).gameObject.AddComponent<BoomboxUI>(); Log.LogInfo((object)"[BoomboxYouTube] BoomboxUI added to plugin GameObject."); } if ((Object)(object)_ui != (Object)null) { Object.DontDestroyOnLoad((Object)(object)((Component)_ui).gameObject); } } catch (Exception arg) { Log.LogWarning((object)$"[BoomboxYouTube] EnsureUiInstance failed: {arg}"); } } private bool TryToggleUiViaMethod(BoomboxUI ui) { try { MethodInfo method = ((object)ui).GetType().GetMethod("ToggleWindow", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method != null) { method.Invoke(ui, null); Log.LogDebug((object)"[BoomboxYouTube] Toggled UI via ToggleWindow()."); return true; } } catch (Exception ex) { Log.LogWarning((object)("[BoomboxYouTube] ToggleWindow invocation failed: " + ex.Message)); } return false; } private bool TryToggleUiViaField(BoomboxUI ui) { try { FieldInfo fieldInfo = ((object)ui).GetType().GetField("_showWindow", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? ((object)ui).GetType().GetField("showWindow", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (fieldInfo != null && fieldInfo.FieldType == typeof(bool)) { bool flag = (bool)fieldInfo.GetValue(ui); fieldInfo.SetValue(ui, !flag); Log.LogDebug((object)$"[BoomboxYouTube] Toggled UI by flipping field '{fieldInfo.Name}': {flag} -> {!flag}"); return true; } } catch (Exception ex) { Log.LogWarning((object)("[BoomboxYouTube] Field-based toggle failed: " + ex.Message)); } Log.LogWarning((object)"[BoomboxYouTube] UI toggle failed—add a public ToggleWindow() method or a bool '_showWindow' field to BoomboxUI."); return false; } private void OnDestroy() { try { } catch (Exception arg) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)$"[BoomboxYouTube] Cleanup failed: {arg}"); } } } }
plugins/YoutubeExplode.dll
Decompiled 8 minutes ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Buffers; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using System.Text.Json; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Threading.Tasks.Sources; using System.Xml.Linq; using AngleSharp.Dom; using AngleSharp.Html.Dom; using AngleSharp.Html.Parser; using Deorcify; using Microsoft.CodeAnalysis; using YoutubeExplode.Bridge; using YoutubeExplode.Bridge.Cipher; using YoutubeExplode.Channels; using YoutubeExplode.Common; using YoutubeExplode.Exceptions; using YoutubeExplode.Playlists; using YoutubeExplode.Search; using YoutubeExplode.Utils; using YoutubeExplode.Utils.Extensions; using YoutubeExplode.Videos; using YoutubeExplode.Videos.ClosedCaptions; using YoutubeExplode.Videos.Streams; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")] [assembly: AssemblyCompany("Tyrrrz")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright (C) Oleksii Holub")] [assembly: AssemblyDescription("Abstraction layer over YouTube's internal API. Note: this package has limited availability in Russia and Belarus.")] [assembly: AssemblyFileVersion("6.5.6.0")] [assembly: AssemblyInformationalVersion("6.5.6+b0f37bcc16cb8210130887c40c796f82b4af2357")] [assembly: AssemblyProduct("YoutubeExplode")] [assembly: AssemblyTitle("YoutubeExplode")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/Tyrrrz/YoutubeExplode")] [assembly: AssemblyVersion("6.5.6.0")] [module: RefSafetyRules(11)] internal class <Module> { static <Module>() { Initializer.Execute(); } } namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class IsReadOnlyAttribute : Attribute { } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class IsByRefLikeAttribute : Attribute { } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NativeIntegerAttribute : Attribute { public readonly bool[] TransformFlags; public NativeIntegerAttribute() { TransformFlags = new bool[1] { true }; } public NativeIntegerAttribute(bool[] P_0) { TransformFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } [ExcludeFromCodeCoverage] internal static class PolyfillExtensions { public static async Task<Stream> GetStreamAsync(this HttpClient httpClient, string requestUri, CancellationToken cancellationToken = default(CancellationToken)) { _ = 1; try { HttpResponseMessage obj = await httpClient.GetAsync(requestUri, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); obj.EnsureSuccessStatusCode(); return await ReadAsStreamAsync(obj.Content, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } catch (OperationCanceledException ex) when (ex.CancellationToken != cancellationToken && cancellationToken.IsCancellationRequested) { throw new OperationCanceledException(ex.Message, ex.InnerException, cancellationToken); } } public static async Task<Stream> GetStreamAsync(this HttpClient httpClient, Uri requestUri, CancellationToken cancellationToken = default(CancellationToken)) { return await GetStreamAsync(httpClient, requestUri.ToString(), cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } public static async Task<byte[]> GetByteArrayAsync(this HttpClient httpClient, string requestUri, CancellationToken cancellationToken = default(CancellationToken)) { _ = 1; try { using HttpResponseMessage response = await httpClient.GetAsync(requestUri, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); response.EnsureSuccessStatusCode(); return await ReadAsByteArrayAsync(response.Content, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } catch (OperationCanceledException ex) when (ex.CancellationToken != cancellationToken && cancellationToken.IsCancellationRequested) { throw new OperationCanceledException(ex.Message, ex.InnerException, cancellationToken); } } public static async Task<byte[]> GetByteArrayAsync(this HttpClient httpClient, Uri requestUri, CancellationToken cancellationToken = default(CancellationToken)) { return await GetByteArrayAsync(httpClient, requestUri.ToString(), cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } public static async Task<string> GetStringAsync(this HttpClient httpClient, string requestUri, CancellationToken cancellationToken = default(CancellationToken)) { _ = 1; try { using HttpResponseMessage response = await httpClient.GetAsync(requestUri, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); response.EnsureSuccessStatusCode(); return await ReadAsStringAsync(response.Content, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } catch (OperationCanceledException ex) when (ex.CancellationToken != cancellationToken && cancellationToken.IsCancellationRequested) { throw new OperationCanceledException(ex.Message, ex.InnerException, cancellationToken); } } public static async Task<string> GetStringAsync(this HttpClient httpClient, Uri requestUri, CancellationToken cancellationToken = default(CancellationToken)) { return await GetStringAsync(httpClient, requestUri.ToString(), cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } public static async Task<Stream> ReadAsStreamAsync(this HttpContent httpContent, CancellationToken cancellationToken = default(CancellationToken)) { cancellationToken.ThrowIfCancellationRequested(); return await httpContent.ReadAsStreamAsync().ConfigureAwait(continueOnCapturedContext: false); } public static async Task<byte[]> ReadAsByteArrayAsync(this HttpContent httpContent, CancellationToken cancellationToken = default(CancellationToken)) { cancellationToken.ThrowIfCancellationRequested(); return await httpContent.ReadAsByteArrayAsync().ConfigureAwait(continueOnCapturedContext: false); } public static async Task<string> ReadAsStringAsync(this HttpContent httpContent, CancellationToken cancellationToken = default(CancellationToken)) { cancellationToken.ThrowIfCancellationRequested(); return await httpContent.ReadAsStringAsync().ConfigureAwait(continueOnCapturedContext: false); } public static async Task WaitForExitAsync(this Process process, CancellationToken cancellationToken = default(CancellationToken)) { TaskCompletionSource<object?> tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously); try { process.EnableRaisingEvents = true; } catch when (process.HasExited) { return; } process.Exited += HandleExited; try { using (cancellationToken.Register(delegate { tcs.TrySetCanceled(cancellationToken); })) { await tcs.Task; } } finally { process.Exited -= HandleExited; } void HandleExited(object? sender, EventArgs args) { tcs.TrySetResult(null); } } public static bool IsAssignableTo(this Type type, Type? otherType) { return otherType?.IsAssignableFrom(type) ?? false; } public static string ReplaceLineEndings(this string str, string replacementText) { return Replace(Replace(Replace(str, "\r\n", "\n", StringComparison.Ordinal), "\r", "\n", StringComparison.Ordinal), "\n", replacementText, StringComparison.Ordinal); } public static string ReplaceLineEndings(this string str) { return ReplaceLineEndings(str, Environment.NewLine); } public static async Task WaitAsync(this Task task, TimeSpan timeout, CancellationToken cancellationToken) { Task cancellationTask = Task.Delay(timeout, cancellationToken); Task finishedTask = await Task.WhenAny(new Task[2] { task, cancellationTask }).ConfigureAwait(continueOnCapturedContext: false); await finishedTask.ConfigureAwait(continueOnCapturedContext: false); if (finishedTask == cancellationTask) { throw new TimeoutException("The operation has timed out."); } } public static async Task WaitAsync(this Task task, CancellationToken cancellationToken) { await WaitAsync(task, Timeout.InfiniteTimeSpan, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } public static async Task WaitAsync(this Task task, TimeSpan timeout) { await WaitAsync(task, timeout, CancellationToken.None).ConfigureAwait(continueOnCapturedContext: false); } public static async Task<T> WaitAsync<T>(this Task<T> task, TimeSpan timeout, CancellationToken cancellationToken) { Task cancellationTask = Task.Delay(timeout, cancellationToken); Task finishedTask = await Task.WhenAny(new Task[2] { task, cancellationTask }).ConfigureAwait(continueOnCapturedContext: false); await finishedTask.ConfigureAwait(continueOnCapturedContext: false); if (finishedTask == cancellationTask) { throw new TimeoutException("The operation has timed out."); } return task.Result; } public static async Task<T> WaitAsync<T>(this Task<T> task, CancellationToken cancellationToken) { return await WaitAsync(task, Timeout.InfiniteTimeSpan, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } public static async Task<T> WaitAsync<T>(this Task<T> task, TimeSpan timeout) { return await WaitAsync(task, timeout, CancellationToken.None).ConfigureAwait(continueOnCapturedContext: false); } public static int ReadAtLeast(this Stream stream, byte[] buffer, int minimumBytes, bool throwOnEndOfStream = true) { int i; int num; for (i = 0; i < buffer.Length; i += num) { num = stream.Read(buffer, i, Math.Min(minimumBytes, buffer.Length - i)); if (num <= 0) { break; } } if (i < minimumBytes && throwOnEndOfStream) { throw new EndOfStreamException(); } return i; } public static void ReadExactly(this Stream stream, byte[] buffer, int offset, int count) { int num; for (int i = 0; i < count; i += num) { num = stream.Read(buffer, offset + i, count - i); if (num <= 0) { throw new EndOfStreamException(); } } } public static void ReadExactly(this Stream stream, byte[] buffer) { stream.ReadExactly(buffer, 0, buffer.Length); } public static async Task<int> ReadAtLeastAsync(this Stream stream, byte[] buffer, int minimumBytes, bool throwOnEndOfStream = true, CancellationToken cancellationToken = default(CancellationToken)) { int totalBytesRead; int num; for (totalBytesRead = 0; totalBytesRead < buffer.Length; totalBytesRead += num) { num = await stream.ReadAsync(buffer, totalBytesRead, Math.Min(minimumBytes, buffer.Length - totalBytesRead), cancellationToken).ConfigureAwait(continueOnCapturedContext: false); if (num <= 0) { break; } } if (totalBytesRead < minimumBytes && throwOnEndOfStream) { throw new EndOfStreamException(); } return totalBytesRead; } public static async Task ReadExactlyAsync(this Stream stream, byte[] buffer, int offset, int count, CancellationToken cancellationToken = default(CancellationToken)) { int num; for (int totalBytesRead = 0; totalBytesRead < count; totalBytesRead += num) { num = await stream.ReadAsync(buffer, offset + totalBytesRead, count - totalBytesRead, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); if (num <= 0) { throw new EndOfStreamException(); } } } public static async Task ReadExactlyAsync(this Stream stream, byte[] buffer, CancellationToken cancellationToken = default(CancellationToken)) { await stream.ReadExactlyAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } public static int ReadAtLeast(this Stream stream, Span<byte> buffer, int minimumBytes, bool throwOnEndOfStream = true) { int i; int num; for (i = 0; i < buffer.Length; i += num) { num = Read(stream, buffer.Slice(i)); if (num <= 0) { break; } } if (i < minimumBytes && throwOnEndOfStream) { throw new EndOfStreamException(); } return i; } public static void ReadExactly(this Stream stream, Span<byte> buffer) { byte[] array = buffer.ToArray(); stream.ReadExactly(array, 0, array.Length); array.CopyTo(buffer); } public static async Task<int> ReadAtLeastAsync(this Stream stream, Memory<byte> buffer, int minimumBytes, bool throwOnEndOfStream = true, CancellationToken cancellationToken = default(CancellationToken)) { int totalBytesRead; int num; for (totalBytesRead = 0; totalBytesRead < buffer.Length; totalBytesRead += num) { num = await ReadAsync(stream, buffer.Slice(totalBytesRead), cancellationToken).ConfigureAwait(continueOnCapturedContext: false); if (num <= 0) { break; } } if (totalBytesRead < minimumBytes && throwOnEndOfStream) { throw new EndOfStreamException(); } return totalBytesRead; } public static async Task ReadExactlyAsync(this Stream stream, Memory<byte> buffer, CancellationToken cancellationToken = default(CancellationToken)) { byte[] bufferArray = buffer.ToArray(); await stream.ReadExactlyAsync(bufferArray, 0, bufferArray.Length, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); bufferArray.CopyTo(buffer); } public static Task CancelAsync(this CancellationTokenSource cts) { cts.Cancel(); return Task.CompletedTask; } public static void Deconstruct(this DictionaryEntry entry, out object key, out object? value) { key = entry.Key; value = entry.Value; } public static void Deconstruct<TKey, TValue>(this KeyValuePair<TKey, TValue> pair, out TKey key, out TValue value) { key = pair.Key; value = pair.Value; } public static IEnumerable<Match> AsEnumerable(this MatchCollection matchCollection) { return matchCollection.Cast<Match>(); } public static IEnumerator<Match> GetEnumerator(this MatchCollection matchCollection) { return matchCollection.AsEnumerable().GetEnumerator(); } public static Match[] ToArray(this MatchCollection matchCollection) { return matchCollection.AsEnumerable().ToArray(); } public static bool StartsWith(this string str, char c) { if (str.Length > 0) { return str[0] == c; } return false; } public static bool EndsWith(this string str, char c) { if (str.Length > 0) { return str[str.Length - 1] == c; } return false; } public static bool Contains(this string str, char c) { return str.IndexOf(c) >= 0; } public static string Replace(this string str, string oldValue, string? newValue, StringComparison comparison) { StringBuilder stringBuilder = new StringBuilder(); int num = 0; int num2 = 0; while (true) { num = str.IndexOf(oldValue, num, comparison); if (num < 0) { break; } stringBuilder.Append(str, num2, num - num2); stringBuilder.Append(newValue); num += oldValue.Length; num2 = num; } stringBuilder.Append(str, num2, str.Length - num2); return stringBuilder.ToString(); } public static string Replace(this string str, string oldValue, string? newValue, bool ignoreCase, CultureInfo? culture) { StringBuilder stringBuilder = new StringBuilder(); int num = 0; int num2 = 0; while (true) { num = (culture ?? CultureInfo.CurrentCulture).CompareInfo.IndexOf(str, oldValue, num, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None); if (num < 0) { break; } stringBuilder.Append(str, num2, num - num2); stringBuilder.Append(newValue); num += oldValue.Length; num2 = num; } stringBuilder.Append(str, num2, str.Length - num2); return stringBuilder.ToString(); } public static string[] Split(this string str, char separator, int count, StringSplitOptions options = StringSplitOptions.None) { return str.Split(new char[1] { separator }, count, options); } public static string[] Split(this string str, char separator, StringSplitOptions options = StringSplitOptions.None) { return str.Split(new char[1] { separator }, options); } public static string[] Split(this string str, string? separator, int count, StringSplitOptions options = StringSplitOptions.None) { return str.Split(new string[1] { separator ?? "" }, count, options); } public static string[] Split(this string str, string? separator, StringSplitOptions options = StringSplitOptions.None) { return str.Split(new string[1] { separator ?? "" }, options); } public static void NextBytes(this Random random, Span<byte> buffer) { byte[] array = buffer.ToArray(); random.NextBytes(array); array.CopyTo(buffer); } public static int Read(this Stream stream, byte[] buffer) { return stream.Read(buffer, 0, buffer.Length); } public static void Write(this Stream stream, byte[] buffer) { stream.Write(buffer, 0, buffer.Length); } public static async Task CopyToAsync(this Stream stream, Stream destination, CancellationToken cancellationToken = default(CancellationToken)) { await stream.CopyToAsync(destination, 81920, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } public static async Task<int> ReadAsync(this Stream stream, byte[] buffer, CancellationToken cancellationToken = default(CancellationToken)) { return await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } public static async Task WriteAsync(this Stream stream, byte[] buffer, CancellationToken cancellationToken = default(CancellationToken)) { await stream.WriteAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } public static int Read(this Stream stream, Span<byte> buffer) { byte[] array = buffer.ToArray(); int result = stream.Read(array, 0, array.Length); array.CopyTo(buffer); return result; } public static void Write(this Stream stream, ReadOnlySpan<byte> buffer) { byte[] array = buffer.ToArray(); stream.Write(array, 0, array.Length); } public static async Task<int> ReadAsync(this Stream stream, Memory<byte> buffer, CancellationToken cancellationToken = default(CancellationToken)) { byte[] bufferArray = buffer.ToArray(); int result = await stream.ReadAsync(bufferArray, 0, bufferArray.Length, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); bufferArray.CopyTo(buffer); return result; } public static async Task WriteAsync(this Stream stream, ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default(CancellationToken)) { byte[] array = buffer.ToArray(); await stream.WriteAsync(array, 0, array.Length, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } public static int Read(this StreamReader reader, char[] buffer) { return reader.Read(buffer, 0, buffer.Length); } public static async Task<int> ReadAsync(this StreamReader reader, char[] buffer, CancellationToken cancellationToken = default(CancellationToken)) { cancellationToken.ThrowIfCancellationRequested(); return await reader.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(continueOnCapturedContext: false); } public static int Read(this StreamReader reader, Span<char> buffer) { char[] array = buffer.ToArray(); int result = reader.Read(array, 0, array.Length); array.CopyTo(buffer); return result; } public static async Task<int> ReadAsync(this StreamReader reader, Memory<char> buffer, CancellationToken cancellationToken = default(CancellationToken)) { char[] bufferArray = buffer.ToArray(); cancellationToken.ThrowIfCancellationRequested(); int result = await reader.ReadAsync(bufferArray, 0, bufferArray.Length).ConfigureAwait(continueOnCapturedContext: false); bufferArray.CopyTo(buffer); return result; } public static void Write(this StreamWriter writer, ReadOnlySpan<char> buffer) { char[] array = buffer.ToArray(); writer.Write(array, 0, array.Length); } public static async Task WriteAsync(this StreamWriter writer, Memory<char> buffer, CancellationToken cancellationToken = default(CancellationToken)) { char[] array = buffer.ToArray(); cancellationToken.ThrowIfCancellationRequested(); await writer.WriteAsync(array, 0, array.Length).ConfigureAwait(continueOnCapturedContext: false); } public static bool Contains(this string str, char c, StringComparison comparison) { return Contains(str, c.ToString(), comparison); } public static bool Contains(this string str, string sub, StringComparison comparison) { return str.IndexOf(sub, comparison) >= 0; } } namespace YoutubeExplode { public class YoutubeClient : IDisposable { private readonly HttpClient _youtubeHttp; public VideoClient Videos { get; } public PlaylistClient Playlists { get; } public ChannelClient Channels { get; } public SearchClient Search { get; } public YoutubeClient(HttpClient http, IReadOnlyList<Cookie> initialCookies) { _youtubeHttp = new HttpClient(new YoutubeHttpHandler(http, initialCookies), disposeHandler: true); Videos = new VideoClient(_youtubeHttp); Playlists = new PlaylistClient(_youtubeHttp); Channels = new ChannelClient(_youtubeHttp); Search = new SearchClient(_youtubeHttp); } public YoutubeClient(HttpClient http) : this(http, Array.Empty<Cookie>()) { } public YoutubeClient(IReadOnlyList<Cookie> initialCookies) : this(Http.Client, initialCookies) { } public YoutubeClient() : this(Http.Client) { } public void Dispose() { _youtubeHttp.Dispose(); } } internal class YoutubeHttpHandler : ClientDelegatingHandler { private readonly CookieContainer _cookieContainer = new CookieContainer(); public YoutubeHttpHandler(HttpClient http, IReadOnlyList<Cookie> initialCookies, bool disposeClient = false) : base(http, disposeClient) { _cookieContainer.Add(new Cookie("SOCS", "CAISEwgDEgk4MTM4MzYzNTIaAmVuIAEaBgiApPzGBg") { Domain = "youtube.com" }); foreach (Cookie initialCookie in initialCookies) { _cookieContainer.Add(initialCookie); } } private string? TryGenerateAuthHeaderValue(Uri uri) { Cookie[] source = _cookieContainer.GetCookies(uri).Cast<Cookie>().ToArray(); string text = source.FirstOrDefault((Cookie c) => string.Equals(c.Name, "__Secure-3PAPISID", StringComparison.Ordinal))?.Value ?? source.FirstOrDefault((Cookie c) => string.Equals(c.Name, "SAPISID", StringComparison.Ordinal))?.Value; if (string.IsNullOrWhiteSpace(text)) { return null; } long num = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); string domain = uri.GetDomain(); string s = $"{num} {text} {domain}"; string arg = Hash.Compute(SHA1.Create(), Encoding.UTF8.GetBytes(s)).ToHex(); return $"SAPISIDHASH {num}_{arg}"; } private HttpRequestMessage HandleRequest(HttpRequestMessage request) { if ((object)request.RequestUri == null) { return request; } if (request.RequestUri.AbsolutePath.StartsWith("/youtubei/", StringComparison.Ordinal) && !UrlEx.ContainsQueryParameter(request.RequestUri.Query, "key")) { request.RequestUri = new Uri(UrlEx.SetQueryParameter(request.RequestUri.OriginalString, "key", "AIzaSyA8eiZmM1FaDVjRy-df2KTyQ_vz_yYM39w")); } if (!UrlEx.ContainsQueryParameter(request.RequestUri.Query, "hl")) { request.RequestUri = new Uri(UrlEx.SetQueryParameter(request.RequestUri.OriginalString, "hl", "en")); } if (!request.Headers.Contains("Origin")) { request.Headers.Add("Origin", request.RequestUri.GetDomain()); } if (!request.Headers.Contains("User-Agent")) { request.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36"); } if (!request.Headers.Contains("Cookie") && _cookieContainer.Count > 0) { string cookieHeader = _cookieContainer.GetCookieHeader(request.RequestUri); if (!string.IsNullOrWhiteSpace(cookieHeader)) { request.Headers.Add("Cookie", cookieHeader); } } if (!request.Headers.Contains("Authorization")) { string text = TryGenerateAuthHeaderValue(request.RequestUri); if (text != null) { request.Headers.Add("Authorization", text); } } return request; } private HttpResponseMessage HandleResponse(HttpResponseMessage response) { if ((object)response.RequestMessage?.RequestUri == null) { return response; } if (response.StatusCode == HttpStatusCode.TooManyRequests) { throw new RequestLimitExceededException("Exceeded request rate limit. Please try again in a few hours. Alternatively, inject cookies corresponding to a pre-authenticated user when initializing an instance of `YoutubeClient`."); } if (response.Headers.TryGetValues("Set-Cookie", out IEnumerable<string> values)) { foreach (string item in values) { try { _cookieContainer.SetCookies(response.RequestMessage.RequestUri, item); } catch (CookieException) { } } } return response; } protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { int retriesRemaining = 5; HttpResponseMessage httpResponseMessage; while (true) { httpResponseMessage = HandleResponse(await base.SendAsync(HandleRequest(request), cancellationToken)); if (httpResponseMessage.StatusCode < HttpStatusCode.InternalServerError || retriesRemaining <= 0) { break; } httpResponseMessage.Dispose(); retriesRemaining--; } return httpResponseMessage; } } } namespace YoutubeExplode.Utils { internal abstract class ClientDelegatingHandler : HttpMessageHandler { [CompilerGenerated] private HttpClient <http>P; [CompilerGenerated] private bool <disposeClient>P; protected ClientDelegatingHandler(HttpClient http, bool disposeClient = false) { <http>P = http; <disposeClient>P = disposeClient; base..ctor(); } protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { using HttpRequestMessage clonedRequest = request.Clone(); return await <http>P.SendAsync(clonedRequest, HttpCompletionOption.ResponseHeadersRead, cancellationToken); } protected override void Dispose(bool disposing) { if (disposing && <disposeClient>P) { <http>P.Dispose(); } base.Dispose(disposing); } } internal static class Hash { public static byte[] Compute(HashAlgorithm algorithm, byte[] data) { using (algorithm) { return algorithm.ComputeHash(data); } } } internal static class Html { private static readonly HtmlParser HtmlParser = new HtmlParser(); public static IHtmlDocument Parse(string source) { return HtmlParser.ParseDocument(source); } } internal static class Http { private static readonly Lazy<HttpClient> HttpClientLazy = new Lazy<HttpClient>(() => new HttpClient()); public static HttpClient Client => HttpClientLazy.Value; } internal static class Json { public static string Extract(string source) { StringBuilder stringBuilder = new StringBuilder(); int num = 0; bool flag = false; foreach (var item3 in source.Index()) { int item = item3.index; char item2 = item3.value; char c = ((item > 0) ? source[item - 1] : '\0'); stringBuilder.Append(item2); if (item2 == '"' && c != '\\') { flag = !flag; } else if (item2 == '{' && !flag) { num++; } else if (item2 == '}' && !flag) { num--; } if (num == 0) { break; } } return stringBuilder.ToString(); } public static JsonElement Parse(string source) { using JsonDocument jsonDocument = JsonDocument.Parse(source); return jsonDocument.RootElement.Clone(); } public static JsonElement? TryParse(string source) { try { return Parse(source); } catch (JsonException) { return null; } } public static string Encode(string value) { StringBuilder stringBuilder = new StringBuilder(value.Length); foreach (char c in value) { switch (c) { case '\n': stringBuilder.Append("\\n"); break; case '\r': stringBuilder.Append("\\r"); break; case '\t': stringBuilder.Append("\\t"); break; case '\\': stringBuilder.Append("\\\\"); break; case '"': stringBuilder.Append("\\\""); break; default: stringBuilder.Append(c); break; } } return stringBuilder.ToString(); } public static string Serialize(string? value) { if (value == null) { return "null"; } return "\"" + Encode(value) + "\""; } public static string Serialize(int? value) { if (!value.HasValue) { return "null"; } return value.Value.ToString(CultureInfo.InvariantCulture); } } internal static class UrlEx { [CompilerGenerated] private sealed class <EnumerateQueryParameters>d__0 : IEnumerable<KeyValuePair<string, string>>, IEnumerable, IEnumerator<KeyValuePair<string, string>>, IEnumerator, IDisposable { private int <>1__state; private KeyValuePair<string, string> <>2__current; private int <>l__initialThreadId; private string url; public string <>3__url; private string[] <>7__wrap1; private int <>7__wrap2; KeyValuePair<string, string> IEnumerator<KeyValuePair<string, string>>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <EnumerateQueryParameters>d__0(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { <>7__wrap1 = null; <>1__state = -2; } private bool MoveNext() { int num = <>1__state; if (num != 0) { if (num != 1) { return false; } <>1__state = -1; goto IL_00b8; } <>1__state = -1; string text = (PolyfillExtensions.Contains(url, '?') ? url.SubstringAfter("?") : url); <>7__wrap1 = text.Split(new char[1] { '&' }); <>7__wrap2 = 0; goto IL_00c6; IL_00b8: <>7__wrap2++; goto IL_00c6; IL_00c6: if (<>7__wrap2 < <>7__wrap1.Length) { string str = <>7__wrap1[<>7__wrap2]; string text2 = WebUtility.UrlDecode(str.SubstringUntil("=")); string value = WebUtility.UrlDecode(str.SubstringAfter("=")); if (!string.IsNullOrWhiteSpace(text2)) { <>2__current = new KeyValuePair<string, string>(text2, value); <>1__state = 1; return true; } goto IL_00b8; } <>7__wrap1 = null; return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator<KeyValuePair<string, string>> IEnumerable<KeyValuePair<string, string>>.GetEnumerator() { <EnumerateQueryParameters>d__0 <EnumerateQueryParameters>d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; <EnumerateQueryParameters>d__ = this; } else { <EnumerateQueryParameters>d__ = new <EnumerateQueryParameters>d__0(0); } <EnumerateQueryParameters>d__.url = <>3__url; return <EnumerateQueryParameters>d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<KeyValuePair<string, string>>)this).GetEnumerator(); } } [IteratorStateMachine(typeof(<EnumerateQueryParameters>d__0))] private static IEnumerable<KeyValuePair<string, string>> EnumerateQueryParameters(string url) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <EnumerateQueryParameters>d__0(-2) { <>3__url = url }; } public static IReadOnlyDictionary<string, string> GetQueryParameters(string url) { return EnumerateQueryParameters(url).ToDictionary<KeyValuePair<string, string>, string, string>((KeyValuePair<string, string> kvp) => kvp.Key, (KeyValuePair<string, string> kvp) => kvp.Value); } private static KeyValuePair<string, string>? TryGetQueryParameter(string url, string key) { foreach (KeyValuePair<string, string> item in EnumerateQueryParameters(url)) { if (string.Equals(item.Key, key, StringComparison.Ordinal)) { return item; } } return null; } public static string? TryGetQueryParameterValue(string url, string key) { return TryGetQueryParameter(url, key)?.Value; } public static bool ContainsQueryParameter(string url, string key) { return TryGetQueryParameterValue(url, key) != null; } public static string RemoveQueryParameter(string url, string key) { if (!ContainsQueryParameter(url, key)) { return url; } UriBuilder uriBuilder = new UriBuilder(url); StringBuilder stringBuilder = new StringBuilder(); foreach (KeyValuePair<string, string> item in EnumerateQueryParameters(url)) { if (!string.Equals(item.Key, key, StringComparison.Ordinal)) { stringBuilder.Append((stringBuilder.Length > 0) ? '&' : '?'); stringBuilder.Append(Uri.EscapeDataString(item.Key)); stringBuilder.Append('='); stringBuilder.Append(Uri.EscapeDataString(item.Value)); } } uriBuilder.Query = stringBuilder.ToString(); return uriBuilder.ToString(); } public static string SetQueryParameter(string url, string key, string value) { string text = RemoveQueryParameter(url, key); bool flag = PolyfillExtensions.Contains(text, '?'); return text + (flag ? '&' : '?') + Uri.EscapeDataString(key) + "=" + Uri.EscapeDataString(value); } } internal static class Xml { public static XElement Parse(string source) { return XElement.Parse(source, LoadOptions.PreserveWhitespace).StripNamespaces(); } } } namespace YoutubeExplode.Utils.Extensions { internal static class AsyncCollectionExtensions { public static async IAsyncEnumerable<T> TakeAsync<T>(this IAsyncEnumerable<T> source, int count) { int currentCount = 0; await foreach (T item in source) { if (currentCount >= count) { break; } yield return item; currentCount++; } } public static async IAsyncEnumerable<T> SelectManyAsync<TSource, T>(this IAsyncEnumerable<TSource> source, Func<TSource, IEnumerable<T>> transform) { await foreach (TSource item in source) { foreach (T item2 in transform(item)) { yield return item2; } } } public static async IAsyncEnumerable<T> OfTypeAsync<T>(this IAsyncEnumerable<object> source) { await foreach (object item in source) { if (item is T) { yield return (T)item; } } } public static async ValueTask<List<T>> ToListAsync<T>(this IAsyncEnumerable<T> source) { List<T> list = new List<T>(); await foreach (T item in source) { list.Add(item); } return list; } public static ValueTaskAwaiter<List<T>> GetAwaiter<T>(this IAsyncEnumerable<T> source) { return source.ToListAsync().GetAwaiter(); } } internal static class BinaryExtensions { public static string ToHex(this byte[] data, bool isUpperCase = true) { StringBuilder stringBuilder = new StringBuilder(2 * data.Length); foreach (byte b in data) { stringBuilder.Append(b.ToString(isUpperCase ? "X2" : "x2", CultureInfo.InvariantCulture)); } return stringBuilder.ToString(); } } internal static class CollectionExtensions { [CompilerGenerated] private sealed class <WhereNotNull>d__0<T> : IEnumerable<T>, IEnumerable, IEnumerator<T>, IEnumerator, IDisposable where T : class { private int <>1__state; private T <>2__current; private int <>l__initialThreadId; private IEnumerable<T?> source; public IEnumerable<T?> <>3__source; private IEnumerator<T?> <>7__wrap1; T IEnumerator<T>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <WhereNotNull>d__0(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } <>7__wrap1 = null; <>1__state = -2; } private bool MoveNext() { try { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>7__wrap1 = source.GetEnumerator(); <>1__state = -3; break; case 1: <>1__state = -3; break; } while (<>7__wrap1.MoveNext()) { T current = <>7__wrap1.Current; if (current != null) { <>2__current = current; <>1__state = 1; return true; } } <>m__Finally1(); <>7__wrap1 = null; return false; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (<>7__wrap1 != null) { <>7__wrap1.Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator<T> IEnumerable<T>.GetEnumerator() { <WhereNotNull>d__0<T> <WhereNotNull>d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; <WhereNotNull>d__ = this; } else { <WhereNotNull>d__ = new <WhereNotNull>d__0<T>(0); } <WhereNotNull>d__.source = <>3__source; return <WhereNotNull>d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<T>)this).GetEnumerator(); } } [CompilerGenerated] private sealed class <WhereNotNull>d__1<T> : IEnumerable<T>, IEnumerable, IEnumerator<T>, IEnumerator, IDisposable where T : struct { private int <>1__state; private T <>2__current; private int <>l__initialThreadId; private IEnumerable<T?> source; public IEnumerable<T?> <>3__source; private IEnumerator<T?> <>7__wrap1; T IEnumerator<T>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <WhereNotNull>d__1(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } <>7__wrap1 = null; <>1__state = -2; } private bool MoveNext() { try { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>7__wrap1 = source.GetEnumerator(); <>1__state = -3; break; case 1: <>1__state = -3; break; } while (<>7__wrap1.MoveNext()) { T? current = <>7__wrap1.Current; if (current.HasValue) { <>2__current = current.Value; <>1__state = 1; return true; } } <>m__Finally1(); <>7__wrap1 = null; return false; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (<>7__wrap1 != null) { <>7__wrap1.Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator<T> IEnumerable<T>.GetEnumerator() { <WhereNotNull>d__1<T> <WhereNotNull>d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; <WhereNotNull>d__ = this; } else { <WhereNotNull>d__ = new <WhereNotNull>d__1<T>(0); } <WhereNotNull>d__.source = <>3__source; return <WhereNotNull>d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<T>)this).GetEnumerator(); } } [IteratorStateMachine(typeof(<WhereNotNull>d__0<>))] public static IEnumerable<T> WhereNotNull<T>(this IEnumerable<T?> source) where T : class { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <WhereNotNull>d__0<T>(-2) { <>3__source = source }; } [IteratorStateMachine(typeof(<WhereNotNull>d__1<>))] public static IEnumerable<T> WhereNotNull<T>(this IEnumerable<T?> source) where T : struct { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <WhereNotNull>d__1<T>(-2) { <>3__source = source }; } public static T? ElementAtOrNull<T>(this IEnumerable<T> source, int index) where T : struct { IReadOnlyList<T> readOnlyList = (source as IReadOnlyList<T>) ?? source.ToArray(); if (index >= readOnlyList.Count) { return null; } return readOnlyList[index]; } public static T? FirstOrNull<T>(this IEnumerable<T> source) where T : struct { using (IEnumerator<T> enumerator = source.GetEnumerator()) { if (enumerator.MoveNext()) { return enumerator.Current; } } return null; } } internal static class GenericExtensions { public static TOut Pipe<TIn, TOut>(this TIn input, Func<TIn, TOut> transform) { return transform(input); } } internal static class HttpExtensions { private class NonDisposableHttpContent : HttpContent { [CompilerGenerated] private HttpContent <content>P; public NonDisposableHttpContent(HttpContent content) { <content>P = content; base..ctor(); } protected override async Task SerializeToStreamAsync(Stream stream, TransportContext? context) { await <content>P.CopyToAsync(stream); } protected override bool TryComputeLength(out long length) { length = 0L; return false; } } public static HttpRequestMessage Clone(this HttpRequestMessage request) { HttpRequestMessage httpRequestMessage = new HttpRequestMessage(request.Method, request.RequestUri) { Version = request.Version, Content = ((request.Content != null) ? new NonDisposableHttpContent(request.Content) : null) }; string key; IEnumerable<string> value; foreach (KeyValuePair<string, IEnumerable<string>> header in request.Headers) { PolyfillExtensions.Deconstruct(header, out key, out value); string name = key; IEnumerable<string> values = value; httpRequestMessage.Headers.TryAddWithoutValidation(name, values); } if (request.Content != null && httpRequestMessage.Content != null) { foreach (KeyValuePair<string, IEnumerable<string>> header2 in request.Content.Headers) { PolyfillExtensions.Deconstruct(header2, out key, out value); string name2 = key; IEnumerable<string> values2 = value; httpRequestMessage.Content.Headers.TryAddWithoutValidation(name2, values2); } } return httpRequestMessage; } public static async ValueTask<HttpResponseMessage> HeadAsync(this HttpClient http, string requestUri, CancellationToken cancellationToken = default(CancellationToken)) { using HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Head, requestUri); return await http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken); } } internal static class JsonExtensions { [CompilerGenerated] private sealed class <>c__DisplayClass9_0 { public string propertyName; internal IEnumerable<JsonElement> <EnumerateDescendantProperties>b__0(JsonElement j) { return j.EnumerateDescendantProperties(propertyName); } internal IEnumerable<JsonElement> <EnumerateDescendantProperties>b__1(JsonProperty j) { return j.Value.EnumerateDescendantProperties(propertyName); } } [CompilerGenerated] private sealed class <EnumerateDescendantProperties>d__9 : IEnumerable<JsonElement>, IEnumerable, IEnumerator<JsonElement>, IEnumerator, IDisposable { private int <>1__state; private JsonElement <>2__current; private int <>l__initialThreadId; private string propertyName; public string <>3__propertyName; private JsonElement element; public JsonElement <>3__element; private <>c__DisplayClass9_0 <>8__1; private IEnumerator<JsonElement> <>7__wrap1; JsonElement IEnumerator<JsonElement>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <EnumerateDescendantProperties>d__9(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { switch (<>1__state) { case -3: case 2: try { } finally { <>m__Finally1(); } break; case -4: case 3: try { } finally { <>m__Finally2(); } break; } <>8__1 = null; <>7__wrap1 = null; <>1__state = -2; } private bool MoveNext() { try { IEnumerable<JsonElement> enumerable; IEnumerable<JsonElement> enumerable2; switch (<>1__state) { default: return false; case 0: { <>1__state = -1; <>8__1 = new <>c__DisplayClass9_0(); <>8__1.propertyName = propertyName; JsonElement? propertyOrNull = element.GetPropertyOrNull(<>8__1.propertyName); if (propertyOrNull.HasValue) { <>2__current = propertyOrNull.Value; <>1__state = 1; return true; } goto IL_0089; } case 1: <>1__state = -1; goto IL_0089; case 2: <>1__state = -3; goto IL_00f1; case 3: { <>1__state = -4; break; } IL_0089: enumerable = element.EnumerateArrayOrEmpty().SelectMany((JsonElement j) => j.EnumerateDescendantProperties(<>8__1.propertyName)); <>7__wrap1 = enumerable.GetEnumerator(); <>1__state = -3; goto IL_00f1; IL_00f1: if (<>7__wrap1.MoveNext()) { JsonElement current = <>7__wrap1.Current; <>2__current = current; <>1__state = 2; return true; } <>m__Finally1(); <>7__wrap1 = null; enumerable2 = element.EnumerateObjectOrEmpty().SelectMany((JsonProperty j) => j.Value.EnumerateDescendantProperties(<>8__1.propertyName)); <>7__wrap1 = enumerable2.GetEnumerator(); <>1__state = -4; break; } if (<>7__wrap1.MoveNext()) { JsonElement current2 = <>7__wrap1.Current; <>2__current = current2; <>1__state = 3; return true; } <>m__Finally2(); <>7__wrap1 = null; return false; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (<>7__wrap1 != null) { <>7__wrap1.Dispose(); } } private void <>m__Finally2() { <>1__state = -1; if (<>7__wrap1 != null) { <>7__wrap1.Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator<JsonElement> IEnumerable<JsonElement>.GetEnumerator() { <EnumerateDescendantProperties>d__9 <EnumerateDescendantProperties>d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; <EnumerateDescendantProperties>d__ = this; } else { <EnumerateDescendantProperties>d__ = new <EnumerateDescendantProperties>d__9(0); } <EnumerateDescendantProperties>d__.element = <>3__element; <EnumerateDescendantProperties>d__.propertyName = <>3__propertyName; return <EnumerateDescendantProperties>d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<JsonElement>)this).GetEnumerator(); } } public static JsonElement? GetPropertyOrNull(this JsonElement element, string propertyName) { if (element.ValueKind != JsonValueKind.Object) { return null; } if (element.TryGetProperty(propertyName, out var value) && value.ValueKind != JsonValueKind.Null && value.ValueKind != 0) { return value; } return null; } public static bool? GetBooleanOrNull(this JsonElement element) { return element.ValueKind switch { JsonValueKind.True => true, JsonValueKind.False => false, _ => null, }; } public static string? GetStringOrNull(this JsonElement element) { if (element.ValueKind != JsonValueKind.String) { return null; } return element.GetString(); } public static int? GetInt32OrNull(this JsonElement element) { if (element.ValueKind != JsonValueKind.Number || !element.TryGetInt32(out var value)) { return null; } return value; } public static long? GetInt64OrNull(this JsonElement element) { if (element.ValueKind != JsonValueKind.Number || !element.TryGetInt64(out var value)) { return null; } return value; } public static JsonElement.ArrayEnumerator? EnumerateArrayOrNull(this JsonElement element) { if (element.ValueKind != JsonValueKind.Array) { return null; } return element.EnumerateArray(); } public static JsonElement.ArrayEnumerator EnumerateArrayOrEmpty(this JsonElement element) { return element.EnumerateArrayOrNull().GetValueOrDefault(); } public static JsonElement.ObjectEnumerator? EnumerateObjectOrNull(this JsonElement element) { if (element.ValueKind != JsonValueKind.Object) { return null; } return element.EnumerateObject(); } public static JsonElement.ObjectEnumerator EnumerateObjectOrEmpty(this JsonElement element) { return element.EnumerateObjectOrNull().GetValueOrDefault(); } [IteratorStateMachine(typeof(<EnumerateDescendantProperties>d__9))] public static IEnumerable<JsonElement> EnumerateDescendantProperties(this JsonElement element, string propertyName) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <EnumerateDescendantProperties>d__9(-2) { <>3__element = element, <>3__propertyName = propertyName }; } } internal static class StreamExtensions { public static async ValueTask CopyToAsync(this Stream source, Stream destination, IProgress<double>? progress = null, CancellationToken cancellationToken = default(CancellationToken)) { using IMemoryOwner<byte> buffer = MemoryPool<byte>.Shared.Rent(81920); long totalBytesRead = 0L; while (true) { int bytesRead = await PolyfillExtensions.ReadAsync(source, buffer.Memory, cancellationToken); if (bytesRead <= 0) { break; } await PolyfillExtensions.WriteAsync(destination, buffer.Memory.Slice(0, bytesRead), cancellationToken); totalBytesRead += bytesRead; progress?.Report(1.0 * (double)totalBytesRead / (double)source.Length); } } } internal static class StringExtensions { public static string? NullIfWhiteSpace(this string str) { if (string.IsNullOrWhiteSpace(str)) { return null; } return str; } public static string SubstringUntil(this string str, string sub, StringComparison comparison = StringComparison.Ordinal) { int num = str.IndexOf(sub, comparison); if (num >= 0) { return str.Substring(0, num); } return str; } public static string SubstringAfter(this string str, string sub, StringComparison comparison = StringComparison.Ordinal) { int num = str.IndexOf(sub, comparison); if (num >= 0) { return str.Substring(num + sub.Length, str.Length - num - sub.Length); } return string.Empty; } public static string StripNonDigit(this string str) { StringBuilder stringBuilder = new StringBuilder(); foreach (char item in str.Where(char.IsDigit)) { stringBuilder.Append(item); } return stringBuilder.ToString(); } public static string Reverse(this string str) { StringBuilder stringBuilder = new StringBuilder(str.Length); for (int num = str.Length - 1; num >= 0; num--) { stringBuilder.Append(str[num]); } return stringBuilder.ToString(); } public static string SwapChars(this string str, int firstCharIndex, int secondCharIndex) { return new StringBuilder(str) { [firstCharIndex] = str[secondCharIndex], [secondCharIndex] = str[firstCharIndex] }.ToString(); } public static int? ParseIntOrNull(this string str) { if (!int.TryParse(str, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out var result)) { return null; } return result; } public static int ParseInt(this string str) { return str.ParseIntOrNull() ?? throw new FormatException("Cannot parse integer number from string '" + str + "'."); } public static long? ParseLongOrNull(this string str) { if (!long.TryParse(str, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out var result)) { return null; } return result; } public static double? ParseDoubleOrNull(this string str) { if (!double.TryParse(str, NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.InvariantInfo, out var result)) { return null; } return result; } public static TimeSpan? ParseTimeSpanOrNull(this string str, string[] formats) { if (!TimeSpan.TryParseExact(str, formats, DateTimeFormatInfo.InvariantInfo, out var result)) { return null; } return result; } public static DateTimeOffset? ParseDateTimeOffsetOrNull(this string str) { if (!DateTimeOffset.TryParse(str, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None, out var result)) { return null; } return result; } public static string ConcatToString<T>(this IEnumerable<T> source) { return string.Concat(source); } } internal static class UriExtensions { public static string GetDomain(this Uri uri) { return uri.Scheme + Uri.SchemeDelimiter + uri.Host; } } internal static class XElementExtensions { public static XElement StripNamespaces(this XElement element) { XElement xElement = new XElement(element); foreach (XElement item in xElement.DescendantsAndSelf()) { item.Name = XNamespace.None.GetName(item.Name.LocalName); item.ReplaceAttributes(from a in item.Attributes() where !a.IsNamespaceDeclaration where a.Name.Namespace != XNamespace.Xml && a.Name.Namespace != XNamespace.Xmlns select new XAttribute(XNamespace.None.GetName(a.Name.LocalName), a.Value)); } return xElement; } } } namespace YoutubeExplode.Search { public class ChannelSearchResult : ISearchResult, IBatchItem, IChannel { public ChannelId Id { get; } public string Url => $"https://www.youtube.com/channel/{Id}"; public string Title { get; } public IReadOnlyList<Thumbnail> Thumbnails { get; } public ChannelSearchResult(ChannelId id, string title, IReadOnlyList<Thumbnail> thumbnails) { Id = id; Title = title; Thumbnails = thumbnails; base..ctor(); } [ExcludeFromCodeCoverage] public override string ToString() { return "Channel (" + Title + ")"; } } public interface ISearchResult : IBatchItem { string Url { get; } string Title { get; } } public class PlaylistSearchResult : ISearchResult, IBatchItem, IPlaylist { public PlaylistId Id { get; } public string Url => $"https://www.youtube.com/playlist?list={Id}"; public string Title { get; } public Author? Author { get; } public IReadOnlyList<Thumbnail> Thumbnails { get; } public PlaylistSearchResult(PlaylistId id, string title, Author? author, IReadOnlyList<Thumbnail> thumbnails) { Id = id; Title = title; Author = author; Thumbnails = thumbnails; base..ctor(); } [ExcludeFromCodeCoverage] public override string ToString() { return "Playlist (" + Title + ")"; } } public class SearchClient { [CompilerGenerated] private sealed class <GetResultBatchesAsync>d__2 : IAsyncEnumerable<Batch<ISearchResult>>, IAsyncEnumerator<Batch<ISearchResult>>, IAsyncDisposable, IValueTaskSource<bool>, IValueTaskSource, IAsyncStateMachine { public int <>1__state; public AsyncIteratorMethodBuilder <>t__builder; public ManualResetValueTaskSourceCore<bool> <>v__promiseOfValueOrEnd; private Batch<ISearchResult> <>2__current; private bool <>w__disposeMode; private CancellationTokenSource <>x__combinedTokens; private int <>l__initialThreadId; public SearchClient <>4__this; private string searchQuery; public string <>3__searchQuery; private SearchFilter searchFilter; public SearchFilter <>3__searchFilter; private CancellationToken cancellationToken; public CancellationToken <>3__cancellationToken; private HashSet<string> <encounteredIds>5__2; private List<ISearchResult> <results>5__3; private SearchResponse <searchResults>5__4; private ValueTaskAwaiter<SearchResponse> <>u__1; Batch<ISearchResult> IAsyncEnumerator<Batch<ISearchResult>>.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <GetResultBatchesAsync>d__2(int <>1__state) { <>t__builder = AsyncIteratorMethodBuilder.Create(); this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } private void MoveNext() { int num = <>1__state; SearchClient searchClient = <>4__this; try { string text; ValueTaskAwaiter<SearchResponse> awaiter; switch (num) { default: if (!<>w__disposeMode) { num = (<>1__state = -1); <encounteredIds>5__2 = new HashSet<string>(StringComparer.Ordinal); text = null; goto IL_0053; } goto end_IL_000e; case 0: awaiter = <>u__1; <>u__1 = default(ValueTaskAwaiter<SearchResponse>); num = (<>1__state = -1); break; case -4: { num = (<>1__state = -1); if (!<>w__disposeMode) { text = <searchResults>5__4.ContinuationToken; <results>5__3 = null; <searchResults>5__4 = null; if (!string.IsNullOrWhiteSpace(text)) { goto IL_0053; } } goto end_IL_000e; } IL_0053: <results>5__3 = new List<ISearchResult>(); <>2__current = null; awaiter = searchClient._controller.GetSearchResponseAsync(searchQuery, searchFilter, text, cancellationToken).GetAwaiter(); if (!awaiter.IsCompleted) { num = (<>1__state = 0); <>u__1 = awaiter; <GetResultBatchesAsync>d__2 stateMachine = this; <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine); return; } break; } SearchResponse result = awaiter.GetResult(); <searchResults>5__4 = result; IEnumerator<SearchResponse.VideoData> enumerator = <searchResults>5__4.Videos.GetEnumerator(); try { while (enumerator.MoveNext()) { SearchResponse.VideoData current = enumerator.Current; if (searchFilter != 0 && searchFilter != SearchFilter.Video) { break; } string text2 = current.Id ?? throw new YoutubeExplodeException("Failed to extract the video ID."); if (<encounteredIds>5__2.Add(text2)) { string title = current.Title ?? throw new YoutubeExplodeException("Failed to extract the video title."); string channelTitle = current.Author ?? throw new YoutubeExplodeException("Failed to extract the video author."); string text3 = current.ChannelId ?? throw new YoutubeExplodeException("Failed to extract the video channel ID."); Thumbnail[] thumbnails = current.Thumbnails.Select(delegate(ThumbnailData t) { string? url3 = t.Url ?? throw new YoutubeExplodeException("Failed to extract the video thumbnail URL."); int width3 = t.Width ?? throw new YoutubeExplodeException("Failed to extract the video thumbnail width."); int height3 = t.Height ?? throw new YoutubeExplodeException("Failed to extract the video thumbnail height."); Resolution resolution3 = new Resolution(width3, height3); return new Thumbnail(url3, resolution3); }).Concat(Thumbnail.GetDefaultSet(text2)).ToArray(); VideoSearchResult item = new VideoSearchResult(text2, title, new Author(text3, channelTitle), current.Duration, thumbnails); <results>5__3.Add(item); } } } finally { if (num == -1) { enumerator?.Dispose(); } } if (!<>w__disposeMode) { IEnumerator<SearchResponse.PlaylistData> enumerator2 = <searchResults>5__4.Playlists.GetEnumerator(); try { while (enumerator2.MoveNext()) { SearchResponse.PlaylistData current2 = enumerator2.Current; if (searchFilter != 0 && searchFilter != SearchFilter.Playlist) { break; } string text4 = current2.Id ?? throw new YoutubeExplodeException("Failed to extract the playlist ID."); if (<encounteredIds>5__2.Add(text4)) { string title2 = current2.Title ?? throw new YoutubeExplodeException("Failed to extract the playlist title."); Author author = ((!string.IsNullOrWhiteSpace(current2.ChannelId) && !string.IsNullOrWhiteSpace(current2.Author)) ? new Author(current2.ChannelId, current2.Author) : null); Thumbnail[] thumbnails2 = current2.Thumbnails.Select(delegate(ThumbnailData t) { string? url2 = t.Url ?? throw new YoutubeExplodeException("Failed to extract the playlist thumbnail URL."); int width2 = t.Width ?? throw new YoutubeExplodeException("Failed to extract the playlist thumbnail width."); int height2 = t.Height ?? throw new YoutubeExplodeException("Failed to extract the playlist thumbnail height."); Resolution resolution2 = new Resolution(width2, height2); return new Thumbnail(url2, resolution2); }).ToArray(); PlaylistSearchResult item2 = new PlaylistSearchResult(text4, title2, author, thumbnails2); <results>5__3.Add(item2); } } } finally { if (num == -1) { enumerator2?.Dispose(); } } if (!<>w__disposeMode) { IEnumerator<SearchResponse.ChannelData> enumerator3 = <searchResults>5__4.Channels.GetEnumerator(); try { while (enumerator3.MoveNext()) { SearchResponse.ChannelData current3 = enumerator3.Current; if (searchFilter == SearchFilter.None || searchFilter == SearchFilter.Channel) { string text5 = current3.Id ?? throw new YoutubeExplodeException("Failed to extract the channel ID."); string title3 = current3.Title ?? throw new YoutubeExplodeException("Failed to extract the channel title."); Thumbnail[] thumbnails3 = current3.Thumbnails.Select(delegate(ThumbnailData t) { string? url = t.Url ?? throw new YoutubeExplodeException("Failed to extract the channel thumbnail URL."); int width = t.Width ?? throw new YoutubeExplodeException("Failed to extract the channel thumbnail width."); int height = t.Height ?? throw new YoutubeExplodeException("Failed to extract the channel thumbnail height."); Resolution resolution = new Resolution(width, height); return new Thumbnail(url, resolution); }).ToArray(); ChannelSearchResult item3 = new ChannelSearchResult(text5, title3, thumbnails3); <results>5__3.Add(item3); continue; } break; } } finally { if (num == -1) { enumerator3?.Dispose(); } } if (!<>w__disposeMode) { <>2__current = Batch.Create(<results>5__3); num = (<>1__state = -4); goto IL_054d; } } } end_IL_000e:; } catch (Exception exception) { <>1__state = -2; <encounteredIds>5__2 = null; <results>5__3 = null; <searchResults>5__4 = null; if (<>x__combinedTokens != null) { <>x__combinedTokens.Dispose(); <>x__combinedTokens = null; } <>2__current = null; <>t__builder.Complete(); <>v__promiseOfValueOrEnd.SetException(exception); return; } <>1__state = -2; <encounteredIds>5__2 = null; <results>5__3 = null; <searchResults>5__4 = null; if (<>x__combinedTokens != null) { <>x__combinedTokens.Dispose(); <>x__combinedTokens = null; } <>2__current = null; <>t__builder.Complete(); <>v__promiseOfValueOrEnd.SetResult(result: false); return; IL_054d: <>v__promiseOfValueOrEnd.SetResult(result: true); } void IAsyncStateMachine.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext this.MoveNext(); } [DebuggerHidden] private void SetStateMachine(IAsyncStateMachine stateMachine) { } void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine) { //ILSpy generated this explicit interface implementation from .override directive in SetStateMachine this.SetStateMachine(stateMachine); } [DebuggerHidden] IAsyncEnumerator<Batch<ISearchResult>> IAsyncEnumerable<Batch<ISearchResult>>.GetAsyncEnumerator(CancellationToken cancellationToken = default(CancellationToken)) { <GetResultBatchesAsync>d__2 <GetResultBatchesAsync>d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = -3; <>t__builder = AsyncIteratorMethodBuilder.Create(); <>w__disposeMode = false; <GetResultBatchesAsync>d__ = this; } else { <GetResultBatchesAsync>d__ = new <GetResultBatchesAsync>d__2(-3) { <>4__this = <>4__this }; } <GetResultBatchesAsync>d__.searchQuery = <>3__searchQuery; <GetResultBatchesAsync>d__.searchFilter = <>3__searchFilter; if (<>3__cancellationToken.Equals(default(CancellationToken))) { <GetResultBatchesAsync>d__.cancellationToken = cancellationToken; } else if (cancellationToken.Equals(<>3__cancellationToken) || cancellationToken.Equals(default(CancellationToken))) { <GetResultBatchesAsync>d__.cancellationToken = <>3__cancellationToken; } else { <>x__combinedTokens = CancellationTokenSource.CreateLinkedTokenSource(<>3__cancellationToken, cancellationToken); <GetResultBatchesAsync>d__.cancellationToken = <>x__combinedTokens.Token; } return <GetResultBatchesAsync>d__; } [DebuggerHidden] ValueTask<bool> IAsyncEnumerator<Batch<ISearchResult>>.MoveNextAsync() { if (<>1__state == -2) { return default(ValueTask<bool>); } <>v__promiseOfValueOrEnd.Reset(); <GetResultBatchesAsync>d__2 stateMachine = this; <>t__builder.MoveNext(ref stateMachine); short version = <>v__promiseOfValueOrEnd.Version; if (<>v__promiseOfValueOrEnd.GetStatus(version) == ValueTaskSourceStatus.Succeeded) { return new ValueTask<bool>(<>v__promiseOfValueOrEnd.GetResult(version)); } return new ValueTask<bool>(this, version); } [DebuggerHidden] bool IValueTaskSource<bool>.GetResult(short token) { return <>v__promiseOfValueOrEnd.GetResult(token); } [DebuggerHidden] ValueTaskSourceStatus IValueTaskSource<bool>.GetStatus(short token) { return <>v__promiseOfValueOrEnd.GetStatus(token); } [DebuggerHidden] void IValueTaskSource<bool>.OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags) { <>v__promiseOfValueOrEnd.OnCompleted(continuation, state, token, flags); } [DebuggerHidden] void IValueTaskSource.GetResult(short token) { <>v__promiseOfValueOrEnd.GetResult(token); } [DebuggerHidden] ValueTaskSourceStatus IValueTaskSource.GetStatus(short token) { return <>v__promiseOfValueOrEnd.GetStatus(token); } [DebuggerHidden] void IValueTaskSource.OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags) { <>v__promiseOfValueOrEnd.OnCompleted(continuation, state, token, flags); } [DebuggerHidden] ValueTask IAsyncDisposable.DisposeAsync() { if (<>1__state >= -1) { throw new NotSupportedException(); } if (<>1__state == -2) { return default(ValueTask); } <>w__disposeMode = true; <>v__promiseOfValueOrEnd.Reset(); <GetResultBatchesAsync>d__2 stateMachine = this; <>t__builder.MoveNext(ref stateMachine); return new ValueTask(this, <>v__promiseOfValueOrEnd.Version); } } private readonly SearchController _controller = new SearchController(http); public SearchClient(HttpClient http) { } [AsyncIteratorStateMachine(typeof(<GetResultBatchesAsync>d__2))] public IAsyncEnumerable<Batch<ISearchResult>> GetResultBatchesAsync(string searchQuery, SearchFilter searchFilter, [EnumeratorCancellation] CancellationToken cancellationToken = default(CancellationToken)) { return new <GetResultBatchesAsync>d__2(-2) { <>4__this = this, <>3__searchQuery = searchQuery, <>3__searchFilter = searchFilter, <>3__cancellationToken = cancellationToken }; } public IAsyncEnumerable<Batch<ISearchResult>> GetResultBatchesAsync(string searchQuery, CancellationToken cancellationToken = default(CancellationToken)) { return GetResultBatchesAsync(searchQuery, SearchFilter.None, cancellationToken); } public IAsyncEnumerable<ISearchResult> GetResultsAsync(string searchQuery, CancellationToken cancellationToken = default(CancellationToken)) { return GetResultBatchesAsync(searchQuery, cancellationToken).FlattenAsync(); } public IAsyncEnumerable<VideoSearchResult> GetVideosAsync(string searchQuery, CancellationToken cancellationToken = default(CancellationToken)) { return GetResultBatchesAsync(searchQuery, SearchFilter.Video, cancellationToken).FlattenAsync().OfTypeAsync<VideoSearchResult>(); } public IAsyncEnumerable<PlaylistSearchResult> GetPlaylistsAsync(string searchQuery, CancellationToken cancellationToken = default(CancellationToken)) { return GetResultBatchesAsync(searchQuery, SearchFilter.Playlist, cancellationToken).FlattenAsync().OfTypeAsync<PlaylistSearchResult>(); } public IAsyncEnumerable<ChannelSearchResult> GetChannelsAsync(string searchQuery, CancellationToken cancellationToken = default(CancellationToken)) { return GetResultBatchesAsync(searchQuery, SearchFilter.Channel, cancellationToken).FlattenAsync().OfTypeAsync<ChannelSearchResult>(); } } internal class SearchController { [CompilerGenerated] private HttpClient <http>P; public SearchController(HttpClient http) { <http>P = http; base..ctor(); } public async ValueTask<SearchResponse> GetSearchResponseAsync(string searchQuery, SearchFilter searchFilter, string? continuationToken, CancellationToken cancellationToken = default(CancellationToken)) { using HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "https://www.youtube.com/youtubei/v1/search"); HttpRequestMessage httpRequestMessage = request; string text = Json.Serialize(searchQuery); string value = searchFilter switch { SearchFilter.Video => "EgIQAQ%3D%3D", SearchFilter.Playlist => "EgIQAw%3D%3D", SearchFilter.Channel => "EgIQAg%3D%3D", _ => null, }; httpRequestMessage.Content = new StringContent("{\n \"query\": " + text + ",\n \"params\": " + Json.Serialize(value) + ",\n \"continuation\": " + Json.Serialize(continuationToken) + ",\n \"context\": {\n \"client\": {\n \"clientName\": \"WEB\",\n \"clientVersion\": \"2.20210408.08.00\",\n \"hl\": \"en\",\n \"gl\": \"US\",\n \"utcOffsetMinutes\": 0\n }\n }\n}"); using HttpResponseMessage response = await <http>P.SendAsync(request, cancellationToken); response.EnsureSuccessStatusCode(); return SearchResponse.Parse(await PolyfillExtensions.ReadAsStringAsync(response.Content, cancellationToken)); } } public enum SearchFilter { None, Video, Playlist, Channel } public class VideoSearchResult : ISearchResult, IBatchItem, IVideo { public VideoId Id { get; } public string Url => $"https://www.youtube.com/watch?v={Id}"; public string Title { get; } public Author Author { get; } public TimeSpan? Duration { get; } public IReadOnlyList<Thumbnail> Thumbnails { get; } public VideoSearchResult(VideoId id, string title, Author author, TimeSpan? duration, IReadOnlyList<Thumbnail> thumbnails) { Id = id; Title = title; Author = author; Duration = duration; Thumbnails = thumbnails; base..ctor(); } [ExcludeFromCodeCoverage] public override string ToString() { return "Video (" + Title + ")"; } } } namespace YoutubeExplode.Playlists { public interface IPlaylist { PlaylistId Id { get; } string Url { get; } string Title { get; } Author? Author { get; } IReadOnlyList<Thumbnail> Thumbnails { get; } } public class Playlist : IPlaylist { public PlaylistId Id { get; } public string Url => $"https://www.youtube.com/playlist?list={Id}"; public string Title { get; } public Author? Author { get; } public string Description { get; } public int? Count { get; } public IReadOnlyList<Thumbnail> Thumbnails { get; } public Playlist(PlaylistId id, string title, Author? author, string description, int? count, IReadOnlyList<Thumbnail> thumbnails) { Id = id; Title = title; Author = author; Description = description; Count = count; Thumbnails = thumbnails; base..ctor(); } [ExcludeFromCodeCoverage] public override string ToString() { return "Playlist (" + Title + ")"; } } public class PlaylistClient { [CompilerGenerated] private sealed class <GetVideoBatchesAsync>d__3 : IAsyncEnumerable<Batch<PlaylistVideo>>, IAsyncEnumerator<Batch<PlaylistVideo>>, IAsyncDisposable, IValueTaskSource<bool>, IValueTaskSource, IAsyncStateMachine { public int <>1__state; public AsyncIteratorMethodBuilder <>t__builder; public ManualResetValueTaskSourceCore<bool> <>v__promiseOfValueOrEnd; private Batch<PlaylistVideo> <>2__current; private bool <>w__disposeMode; private CancellationTokenSource <>x__combinedTokens; private int <>l__initialThreadId; public PlaylistClient <>4__this; private PlaylistId playlistId; public PlaylistId <>3__playlistId; private CancellationToken cancellationToken; public CancellationToken <>3__cancellationToken; private HashSet<VideoId> <encounteredIds>5__2; private VideoId? <lastVideoId>5__3; private int <lastVideoIndex>5__4; private string <visitorData>5__5; private PlaylistNextResponse <response>5__6; private ValueTaskAwaiter<PlaylistNextResponse> <>u__1; Batch<PlaylistVideo> IAsyncEnumerator<Batch<PlaylistVideo>>.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <GetVideoBatchesAsync>d__3(int <>1__state) { <>t__builder = AsyncIteratorMethodBuilder.Create(); this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } private void MoveNext() { int num = <>1__state; PlaylistClient playlistClient = <>4__this; try { ValueTaskAwaiter<PlaylistNextResponse> awaiter; switch (num) { default: if (!<>w__disposeMode) { num = (<>1__state = -1); <encounteredIds>5__2 = new HashSet<VideoId>(); <lastVideoId>5__3 = null; <lastVideoIndex>5__4 = 0; <visitorData>5__5 = null; goto IL_0066; } goto end_IL_000e; case 0: awaiter = <>u__1; <>u__1 = default(ValueTaskAwaiter<PlaylistNextResponse>); num = (<>1__state = -1); break; case -4: { num = (<>1__state = -1); if (!<>w__disposeMode) { if (<visitorData>5__5 == null) { <visitorData>5__5 = <response>5__6.VisitorData; } <response>5__6 = null; goto IL_0066; } goto end_IL_000e; } IL_0066: <>2__current = null; awaiter = playlistClient._controller.GetPlaylistNextResponseAsync(playlistId, <lastVideoId>5__3, <lastVideoIndex>5__4, <visitorData>5__5, cancellationToken).GetAwaiter(); if (!awaiter.IsCompleted) { num = (<>1__state = 0); <>u__1 = awaiter; <GetVideoBatchesAsync>d__3 stateMachine = this; <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine); return; } break; } PlaylistNextResponse result = awaiter.GetResult(); <response>5__6 = result; List<PlaylistVideo> list = new List<PlaylistVideo>(); IEnumerator<PlaylistVideoData> enumerator = <response>5__6.Videos.GetEnumerator(); try { while (enumerator.MoveNext()) { PlaylistVideoData current = enumerator.Current; string text = current.Id ?? throw new YoutubeExplodeException("Failed to extract the video ID."); <lastVideoId>5__3 = text; <lastVideoIndex>5__4 = current.Index ?? throw new YoutubeExplodeException("Failed to extract the video index."); if (<encounteredIds>5__2.Add(text)) { string title = current.Title ?? ""; string channelTitle = current.Author ?? throw new YoutubeExplodeException("Failed to extract the video author."); string text2 = current.ChannelId ?? throw new YoutubeExplodeException("Failed to extract the video channel ID."); Thumbnail[] thumbnails = current.Thumbnails.Select(delegate(ThumbnailData t) { string? url = t.Url ?? throw new YoutubeExplodeException("Failed to extract the thumbnail URL."); int width = t.Width ?? throw new YoutubeExplodeException("Failed to extract the thumbnail width."); int height = t.Height ?? throw new YoutubeExplodeException("Failed to extract the thumbnail height."); Resolution resolution = new Resolution(width, height); return new Thumbnail(url, resolution); }).Concat(Thumbnail.GetDefaultSet(text)).ToArray(); PlaylistVideo item = new PlaylistVideo(playlistId, text, title, new Author(text2, channelTitle), current.Duration, thumbnails); list.Add(item); } } } finally { if (num == -1) { enumerator?.Dispose(); } } if (!<>w__disposeMode && list.Any()) { <>2__current = Batch.Create(list); num = (<>1__state = -4); goto IL_0384; } end_IL_000e:; } catch (Exception exception) { <>1__state = -2; <encounteredIds>5__2 = null; <visitorData>5__5 = null; <response>5__6 = null; if (<>x__combinedTokens != null) { <>x__combinedTokens.Dispose(); <>x__combinedTokens = null; } <>2__current = null; <>t__builder.Complete(); <>v__promiseOfValueOrEnd.SetException(exception); return; } <>1__state = -2; <encounteredIds>5__2 = null; <visitorData>5__5 = null; <response>5__6 = null; if (<>x__combinedTokens != null) { <>x__combinedTokens.Dispose(); <>x__combinedTokens = null; } <>2__current = null; <>t__builder.Complete(); <>v__promiseOfValueOrEnd.SetResult(result: false); return; IL_0384: <>v__promiseOfValueOrEnd.SetResult(result: true); } void IAsyncStateMachine.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext this.MoveNext(); } [DebuggerHidden] private void SetStateMachine(IAsyncStateMachine stateMachine) { } void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine) { //ILSpy generated this explicit interface implementation from .override directive in SetStateMachine this.SetStateMachine(stateMachine); } [DebuggerHidden] IAsyncEnumerator<Batch<PlaylistVideo>> IAsyncEnumerable<Batch<PlaylistVideo>>.GetAsyncEnumerator(CancellationToken cancellationToken = default(CancellationToken)) { <GetVideoBatchesAsync>d__3 <GetVideoBatchesAsync>d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = -3; <>t__builder = AsyncIteratorMethodBuilder.Create(); <>w__disposeMode = false; <GetVideoBatchesAsync>d__ = this; } else { <GetVideoBatchesAsync>d__ = new <GetVideoBatchesAsync>d__3(-3) { <>4__this = <>4__this }; } <GetVideoBatchesAsync>d__.playlistId = <>3__playlistId; if (<>3__cancellationToken.Equals(default(CancellationToken))) { <GetVideoBatchesAsync>d__.cancellationToken = cancellationToken; } else if (cancellationToken.Equals(<>3__cancellationToken) || cancellationToken.Equals(default(CancellationToken))) { <GetVideoBatchesAsync>d__.cancellationToken = <>3__cancellationToken; } else { <>x__combinedTokens = CancellationTokenSource.CreateLinkedTokenSource(<>3__cancellationToken, cancellationToken); <GetVideoBatchesAsync>d__.cancellationToken = <>x__combinedTokens.Token; } return <GetVideoBatchesAsync>d__; } [DebuggerHidden] ValueTask<bool> IAsyncEnumerator<Batch<PlaylistVideo>>.MoveNextAsync() { if (<>1__state == -2) { return default(ValueTask<bool>); } <>v__promiseOfValueOrEnd.Reset(); <GetVideoBatchesAsync>d__3 stateMachine = this; <>t__builder.MoveNext(ref stateMachine); short version = <>v__promiseOfValueOrEnd.Version; if (<>v__promiseOfValueOrEnd.GetStatus(version) == ValueTaskSourceStatus.Succeeded) { return new ValueTask<bool>(<>v__promiseOfValueOrEnd.GetResult(version)); } return new ValueTask<bool>(this, version); } [DebuggerHidden] bool IValueTaskSource<bool>.GetResult(short token) { return <>v__promiseOfValueOrEnd.GetResult(token); } [DebuggerHidden] ValueTaskSourceStatus IValueTaskSource<bool>.GetStatus(short token) { return <>v__promiseOfValueOrEnd.GetStatus(token); } [DebuggerHidden] void IValueTaskSource<bool>.OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags) { <>v__promiseOfValueOrEnd.OnCompleted(continuation, state, token, flags); } [DebuggerHidden] void IValueTaskSource.GetResult(short token) { <>v__promiseOfValueOrEnd.GetResult(token); } [DebuggerHidden] ValueTaskSourceStatus IValueTaskSource.GetStatus(short token) { return <>v__promiseOfValueOrEnd.GetStatus(token); } [DebuggerHidden] void IValueTaskSource.OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags) { <>v__promiseOfValueOrEnd.OnCompleted(continuation, state, token, flags); } [DebuggerHidden] ValueTask IAsyncDisposable.DisposeAsync() { if (<>1__state >= -1) { throw new NotSupportedException(); } if (<>1__state == -2) { return default(ValueTask); } <>w__disposeMode = true; <>v__promiseOfValueOrEnd.Reset(); <GetVideoBatchesAsync>d__3 stateMachine = this; <>t__builder.MoveNext(ref stateMachine); return new ValueTask(this, <>v__promiseOfValueOrEnd.Version); } } private readonly PlaylistController _controller = new PlaylistController(http); public PlaylistClient(HttpClient http) { } public async ValueTask<Playlist> GetAsync(PlaylistId playlistId, CancellationToken cancellationToken = default(CancellationToken)) { IPlaylistData obj = await _controller.GetPlaylistResponseAsync(playlistId, cancellationToken); string title = obj.Title ?? throw new YoutubeExplodeException("Failed to extract the playlist title."); string channelId = obj.ChannelId; string author = obj.Author; Author author2 = ((channelId != null && author != null) ? new Author(channelId, author) : null); string description = obj.Description ?? ""; int? count = obj.Count; Thumbnail[] thumbnails = obj.Thumbnails.Select(delegate(ThumbnailData t) { string? url = t.Url ?? throw new YoutubeExplodeException("Failed to extract the thumbnail URL."); int width = t.Width ?? throw new YoutubeExplodeException("Failed to extract the thumbnail width."); int height = t.Height ?? throw new YoutubeExplodeException("Failed to extract the thumbnail height."); Resolution resolution = new Resolution(width, height); return new Thumbnail(url, resolution); }).ToArray(); return new Playlist(playlistId, title, author2, description, count, thumbnails); } [AsyncIteratorStateMachine(typeof(<GetVideoBatchesAsync>d__3))] public IAsyncEnumerable<Batch<PlaylistVideo>> GetVideoBatchesAsync(PlaylistId playlistId, [EnumeratorCancellation] CancellationToken cancellationToken = default(CancellationToken)) { return new <GetVideoBatchesAsync>d__3(-2) { <>4__this = this, <>3__playlistId = playlistId, <>3__cancellationToken = cancellationToken }; } public IAsyncEnumerable<PlaylistVideo> GetVideosAsync(PlaylistId playlistId, CancellationToken cancellationToken = default(CancellationToken)) { return GetVideoBatchesAsync(playlistId, cancellationToken).FlattenAsync(); } } internal class PlaylistController { [CompilerGenerated] private HttpClient <http>P; public PlaylistController(HttpClient http) { <http>P = http; base..ctor(); } public async ValueTask<PlaylistBrowseResponse> GetPlaylistBrowseResponseAsync(PlaylistId playlistId, CancellationToken cancellationToken = default(CancellationToken)) { using HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "https://www.youtube.com/youtubei/v1/browse"); request.Content = new StringContent("{\n \"browseId\": " + Json.Serialize("VL" + playlistId) + ",\n \"context\": {\n \"client\": {\n \"clientName\": \"WEB\",\n \"clientVersion\": \"2.20210408.08.00\",\n \"hl\": \"en\",\n \"gl\": \"US\",\n \"utcOffsetMinutes\": 0\n }\n }\n}"); using HttpResponseMessage response = await <http>P.SendAsync(request, cancellationToken); response.EnsureSuccessStatusCode(); PlaylistBrowseResponse playlistBrowseResponse = PlaylistBrowseResponse.Parse(await PolyfillExtensions.ReadAsStringAsync(response.Content, cancellationToken)); if (!playlistBrowseResponse.IsAvailable) { throw new PlaylistUnavailableException($"Playlist '{playlistId}' is not available."); } return playlistBrowseResponse; } public async ValueTask<PlaylistNextResponse> GetPlaylistNextResponseAsync(PlaylistId playlistId, VideoId? videoId = null, int index = 0, string? visitorData = null, CancellationToken cancellationToken = default(CancellationToken)) { int retriesRemaining = 5; while (true) { using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "https://www.youtube.com/youtubei/v1/next")) { string[] obj = new string[9] { "{\n \"playlistId\": ", Json.Serialize(playlistId), ",\n \"videoId\": ", null, null, null, null, null, null }; VideoId? videoId2 = videoId; obj[3] = Json.Serialize(videoId2.HasValue ? ((string)videoId2.GetValueOrDefault()) : null); obj[4] = ",\n \"playlistIndex\": "; obj[5] = Json.Serialize(index); obj[6] = ",\n \"context\": {\n \"client\": {\n \"clientName\": \"WEB\",\n \"clientVersion\": \"2.20210408.08.00\",\n \"hl\": \"en\",\n \"gl\": \"US\",\n \"utcOffsetMinutes\": 0,\n \"visitorData\": "; obj[7] = Json.Serialize(visitorData); obj[8] = "\n }\n }\n}"; request.Content = new StringContent(string.Concat(obj)); using HttpResponseMessage response = await <http>P.SendAsync(request, cancellationToken); response.EnsureSuccessStatusCode(); PlaylistNextResponse playlistNextResponse = PlaylistNextResponse.Parse(await PolyfillExtensions.ReadAsStringAsync(response.Content, cancellationToken)); if (playlistNextResponse.IsAvailable) { return playlistNextResponse; } if (index <= 0 || string.IsNullOrWhiteSpace(visitorData) || retriesRemaining <= 0) { if (index > 0 || !string.IsNullOrWhiteSpace(visitorData) || retriesRemaining < 5) { throw new PlaylistUnavailableException($"Playlist '{playlistId}' is not available."); } using (await <http>P.GetAsync($"https://youtube.com/playlist?list={playlistId}", cancellationToken)) { } } } retriesRemaining--; } } public async ValueTask<IPlaylistData> GetPlaylistResponseAsync(PlaylistId playlistId, CancellationToken cancellationToken = default(CancellationToken)) { IPlaylistData result = default(IPlaylistData); int num; try { result = await GetPlaylistBrowseResponseAsync(playlistId, cancellationToken); return result; } catch (PlaylistUnavailableException) { num = 1; } if (num != 1) { return result; } return await GetPlaylistNextResponseAsync(playlistId, null, 0, null, cancellationToken); } } public readonly struct PlaylistId : IEquatable<PlaylistId> { public string Value { get; } public PlaylistId(string value) { Value = value; } public override string ToString() { return Value; } private static bool IsValid(string playlistId) { if (playlistId.Length >= 2) { return playlistId.All(delegate(char c) { bool flag = char.IsLetterOrDigit(c); if (!flag) { bool flag2 = ((c == '-' || c == '_') ? true : false); flag = flag2; } return flag; }); } return false; } private static string? TryNormalize(string? playlistIdOrUrl) { if (string.IsNullOrWhiteSpace(playlistIdOrUrl)) { return null; } if (IsValid(playlistIdOrUrl)) { return playlistIdOrUrl; } string text = Regex.Match(playlistIdOrUrl, "youtube\\..+?/playlist.*?list=(.*?)(?:&|/|$)").Groups[1].Value.Pipe(WebUtility.UrlDecode); if (!string.IsNullOrWhiteSpace(text) && IsValid(text)) { return text; } string text2 = Regex.Match(playlistIdOrUrl, "youtube\\..+?/watch.*?list=(.*?)(?:&|/|$)").Groups[1].Value.Pipe(WebUtility.UrlDecode); if (!string.IsNullOrWhiteSpace(text2) && IsValid(text2)) { return text2; } string text3 = Regex.Match(playlistIdOrUrl, "youtu\\.be/.*?/.*?list=(.*?)(?:&|/|$)").Groups[1].Value.Pipe(WebUtility.UrlDecode); if (!string.IsNullOrWhiteSpace(text3) && IsValid(text3)) { return text3; } string text4 = Regex.Match(playlistIdOrUrl, "youtube\\..+?/embed/.*?/.*?list=(.*?)(?:&|/|$)").Groups[1].Value.Pipe(WebUtility.UrlDecode); if (!string.IsNullOrWhiteSpace(text4) && IsValid(text4)) { return text4; } return null; } public static PlaylistId? TryParse(string? playlistIdOrUrl) { return TryNormalize(playlistIdOrUrl)?.Pipe((string id) => new PlaylistId(id)); } public static PlaylistId Parse(string playlistIdOrUrl) { return TryParse(playlistIdOrUrl) ?? throw new ArgumentException("Invalid YouTube playlist ID or URL '" + playlistIdOrUrl + "'."); } public static implicit operator PlaylistId(string playlistIdOrUrl) { return Parse(playlistIdOrUrl); } public static implicit operator string(PlaylistId playlistId) { return playlistId.ToString(); } public bool Equals(PlaylistId other) { return StringComparer.Ordinal.Equals(Value, other.Value); } public override bool Equals(object? obj) { if (obj is PlaylistId other) { return Equals(other); } return false; } public override int GetHashCode() { return StringComparer.Ordinal.GetHashCode(Value); } public static bool operator ==(PlaylistId left, PlaylistId right) { return left.Equals(right); } public static bool operator !=(PlaylistId left, PlaylistId right) { return !(left == right); } } public class PlaylistVideo : IVideo, IBatchItem { public PlaylistId PlaylistId { get; } public VideoId Id { get; } public string Url => $"https://www.youtube.com/watch?v={Id}&list={PlaylistId}"; public string Title { get; } public Author Author { get; } public TimeSpan? Duration { get; } public IReadOnlyList<Thumbnail> Thumbnails { get; } public PlaylistVideo(PlaylistId playlistId, VideoId id, string title, Author author, TimeSpan? duration, IReadOnlyList<Thumbnail> thumbnails) { PlaylistId = playlistId; Id = id; Title = title; Author = author; Duration = duration; Thumbnails = thumbnails; base..ctor(); } [Obsolete("Use the other constructor instead.")] [ExcludeFromCodeCoverage] public PlaylistVideo(VideoId id, string title, Author author, TimeSpan? duration, IReadOnlyList<Thumbnail> thumbnails) : this(default(PlaylistId), id, title, author, duration, thumbnails) { } [ExcludeFromCodeCoverage] public override string ToString() { return "Video (" + Title + ")"; } } } namespace YoutubeExplode.Exceptions { public class PlaylistUnavailableException : YoutubeExplodeException { public PlaylistUnavailableException(string message) : base(message) { } } public class RequestLimitExceededException : YoutubeExplodeException { public RequestLimitExceededException(string message) : base(message) { } } public class VideoRequiresPurchaseException : VideoUnplayableException { public VideoId PreviewVideoId { get; } public VideoRequiresPurchaseException(string message, VideoId previewVideoId) { PreviewVideoId = previewVideoId; base..ctor(message); } } public class VideoUnavailableException : VideoUnplayableException { public VideoUnavailableException(string message) : base(message) { } } public class VideoUnplayableException : YoutubeExplodeException { public VideoUnplayableException(string message) : base(message) { } } public class YoutubeExplodeException : Exception { public YoutubeExplodeException(string message) : base(message) { } } } namespace YoutubeExplode.Videos { public class Engagement { public long ViewCount { get; } public long LikeCount { get; } public long DislikeCount { get; } public double AverageRating { get { if (LikeCount + DislikeCount == 0L) { return 0.0; } return 1.0 + 4.0 * (double)LikeCount / (double)(LikeCount + DislikeCount); } } public Engagement(long viewCount, long likeCount, long dislikeCount) { ViewCount = viewCount; LikeCount = likeCount; DislikeCount = dislikeCount; base..ctor(); } [ExcludeFromCodeCoverage] public override string ToString() { return $"Rating: {AverageRating:N1}"; } } public interface IVideo { VideoId Id { get; } string Url { get; } string Title { get; } Author Author { get; } TimeSpan? Duration { get; } IReadOnlyList<Thumbnail> Thumbnails { get; } } public class Video : IVideo { public VideoId Id { get; } public string Url => $"https://www.youtube.com/watch?v={Id}"; public string Title { get; } public Author Author { get; } public DateTimeOffset UploadDate { get; } public string Description { get; } public TimeSpan? Duration { get; } public IReadOnlyList<Thumbnail> Thumbnails { get; } public IReadOnlyList<string> Keywords { get; } public Engagement Engagement { get; } public Video(VideoId id, string title, Author author, DateTimeOffset uploadDate, string description, TimeSpan? duration, IReadOnlyList<Thumbnail> thumbnails, IReadOnlyList<string> keywords, Engagement engagement) { Id = id; Title = title; Author = author; UploadDate = uploadDate; Description = description; Duration = duration; Thumbnails = thumbnails; Keywords = keywords; Engagement = engagement; base..ctor(); } [ExcludeFromCodeCoverage] public override string ToString() { return "Video (" + Title + ")"; } } public class VideoClient { private readonly VideoController _controller = new VideoController(http); public StreamClient Streams { get; } = new StreamClient(http); public ClosedCaptionClient ClosedCaptions { get; } = new ClosedCaptionClient(http); public VideoClient(HttpClient http) { } public async ValueTask<Video> GetAsync(VideoId videoId, CancellationToken cancellationToken = default(CancellationToken)) { VideoWatchPage watchPage = await _controller.GetVideoWatchPageAsync(videoId, cancellationToken); PlayerResponse playerResponse = watchPage.PlayerResponse; if (playerResponse == null) { playerResponse = await _controller.GetPlayerResponseAsync(videoId, cancellationToken); } PlayerResponse playerResponse2 = playerResponse; string title = playerResponse2.Title ?? ""; string channelTitle = playerResponse2.Author ?? throw new YoutubeExplodeException("Failed to extract the video author."); string text = playerResponse2.ChannelId ?? throw new YoutubeExplodeException("Failed to extract the video channel ID."); DateTimeOffset uploadDate = playerResponse2.UploadDate ?? watchPage.UploadDate ?? throw new YoutubeExplodeException("Failed to extract the video upload date."); Thumbnail[] thumbnails = playerResponse2.Thumbnails.Select(delegate(ThumbnailData t) { string? url = t.Url ?? throw new YoutubeExplodeException("Failed to extract the thumbnail URL."); int width = t.Width ?? throw new YoutubeExplodeException("Failed to extract the thumbnail width."); int height = t.Height ?? throw new YoutubeExplodeException("Failed to extract the thumbnail height."); Resolution resolution = new Resolution(width, height); return new Thumbnail(url, resolution); }).Concat(Thumbnail.GetDefaultSet(videoId)).ToArray(); return new Video(videoId, title, new Author(text, channelTitle), uploadDate, playerResponse2.Description ?? "", playerResponse2.Duration, thumbnails, playerResponse2.Keywords, new Engagement(playerResponse2.ViewCount.GetValueOrDefault(), watchPage.LikeCount.GetValueOrDefault(), watchPage.DislikeCount.GetValueOrDefault())); } } internal class VideoController { private string? _visitorData; protected HttpClient Http { get; } public VideoController(HttpClient http) { Http = http; base..ctor(); } private async ValueTask<string> ResolveVisitorDataAsync(CancellationToken cancellationToken = default(CancellationToken)) { if (!string.IsNullOrWhiteSpace(_visitorData))