Please disclose if your mod was created primarily using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of YoutubeDownloader v0.0.4
YoutubeDownloader.dll
Decompiled 2 years agousing System; using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Net; using System.Net.Http; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using BestestTVModPlugin; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using YoutubeDLSharp; using YoutubeDLSharp.Metadata; using YoutubeDLSharp.Options; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: AssemblyCompany("YoutubeDownloader")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+0e14460f7cfc4423db0502c178e3415750870e39")] [assembly: AssemblyProduct("YoutubeDownloader")] [assembly: AssemblyTitle("YoutubeDownloader")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [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; } } } namespace YoutubeDownloader { [BepInPlugin("DeathWrench.YoutubeDownloader", "\u200bYoutubeDownloader", "0.0.4")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { private ConfigEntry<bool> playlistModeActivated; private ConfigEntry<bool> skipDownloadEnabled; private ConfigEntry<bool> deleteTelevisionVideos; private ConfigEntry<Key> downloadKeyBind; private ConfigEntry<string> defaultVideoUrl; private ConfigEntry<int> startOfPlaylist; private ConfigEntry<int> endOfPlaylist; public static ManualLogSource Log = new ManualLogSource("YoutubeDownloader"); private static readonly Harmony Harmony = new Harmony("DeathWrench.YoutubeDownloader"); private YoutubeDL ytdl; private InputAction downloadAction; private static string pluginPath = Paths.PluginPath + Path.DirectorySeparatorChar + "DeathWrench-YoutubeDownloader"; private static string libraryPath = Path.Combine(pluginPath, "lib"); private string ytDlpPath = Path.Combine(libraryPath, "yt-dlp.exe"); private string televisionVideosPath = Path.Combine(pluginPath, "Television Videos"); private async void Start() { Log.LogInfo((object)"Plugin DeathWrench.YoutubeDownloader is loaded!"); playlistModeActivated = ((BaseUnityPlugin)this).Config.Bind<bool>("Settings", "Playlist Mode", false, "Download playlists?"); skipDownloadEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Settings", "Skip Downloads", false, "Enable skipping downloads if the file already exists. This is ignored in Playlist Mode."); downloadKeyBind = ((BaseUnityPlugin)this).Config.Bind<Key>("Settings", "Download Keybind", (Key)6, "Which key to press to initiate YouTube video download."); defaultVideoUrl = ((BaseUnityPlugin)this).Config.Bind<string>("Settings", "Video URL", "https://www.youtube.com/watch?v=4Nty0riqSOs", "YouTube video URL for downloading."); startOfPlaylist = ((BaseUnityPlugin)this).Config.Bind<int>("Settings", "Start of Playlist", 1, "Select specific videos in a playlist to download. Setting to 5 will download the 5th video in the playlist."); endOfPlaylist = ((BaseUnityPlugin)this).Config.Bind<int>("Settings", "End of Playlist", 999999, "Which video to stop downloading at. Setting to 7 with start set to 5 will only download the 5th, 6th, and 7th videos."); deleteTelevisionVideos = ((BaseUnityPlugin)this).Config.Bind<bool>("Settings", "Delete Videos", false, "Delete the downloaded videos every time the game starts?"); if (!Directory.Exists(televisionVideosPath)) { Directory.CreateDirectory(televisionVideosPath); } if (deleteTelevisionVideos.Value) { string[] filesToDelete = Directory.GetFiles(televisionVideosPath, "*.mp4"); string[] array = filesToDelete; foreach (string fileToDelete in array) { File.Delete(fileToDelete); } } if (!Directory.Exists(libraryPath)) { Directory.CreateDirectory(libraryPath); } if (!File.Exists(libraryPath + "\\yt-dlp.exe")) { await DownloadLatestYtDlpRelease(libraryPath + "\\yt-dlp.exe"); } else { Log.LogInfo((object)"yt-dlp already exists. Skipping download."); } if (!File.Exists(libraryPath + "\\ffmpeg.exe")) { await DownloadAndExtractFFmpeg(libraryPath + "\\ffmpeg.exe"); } else { Log.LogInfo((object)"FFmpeg already exists. Skipping download."); } ytdl = new YoutubeDL((byte)4); ytdl.YoutubeDLPath = libraryPath + "\\yt-dlp.exe"; ytdl.FFmpegPath = libraryPath + "\\ffmpeg.exe"; Key downloadKey = downloadKeyBind.Value; downloadAction = new InputAction((string)null, (InputActionType)0, $"<Keyboard>/{downloadKey}", "press", (string)null, (string)null); downloadAction.Enable(); downloadAction.performed += async delegate(CallbackContext ctx) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) if (((CallbackContext)(ref ctx)).ReadValueAsButton()) { if (CheckDiskSpace(videoSize: await GetVideoSize(defaultVideoUrl.Value), path: televisionVideosPath)) { if (!playlistModeActivated.Value) { HUDManager.Instance.DisplayTip("Download in Progress", "Please wait while the video is being downloaded...", false, false, "DownloadProgressTip"); await DownloadVideo(defaultVideoUrl.Value, televisionVideosPath); } else { HUDManager.Instance.DisplayTip("Download in Progress", "Please wait while the playlist is being downloaded...", false, false, "DownloadProgressTip"); await DownloadPlaylist(defaultVideoUrl.Value, televisionVideosPath); } } else { HUDManager.Instance.DisplayTip("Download Error", "Not enough free disk space to download the video.", false, false, "DownloadErrorTip"); } } }; Harmony.PatchAll(); } private void Update() { if (CheckForConfigChanges()) { UpdateSettings(); } if (CheckForNewFiles()) { HandleNewFiles(); } if (Keyboard.current.ctrlKey.isPressed && ((ButtonControl)Keyboard.current.vKey).wasPressedThisFrame) { PasteTextFromClipboard(); } } private void PasteTextFromClipboard() { string systemCopyBuffer = GUIUtility.systemCopyBuffer; Debug.Log((object)("Pasting text from clipboard: " + systemCopyBuffer)); } private bool CheckForConfigChanges() { //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) if (playlistModeActivated.Value != ((BaseUnityPlugin)this).Config.Bind<bool>("Settings", "Playlist Mode", playlistModeActivated.Value, (ConfigDescription)null).Value || skipDownloadEnabled.Value != ((BaseUnityPlugin)this).Config.Bind<bool>("Settings", "Skip Downloads", skipDownloadEnabled.Value, (ConfigDescription)null).Value || downloadKeyBind.Value != ((BaseUnityPlugin)this).Config.Bind<Key>("Settings", "Download Keybind", downloadKeyBind.Value, (ConfigDescription)null).Value || defaultVideoUrl.Value != ((BaseUnityPlugin)this).Config.Bind<string>("Settings", "Video URL", defaultVideoUrl.Value, (ConfigDescription)null).Value || startOfPlaylist.Value != ((BaseUnityPlugin)this).Config.Bind<int>("Settings", "Start of Playlist", startOfPlaylist.Value, (ConfigDescription)null).Value || endOfPlaylist.Value != ((BaseUnityPlugin)this).Config.Bind<int>("Settings", "End of Playlist", endOfPlaylist.Value, (ConfigDescription)null).Value || deleteTelevisionVideos.Value != ((BaseUnityPlugin)this).Config.Bind<bool>("Settings", "Delete Videos", deleteTelevisionVideos.Value, (ConfigDescription)null).Value) { return true; } return false; } private void UpdateSettings() { //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) playlistModeActivated.Value = ((BaseUnityPlugin)this).Config.Bind<bool>("Settings", "Playlist Mode", playlistModeActivated.Value, (ConfigDescription)null).Value; skipDownloadEnabled.Value = ((BaseUnityPlugin)this).Config.Bind<bool>("Settings", "Skip Downloads", skipDownloadEnabled.Value, (ConfigDescription)null).Value; downloadKeyBind.Value = ((BaseUnityPlugin)this).Config.Bind<Key>("Settings", "Download Keybind", downloadKeyBind.Value, (ConfigDescription)null).Value; defaultVideoUrl.Value = ((BaseUnityPlugin)this).Config.Bind<string>("Settings", "Video URL", defaultVideoUrl.Value, (ConfigDescription)null).Value; startOfPlaylist.Value = ((BaseUnityPlugin)this).Config.Bind<int>("Settings", "Start of Playlist", startOfPlaylist.Value, (ConfigDescription)null).Value; endOfPlaylist.Value = ((BaseUnityPlugin)this).Config.Bind<int>("Settings", "End of Playlist", endOfPlaylist.Value, (ConfigDescription)null).Value; deleteTelevisionVideos.Value = ((BaseUnityPlugin)this).Config.Bind<bool>("Settings", "Delete Videos", deleteTelevisionVideos.Value, (ConfigDescription)null).Value; } private bool CheckForNewFiles() { string[] files = Directory.GetFiles(televisionVideosPath, "*.mp4"); if (files.Length > VideoManager.Videos.Count) { return true; } return false; } private void HandleNewFiles() { VideoManager.Videos.Clear(); VideoManager.Load(); if (ConfigManager.reloadedVideosHUD.Value) { HUDManager.Instance.DisplayTip("Reloaded Videos", "Video list has been reloaded.", false, false, "ReloadVideosTip"); } } private async Task DownloadLatestYtDlpRelease(string destinationFolder) { string latestReleaseUrl = "https://github.com/yt-dlp/yt-dlp/releases/latest"; HttpWebRequest request = (HttpWebRequest)WebRequest.Create(latestReleaseUrl); request.AllowAutoRedirect = true; try { using HttpWebResponse response = (HttpWebResponse)request.GetResponse(); string redirectedUrl = response.ResponseUri.AbsoluteUri; string releaseDateOrVersion = redirectedUrl.Split('/')[^1]; string downloadUrl = "https://github.com/yt-dlp/yt-dlp/releases/download/" + releaseDateOrVersion + "/yt-dlp.exe"; string downloadPath = ytDlpPath; using WebClient client = new WebClient(); client.DownloadFile(downloadUrl, downloadPath); Log.LogInfo((object)"Download completed successfully."); } catch (Exception ex2) { Exception ex = ex2; Log.LogError((object)("Error: " + ex.Message)); } } private async Task DownloadAndExtractFFmpeg(string ffmpegPath) { Log.LogInfo((object)"Downloading FFmpeg..."); string releaseUrl = "https://github.com/GyanD/codexffmpeg/releases/latest/"; HttpWebRequest request = (HttpWebRequest)WebRequest.Create(releaseUrl); request.AllowAutoRedirect = true; string actualReleaseUrl; try { using HttpWebResponse response = (HttpWebResponse)request.GetResponse(); actualReleaseUrl = response.ResponseUri.AbsoluteUri; } catch (Exception ex4) { Exception ex3 = ex4; Log.LogError((object)("Error getting release URL: " + ex3.Message)); return; } string releaseVersion = actualReleaseUrl.Split('/')[^1]; string ffmpegZipUrl = "https://github.com/GyanD/codexffmpeg/releases/download/" + releaseVersion + "/ffmpeg-" + releaseVersion + "-essentials_build.zip"; string tempZipPath = Path.Combine(pluginPath, "ffmpeg-" + releaseVersion + "-essentials_build.zip"); using (WebClient client = new WebClient()) { await client.DownloadFileTaskAsync(ffmpegZipUrl, tempZipPath); } try { ZipFile.ExtractToDirectory(tempZipPath, pluginPath); Log.LogInfo((object)"FFmpeg downloaded and extracted successfully."); string binDirectory = Path.Combine(pluginPath, "ffmpeg-" + releaseVersion + "-essentials_build", "bin"); string[] files = Directory.GetFiles(binDirectory); foreach (string filePath in files) { string destinationPath = Path.Combine(path2: Path.GetFileName(filePath), path1: libraryPath); File.Move(filePath, destinationPath); Log.LogInfo((object)("FFmpeg binary moved to: " + destinationPath)); } } catch (Exception ex2) { Log.LogError((object)("Failed to extract FFmpeg zip: " + ex2.Message)); } try { File.Delete(tempZipPath); Directory.Delete(Path.Combine(pluginPath, "ffmpeg-" + releaseVersion + "-essentials_build"), recursive: true); } catch (Exception ex) { Log.LogWarning((object)("Failed to delete temporary files: " + ex.Message)); } } private async Task<long> GetVideoSize(string videoUrl) { using HttpClient client = new HttpClient(); HttpRequestMessage headRequest = new HttpRequestMessage(HttpMethod.Head, videoUrl); HttpResponseMessage response = await client.SendAsync(headRequest); if (response.IsSuccessStatusCode && response.Content.Headers.ContentLength.HasValue) { long videoSize = response.Content.Headers.ContentLength.Value; Log.LogInfo((object)$"Video size obtained: {videoSize} bytes"); return videoSize; } Log.LogError((object)"Failed to obtain video size. Default size set to 0 bytes"); return 0L; } private bool CheckDiskSpace(string path, long videoSize) { DriveInfo driveInfo = new DriveInfo(Path.GetPathRoot(path)); long availableFreeSpace = driveInfo.AvailableFreeSpace; bool flag = availableFreeSpace >= videoSize; if (!flag) { Log.LogWarning((object)"Not enough free disk space to download the video."); Log.LogWarning((object)$"Required space: {videoSize} bytes, Available space: {availableFreeSpace} bytes"); } else { Log.LogInfo((object)"Sufficient disk space available for download."); Log.LogInfo((object)$"Required space: {videoSize} bytes, Available space: {availableFreeSpace} bytes"); } return flag; } private async Task<bool> IsVideoAlreadyDownloaded(string videoUrl, string destinationFolder) { RunResult<VideoData> res = await ytdl.RunVideoDataFetch(videoUrl, default(CancellationToken), true, false, (OptionSet)null); if (res == null) { Log.LogError((object)("Failed to fetch video metadata for URL: " + videoUrl)); return false; } VideoData video = res.Data; string title = video.Title; string videoId = video.ID; string fileNamePattern = "\\[" + Regex.Escape(videoId) + "\\]\\.mp4"; Regex regex = new Regex(fileNamePattern); string[] files = Directory.GetFiles(destinationFolder); string[] array = files; foreach (string filePath in array) { string fileName = Path.GetFileName(filePath); if (regex.IsMatch(fileName)) { Log.LogInfo((object)("Video '" + title + "' already exists. Skipping download.")); return true; } } return false; } private async Task DownloadPlaylist(string playlistUrl, string destinationFolder) { Log.LogInfo((object)"Downloading playlist..."); ytdl.OutputFolder = destinationFolder; OptionSet options = new OptionSet { Format = "best", RecodeVideo = (VideoRecodeFormat)1 }; if (await ytdl.RunVideoPlaylistDownload(playlistUrl, (int?)startOfPlaylist.Value, (int?)endOfPlaylist.Value, (int[])null, "bestvideo+bestaudio/best", (VideoRecodeFormat)0, default(CancellationToken), (IProgress<DownloadProgress>)null, (IProgress<string>)null, options) == null) { HUDManager.Instance.DisplayTip("Download Error", "Failed to download the playlist.", false, false, "DownloadErrorTip"); return; } HUDManager.Instance.DisplayTip("Download Complete", "The playlist has been successfully downloaded.", false, false, "DownloadCompleteTip"); VideoManager.Videos.Clear(); VideoManager.Load(); } private async Task DownloadVideo(string videoUrl, string destinationFolder) { Log.LogInfo((object)"Downloading video..."); if (skipDownloadEnabled.Value && await IsVideoAlreadyDownloaded(videoUrl, destinationFolder)) { HUDManager.Instance.DisplayTip("Download Skipped", "The video is already downloaded.", false, false, "DownloadSkippedTip"); return; } ytdl.OutputFolder = destinationFolder; OptionSet options = new OptionSet { Format = "best", RecodeVideo = (VideoRecodeFormat)1 }; if (await ytdl.RunVideoDownload(videoUrl, "bestvideo+bestaudio/best", (DownloadMergeFormat)0, (VideoRecodeFormat)0, default(CancellationToken), (IProgress<DownloadProgress>)null, (IProgress<string>)null, options) == null) { HUDManager.Instance.DisplayTip("Download Error", "Failed to download the video.", false, false, "DownloadErrorTip"); return; } HUDManager.Instance.DisplayTip("Download Complete", "The video has been successfully downloaded.", false, false, "DownloadCompleteTip"); VideoManager.Videos.Clear(); VideoManager.Load(); } private void OnDestroy() { downloadAction.Disable(); Log.LogInfo((object)"YoutubeDownloader plugin destroyed or disabled. Download action disabled."); } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }