From 525ab5c41e6b291365cfa9f5d20d87ffe3142823 Mon Sep 17 00:00:00 2001 From: Marro64 Date: Sun, 1 Feb 2026 13:57:36 +0100 Subject: [PATCH] V1 --- .gitignore | 1 + Form1.Designer.cs | 39 --- Form1.cs | 10 - MainForm.Designer.cs | 206 +++++++++++++++ MainForm.cs | 237 ++++++++++++++++++ Form1.resx => MainForm.resx | 54 ++-- Program.cs | 36 ++- .../PublishProfiles/FolderProfile.pubxml | 11 + .../PublishProfiles/FolderProfile.pubxml.user | 8 + VRChat-YouTube-Workaround.csproj | 11 + VRChat-YouTube-Workaround.csproj.user | 15 +- 11 files changed, 533 insertions(+), 95 deletions(-) delete mode 100644 Form1.Designer.cs delete mode 100644 Form1.cs create mode 100644 MainForm.Designer.cs create mode 100644 MainForm.cs rename Form1.resx => MainForm.resx (93%) create mode 100644 Properties/PublishProfiles/FolderProfile.pubxml create mode 100644 Properties/PublishProfiles/FolderProfile.pubxml.user diff --git a/.gitignore b/.gitignore index 538f3d7..63bc327 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .vs obj +bin diff --git a/Form1.Designer.cs b/Form1.Designer.cs deleted file mode 100644 index 32992cb..0000000 --- a/Form1.Designer.cs +++ /dev/null @@ -1,39 +0,0 @@ -namespace VRChat_YouTube_Workaround -{ - partial class Form1 - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - components = new System.ComponentModel.Container(); - AutoScaleMode = AutoScaleMode.Font; - ClientSize = new Size(800, 450); - Text = "Form1"; - } - - #endregion - } -} diff --git a/Form1.cs b/Form1.cs deleted file mode 100644 index 88f6d16..0000000 --- a/Form1.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace VRChat_YouTube_Workaround -{ - public partial class Form1 : Form - { - public Form1() - { - InitializeComponent(); - } - } -} diff --git a/MainForm.Designer.cs b/MainForm.Designer.cs new file mode 100644 index 0000000..dd64bee --- /dev/null +++ b/MainForm.Designer.cs @@ -0,0 +1,206 @@ +namespace Marro.VRChatYouTubeWorkaround +{ + partial class MainForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + txtInputUrl = new TextBox(); + btnGo = new Button(); + grpInputUrl = new GroupBox(); + btnPasteAndGo = new Button(); + txtLog = new TextBox(); + grpStatus = new GroupBox(); + btnCancel = new Button(); + lblStatus = new Label(); + prgProgress = new ProgressBar(); + grpResult = new GroupBox(); + txtResultUrl = new TextBox(); + btnCopy = new Button(); + grpInputUrl.SuspendLayout(); + grpStatus.SuspendLayout(); + grpResult.SuspendLayout(); + SuspendLayout(); + // + // txtInputUrl + // + txtInputUrl.Location = new Point(6, 51); + txtInputUrl.Name = "txtInputUrl"; + txtInputUrl.Size = new Size(683, 23); + txtInputUrl.TabIndex = 2; + // + // btnGo + // + btnGo.Location = new Point(695, 51); + btnGo.Name = "btnGo"; + btnGo.Size = new Size(75, 23); + btnGo.TabIndex = 3; + btnGo.TabStop = false; + btnGo.Text = "Go!"; + btnGo.UseVisualStyleBackColor = true; + btnGo.Click += btnGo_Click; + // + // grpInputUrl + // + grpInputUrl.Controls.Add(btnPasteAndGo); + grpInputUrl.Controls.Add(txtInputUrl); + grpInputUrl.Controls.Add(btnGo); + grpInputUrl.Location = new Point(12, 12); + grpInputUrl.Name = "grpInputUrl"; + grpInputUrl.Size = new Size(776, 81); + grpInputUrl.TabIndex = 4; + grpInputUrl.TabStop = false; + grpInputUrl.Text = "Input URL"; + // + // btnPasteAndGo + // + btnPasteAndGo.BackColor = Color.Chartreuse; + btnPasteAndGo.ForeColor = SystemColors.ControlText; + btnPasteAndGo.Location = new Point(6, 22); + btnPasteAndGo.Name = "btnPasteAndGo"; + btnPasteAndGo.Size = new Size(764, 23); + btnPasteAndGo.TabIndex = 1; + btnPasteAndGo.Text = "Paste and Go!"; + btnPasteAndGo.UseVisualStyleBackColor = false; + btnPasteAndGo.Click += btnPasteAndGo_Click; + // + // txtLog + // + txtLog.Location = new Point(12, 259); + txtLog.Multiline = true; + txtLog.Name = "txtLog"; + txtLog.ReadOnly = true; + txtLog.ScrollBars = ScrollBars.Both; + txtLog.Size = new Size(776, 172); + txtLog.TabIndex = 0; + txtLog.TabStop = false; + txtLog.WordWrap = false; + // + // grpStatus + // + grpStatus.Controls.Add(btnCancel); + grpStatus.Controls.Add(lblStatus); + grpStatus.Controls.Add(prgProgress); + grpStatus.Location = new Point(12, 99); + grpStatus.Name = "grpStatus"; + grpStatus.Size = new Size(776, 97); + grpStatus.TabIndex = 6; + grpStatus.TabStop = false; + grpStatus.Text = "Status"; + // + // btnCancel + // + btnCancel.Enabled = false; + btnCancel.Location = new Point(6, 66); + btnCancel.Name = "btnCancel"; + btnCancel.Size = new Size(764, 23); + btnCancel.TabIndex = 4; + btnCancel.Text = "Cancel"; + btnCancel.UseVisualStyleBackColor = true; + btnCancel.Click += btnCancel_Click; + // + // lblStatus + // + lblStatus.AutoSize = true; + lblStatus.Location = new Point(6, 19); + lblStatus.Name = "lblStatus"; + lblStatus.Size = new Size(39, 15); + lblStatus.TabIndex = 6; + lblStatus.Text = "Ready"; + // + // prgProgress + // + prgProgress.Location = new Point(6, 37); + prgProgress.Name = "prgProgress"; + prgProgress.Size = new Size(764, 23); + prgProgress.TabIndex = 0; + // + // grpResult + // + grpResult.Controls.Add(txtResultUrl); + grpResult.Controls.Add(btnCopy); + grpResult.Location = new Point(12, 202); + grpResult.Name = "grpResult"; + grpResult.Size = new Size(776, 51); + grpResult.TabIndex = 5; + grpResult.TabStop = false; + grpResult.Text = "Result"; + // + // txtResultUrl + // + txtResultUrl.Location = new Point(6, 19); + txtResultUrl.Name = "txtResultUrl"; + txtResultUrl.ReadOnly = true; + txtResultUrl.Size = new Size(683, 23); + txtResultUrl.TabIndex = 5; + // + // btnCopy + // + btnCopy.Location = new Point(695, 19); + btnCopy.Name = "btnCopy"; + btnCopy.Size = new Size(75, 23); + btnCopy.TabIndex = 1; + btnCopy.Text = "Copy"; + btnCopy.UseVisualStyleBackColor = true; + btnCopy.Click += btnCopy_Click; + // + // MainForm + // + AutoScaleDimensions = new SizeF(7F, 15F); + AutoScaleMode = AutoScaleMode.Font; + ClientSize = new Size(800, 443); + Controls.Add(grpResult); + Controls.Add(grpInputUrl); + Controls.Add(grpStatus); + Controls.Add(txtLog); + Name = "MainForm"; + Text = "VRChat-YouTube-Workaround"; + grpInputUrl.ResumeLayout(false); + grpInputUrl.PerformLayout(); + grpStatus.ResumeLayout(false); + grpStatus.PerformLayout(); + grpResult.ResumeLayout(false); + grpResult.PerformLayout(); + ResumeLayout(false); + PerformLayout(); + } + + #endregion + + private TextBox txtInputUrl; + private Button btnGo; + private GroupBox grpInputUrl; + private TextBox txtLog; + private GroupBox grpStatus; + private ProgressBar prgProgress; + private GroupBox grpResult; + private TextBox txtResultUrl; + private Button btnCopy; + private Button btnPasteAndGo; + private Label lblStatus; + private Button btnCancel; + } +} diff --git a/MainForm.cs b/MainForm.cs new file mode 100644 index 0000000..267fcb4 --- /dev/null +++ b/MainForm.cs @@ -0,0 +1,237 @@ +using Microsoft.Extensions.Logging; +using Microsoft.VisualBasic.ApplicationServices; +using Newtonsoft.Json; +using System.Media; +using System.Security.Policy; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using System.Windows.Forms; +using System.Xml; +using YoutubeDLSharp; + +namespace Marro.VRChatYouTubeWorkaround; + +public partial class MainForm : Form +{ + private readonly YoutubeDL ytdl; + + private readonly Progress downloadProgress; + + private readonly Progress outputProgress; + + private CancellationTokenSource cts; + + private class FailedException(string message) : Exception(message) { } + + public MainForm() + { + InitializeComponent(); + + ytdl = new YoutubeDL(); + downloadProgress = new Progress(CaptureProgress); + outputProgress = new Progress(WriteLog); + + prgProgress.Maximum = 100; + } + + private void btnGo_Click(object sender, EventArgs e) + { + ClearLog(); + ResetProgress(); + + var url = txtInputUrl.Text.Trim(); + if (string.IsNullOrEmpty(url)) + { + WriteStatus("Enter a url!"); + return; + } + + cts = new CancellationTokenSource(); + var cancellationToken = cts.Token; + var task = Task.Run(() => ProcessVideo(url, cancellationToken)); + task.ContinueWith(ProcessVideoShowResult); + + grpInputUrl.Enabled = false; + btnCancel.Enabled = true; + } + + private async Task ProcessVideo(string sourceUrl, CancellationToken cancellationToken) + { + string filePath = ""; + try + { + filePath = await DownloadVideoToFile(sourceUrl, cancellationToken); + cancellationToken.ThrowIfCancellationRequested(); + + WriteStatus("Uploading video..."); + var url = await UploadFile(filePath, cancellationToken); + return url; + } + finally + { + File.Delete(filePath); + } + } + + private void ProcessVideoShowResult(Task task) + { + if (InvokeRequired) + { + Invoke(() => ProcessVideoShowResult(task)); + return; + } + + grpInputUrl.Enabled = true; + btnCancel.Enabled = false; + + if (cts.IsCancellationRequested || task.IsCanceled) + { + WriteStatus("Cancelled."); + ResetProgress(); + return; + } + + if (task.IsFaulted) + { + WriteLog($"{task.Exception.InnerException}"); + WriteStatus("Failed."); + ResetProgress(); + return; + } + + var url = task.Result; + + txtResultUrl.Text = url; + WriteLog($"Final URL: {url}"); + WriteStatus("Finished."); + Clipboard.SetText(url); + Chime(); + } + + private async Task DownloadVideoToFile(string url, CancellationToken cancellationToken) + { + WriteStatus("Downloading required files..."); + await Utils.DownloadBinaries(); + + WriteStatus("Downloading video..."); + var res = await ytdl.RunVideoDownload(url, format: "mp4", progress: downloadProgress, output: outputProgress, ct: cancellationToken); + + if (!res.Success) + { + throw new FailedException("Failed to download video!"); + } + + return res.Data; + } + + private static async Task UploadFile(string filePath, CancellationToken cancellationToken) + { + var url = new Uri("https://media.komawo.gay/"); + url = new Uri(url, $"{Path.GetFileName(filePath)}?j"); + + using var client = new HttpClient(); + using var fileStream = File.OpenRead(filePath); + using var content = new StreamContent(fileStream); + + content.Headers.ContentType = + new System.Net.Http.Headers.MediaTypeHeaderValue("video/mp4"); + content.Headers.Add("pw", "Luminance7-Trapper0-Choice6-Brunette9-Abacus7"); + content.Headers.Add("want", "url"); + content.Headers.Add("ck", "no"); + content.Headers.Add("replace", "1"); + + var response = await client.PutAsync(url, content, cancellationToken); + + cancellationToken.ThrowIfCancellationRequested(); + + response.EnsureSuccessStatusCode(); + + var responseJson = await response.Content.ReadAsStringAsync(cancellationToken); + cancellationToken.ThrowIfCancellationRequested(); + var responseObject = JsonConvert.DeserializeObject(responseJson); + + return responseObject?.FileUrl ?? throw new FailedException("Failed to get url for uploaded video!"); + } + + private class CopyPartyResult + { + [JsonProperty("filesz")] + public int? FileSZ { get; set; } + + [JsonProperty("fileurl")] + public string? FileUrl { get; set; } + } + + private void WriteLog(string message) + { + if (InvokeRequired) + { + Invoke(() => WriteLog(message)); + return; + } + + txtLog.AppendText($"{message}{Environment.NewLine}"); + } + + private void WriteStatus(string message) + { + if (InvokeRequired) + { + Invoke(() => WriteStatus(message)); + return; + } + + lblStatus.Text = message; + } + + private void ClearLog() + { + txtLog.Clear(); + } + + private void Chime() + { + SoundPlayer simpleSound = new SoundPlayer(@"C:\Windows\Media\Windows Background.wav"); + try + { + simpleSound.Play(); + } + catch + { + // Playing sound is not critical + } + } + + private void CaptureProgress(DownloadProgress progress) + { + var newValue = (int)(progress.Progress * 100); + var step = newValue - prgProgress.Value; + if (step <= 0) + { + return; + } + prgProgress.Step = step; + prgProgress.PerformStep(); + } + + private void ResetProgress() + { + prgProgress.Value = 0; + } + + private void btnCopy_Click(object sender, EventArgs e) + { + Clipboard.SetText(txtResultUrl.Text); + } + + private void btnPasteAndGo_Click(object sender, EventArgs e) + { + txtInputUrl.Text = Clipboard.GetText(); + btnGo.PerformClick(); + } + + private void btnCancel_Click(object sender, EventArgs e) + { + cts?.Cancel(); + } +} diff --git a/Form1.resx b/MainForm.resx similarity index 93% rename from Form1.resx rename to MainForm.resx index 1af7de1..8b2ff64 100644 --- a/Form1.resx +++ b/MainForm.resx @@ -1,17 +1,17 @@  - diff --git a/Program.cs b/Program.cs index 147c13a..68b9cdb 100644 --- a/Program.cs +++ b/Program.cs @@ -1,17 +1,27 @@ -namespace VRChat_YouTube_Workaround +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace Marro.VRChatYouTubeWorkaround; + +internal static class Program { - internal static class Program + [STAThread] + static void Main() { - /// - /// The main entry point for the application. - /// - [STAThread] - static void Main() - { - // To customize application configuration such as set high DPI settings or default font, - // see https://aka.ms/applicationconfiguration. - ApplicationConfiguration.Initialize(); - Application.Run(new Form1()); - } + Application.SetHighDpiMode(HighDpiMode.SystemAware); + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + + var services = new ServiceCollection(); + ConfigureServices(services); + using ServiceProvider serviceProvider = services.BuildServiceProvider(); + + var form = serviceProvider.GetRequiredService(); + Application.Run(form); + } + + private static void ConfigureServices(ServiceCollection services) + { + services.AddSingleton(); } } \ No newline at end of file diff --git a/Properties/PublishProfiles/FolderProfile.pubxml b/Properties/PublishProfiles/FolderProfile.pubxml new file mode 100644 index 0000000..cadfda9 --- /dev/null +++ b/Properties/PublishProfiles/FolderProfile.pubxml @@ -0,0 +1,11 @@ + + + + + Release + Any CPU + bin\Release\net10.0-windows\publish\ + FileSystem + <_TargetId>Folder + + \ No newline at end of file diff --git a/Properties/PublishProfiles/FolderProfile.pubxml.user b/Properties/PublishProfiles/FolderProfile.pubxml.user new file mode 100644 index 0000000..af239df --- /dev/null +++ b/Properties/PublishProfiles/FolderProfile.pubxml.user @@ -0,0 +1,8 @@ + + + + + True|2026-01-31T21:08:30.9376550Z||;True|2026-01-31T22:07:21.7187910+01:00||; + + + \ No newline at end of file diff --git a/VRChat-YouTube-Workaround.csproj b/VRChat-YouTube-Workaround.csproj index f967408..487be19 100644 --- a/VRChat-YouTube-Workaround.csproj +++ b/VRChat-YouTube-Workaround.csproj @@ -9,4 +9,15 @@ enable + + Marro.VRChatYouTubeWorkaround + + + + + + + + + \ No newline at end of file diff --git a/VRChat-YouTube-Workaround.csproj.user b/VRChat-YouTube-Workaround.csproj.user index 7814ea2..08ee9fd 100644 --- a/VRChat-YouTube-Workaround.csproj.user +++ b/VRChat-YouTube-Workaround.csproj.user @@ -1,8 +1,11 @@  - - - Form - - - + + <_LastSelectedProfileId>C:\Users\marro\Etc\DotNET_Projects\VRChat-YouTube-Workaround\Properties\PublishProfiles\FolderProfile.pubxml + + + + Form + + + \ No newline at end of file