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