Files
VRChat-YouTube-Workaround/MainForm.cs
2026-02-01 13:57:36 +01:00

238 lines
6.2 KiB
C#

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> downloadProgress;
private readonly Progress<string> outputProgress;
private CancellationTokenSource cts;
private class FailedException(string message) : Exception(message) { }
public MainForm()
{
InitializeComponent();
ytdl = new YoutubeDL();
downloadProgress = new Progress<DownloadProgress>(CaptureProgress);
outputProgress = new Progress<string>(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<string> 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<string> 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<string> 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<string> 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<CopyPartyResult>(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();
}
}