using AutoUpdaterDotNET;
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Interop;
using TwitchDownloaderCore.Extensions;
using TwitchDownloaderWPF.Properties;
using TwitchDownloaderWPF.Services;
using Xabe.FFmpeg;
using Xabe.FFmpeg.Downloader;
namespace TwitchDownloaderWPF
{
///
/// Interaction logic for MainWindow.xaml
///
public partial class MainWindow : Window
{
public static PageVodDownload pageVodDownload = new PageVodDownload();
public static PageClipDownload pageClipDownload = new PageClipDownload();
public static PageChatDownload pageChatDownload = new PageChatDownload();
public static PageChatUpdate pageChatUpdate = new PageChatUpdate();
public static PageChatRender pageChatRender = new PageChatRender();
public static PageQueue pageQueue = new PageQueue();
public MainWindow()
{
InitializeComponent();
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
if (Settings.Default.UpgradeRequired)
{
Settings.Default.Upgrade();
Settings.Default.UpgradeRequired = false;
Settings.Default.Save();
}
}
private void btnVodDownload_Click(object sender, RoutedEventArgs e)
{
Main.Content = pageVodDownload;
}
private void btnClipDownload_Click(object sender, RoutedEventArgs e)
{
Main.Content = pageClipDownload;
}
private void btnChatDownload_Click(object sender, RoutedEventArgs e)
{
Main.Content = pageChatDownload;
}
private void btnChatUpdate_Click(object sender, RoutedEventArgs e)
{
Main.Content = pageChatUpdate;
}
private void btnChatRender_Click(object sender, RoutedEventArgs e)
{
Main.Content = pageChatRender;
}
private void btnQueue_Click(object sender, RoutedEventArgs e)
{
Main.Content = pageQueue;
}
private void Window_OnSourceInitialized(object sender, EventArgs e)
{
App.RequestAppThemeChange();
}
private async void Window_Loaded(object sender, RoutedEventArgs e)
{
Main.Content = pageVodDownload;
// Replace old crop parameters with new trim parameters
Settings.Default.TemplateVod = Regex.Replace(Settings.Default.TemplateVod, "{crop_(?=(?:start|end)(?:_|}))", "{trim_");
Settings.Default.TemplateClip = Regex.Replace(Settings.Default.TemplateClip, "{crop_(?=(?:start|end)(?:_|}))", "{trim_");
Settings.Default.TemplateChat = Regex.Replace(Settings.Default.TemplateChat, "{crop_(?=(?:start|end)(?:_|}))", "{trim_");
Settings.Default.Save();
// Flash the window taskbar icon if it is not in the foreground. This is to mitigate a problem where
// it will sometimes start behind other windows, usually (but not always) due to the user's actions.
FlashTaskbarIconIfNotForeground(TimeSpan.FromSeconds(3));
// Despite not specifying a revision in the AssemblyVersion, the compiler still adds one. We don't want that.
var currentVersion = Assembly.GetExecutingAssembly().GetName().Version!.StripRevisionIfDefault();
#if DEBUG
Title = $"Twitch Downloader v{currentVersion} - DEBUG";
#else
Title = $"Twitch Downloader v{currentVersion}";
#endif
// TODO: extract FFmpeg handling to a dedicated service
if (!File.Exists("ffmpeg.exe") || File.GetLastWriteTime("ffmpeg.exe") < DateTime.Now - TimeSpan.FromDays(365))
{
var oldTitle = Title;
try
{
await FFmpegDownloader.GetLatestVersion(FFmpegVersion.Official, new FfmpegDownloadProgress());
// Flash the window to signify that FFmpeg has been downloaded
FlashTaskbarIconIfNotForeground(TimeSpan.FromSeconds(3));
}
catch (Exception ex)
{
var messageBoxResult = MessageBox.Show(this, string.Format(Translations.Strings.UnableToDownloadFfmpegFull, "https://ffmpeg.org/download.html", Path.Combine(Environment.CurrentDirectory, "ffmpeg.exe")),
Translations.Strings.UnableToDownloadFfmpeg, MessageBoxButton.OKCancel, MessageBoxImage.Information);
if (messageBoxResult == MessageBoxResult.OK)
{
Process.Start(new ProcessStartInfo("https://ffmpeg.org/download.html") { UseShellExecute = true });
}
if (Settings.Default.VerboseErrors)
{
MessageBox.Show(this, ex.ToString(), Translations.Strings.VerboseErrorOutput, MessageBoxButton.OK, MessageBoxImage.Error);
}
}
Title = oldTitle;
}
AutoUpdater.InstalledVersion = currentVersion;
#if !DEBUG
if (AppContext.BaseDirectory.StartsWith(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)))
{
// If the app is in user profile, the updater probably doesn't need administrator permissions
AutoUpdater.RunUpdateAsAdmin = false;
}
AutoUpdater.Start("https://downloader-update.twitcharchives.workers.dev");
#endif
}
private async void FlashTaskbarIconIfNotForeground(TimeSpan flashDuration)
{
var currentWindow = new WindowInteropHelper(this).Handle;
var foregroundWindow = NativeFunctions.GetForegroundWindow();
if (currentWindow == foregroundWindow)
return;
var flashWInfo = new NativeFunctions.FlashWInfo
{
StructSize = (uint)Marshal.SizeOf(),
WindowHandle = currentWindow,
Flags = NativeFunctions.FlashWInfo.FLASHW_TRAY,
FlashCount = uint.MaxValue,
Timeout = 0
};
_ = NativeFunctions.FlashWindowEx(ref flashWInfo);
await Task.Delay(flashDuration);
var stopFlashWInfo = new NativeFunctions.FlashWInfo
{
StructSize = (uint)Marshal.SizeOf(),
WindowHandle = currentWindow,
Flags = NativeFunctions.FlashWInfo.FLASHW_STOP,
FlashCount = 0,
Timeout = 0
};
_ = NativeFunctions.FlashWindowEx(ref stopFlashWInfo);
}
private class FfmpegDownloadProgress : IProgress
{
private int _lastPercent = -1;
public void Report(ProgressInfo value)
{
var percent = (int)(value.DownloadedBytes / (double)value.TotalBytes * 100);
if (percent > _lastPercent)
{
var window = Application.Current.MainWindow;
if (window is null) return;
_lastPercent = percent;
var oldTitle = window.Title;
if (oldTitle.IndexOf('-') == -1) oldTitle += " -";
window.Title = string.Concat(
oldTitle.AsSpan(0, oldTitle.IndexOf('-')),
"- ",
string.Format(Translations.Strings.StatusDownloaderFFmpeg, percent.ToString())
);
}
}
}
}
}