Jackett/src/Jackett.Tray/Main.cs

327 lines
13 KiB
C#

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Jackett.Common.Models.Config;
using Jackett.Common.Services;
using Jackett.Common.Services.Interfaces;
using Jackett.Common.Utils;
using NLog;
namespace Jackett.Tray
{
public partial class Main : Form
{
private readonly IProcessService processService;
private readonly IServiceConfigService windowsService;
private readonly ITrayLockService trayLockService;
private readonly ISerializeService serializeService;
private readonly IConfigurationService configurationService;
private readonly ServerConfig serverConfig;
private Process consoleProcess;
private readonly Logger logger;
private bool closeApplicationInitiated;
public Main(string updatedVersion)
{
Hide();
InitializeComponent();
Opacity = 0;
Enabled = false;
WindowState = FormWindowState.Minimized;
FormBorderStyle = FormBorderStyle.FixedToolWindow;
var runtimeSettings = new RuntimeSettings()
{
CustomLogFileName = "TrayLog.txt"
};
LogManager.Configuration = LoggingSetup.GetLoggingConfiguration(runtimeSettings);
logger = LogManager.GetCurrentClassLogger();
logger.Info("Starting Jackett Tray v" + EnvironmentUtil.JackettVersion);
processService = new ProcessService(logger);
windowsService = new WindowsServiceConfigService(processService, logger);
trayLockService = new TrayLockService();
serializeService = new SerializeService();
configurationService = new ConfigurationService(serializeService, processService, logger, runtimeSettings);
serverConfig = configurationService.BuildServerConfig(runtimeSettings);
toolStripMenuItemAutoStart.Checked = AutoStart;
toolStripMenuItemAutoStart.CheckedChanged += toolStripMenuItemAutoStart_CheckedChanged;
toolStripMenuItemWebUI.Click += toolStripMenuItemWebUI_Click;
toolStripMenuItemShutdown.Click += toolStripMenuItemShutdown_Click;
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
{
toolStripMenuItemAutoStart.Visible = true;
}
if (!windowsService.ServiceExists())
{
// We are not installed as a service so just start the web server via JackettConsole and run from the tray.
logger.Info("Starting server from tray");
StartConsoleApplication();
}
updatedVersion = updatedVersion.Equals("yes", StringComparison.OrdinalIgnoreCase) ? EnvironmentUtil.JackettVersion : updatedVersion;
if (!string.IsNullOrWhiteSpace(updatedVersion))
{
notifyIcon1.BalloonTipTitle = "Jackett";
notifyIcon1.BalloonTipText = $"Jackett has updated to version {updatedVersion}";
notifyIcon1.BalloonTipIcon = ToolTipIcon.Info;
notifyIcon1.ShowBalloonTip(10000);
logger.Info($"Display balloon tip, updated to {updatedVersion}");
}
Task.Factory.StartNew(WaitForEvent);
}
private void WaitForEvent()
{
trayLockService.WaitForSignal();
logger.Info("Received signal from tray lock service");
if (windowsService.ServiceExists() && windowsService.ServiceRunning())
{
// We won't be able to start the tray app up again from the updater, as when running via a windows
// service there is no interaction with the desktop.
var scriptPath = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "jackett_launcher.bat");
var startInfo = new ProcessStartInfo()
{
Arguments = $"/c \"{scriptPath}\"",
FileName = "cmd.exe",
UseShellExecute = true,
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden
};
logger.Info($"Starting launcher script as Jackett is running as a Windows service: {startInfo.FileName} {startInfo.Arguments}");
Process.Start(startInfo);
}
CloseTrayApplication();
}
private void toolStripMenuItemWebUI_Click(object sender, EventArgs e)
{
var psi = new ProcessStartInfo
{
FileName = "http://127.0.0.1:" + serverConfig.Port,
UseShellExecute = true
};
Process.Start(psi);
}
private void toolStripMenuItemShutdown_Click(object sender, EventArgs e) => CloseTrayApplication();
private void toolStripMenuItemAutoStart_CheckedChanged(object sender, EventArgs e) => AutoStart = toolStripMenuItemAutoStart.Checked;
private string ProgramTitle => Assembly.GetExecutingAssembly().GetName().Name;
private bool AutoStart
{
get => File.Exists(ShortcutPath);
set
{
if (value && !AutoStart)
{
CreateShortcut();
}
else if (!value && AutoStart)
{
File.Delete(ShortcutPath);
}
}
}
public string ShortcutPath => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Startup), "Jackett.lnk");
private void CreateShortcut()
{
var shellLink = (IShellLink)new ShellLink();
shellLink.SetDescription("Jackett");
shellLink.SetPath(Process.GetCurrentProcess().MainModule.FileName);
shellLink.SetWorkingDirectory(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName));
var persistFile = (IPersistFile)shellLink;
persistFile.Save(ShortcutPath, false);
}
private void contextMenuStrip1_Opening(object sender, CancelEventArgs e)
{
if (windowsService.ServiceExists())
{
backgroundMenuItem.Visible = true;
serviceControlMenuItem.Visible = true;
toolStripSeparator1.Visible = true;
toolStripSeparator2.Visible = true;
if (windowsService.ServiceRunning())
{
serviceControlMenuItem.Text = "Stop background service";
backgroundMenuItem.Text = "Jackett is running as a background service";
toolStripMenuItemWebUI.Enabled = true;
}
else
{
serviceControlMenuItem.Text = "Start background service";
backgroundMenuItem.Text = "Jackett will run as a background service";
toolStripMenuItemWebUI.Enabled = false;
}
toolStripMenuItemShutdown.Text = "Close tray icon";
}
else
{
backgroundMenuItem.Visible = false;
serviceControlMenuItem.Visible = false;
toolStripSeparator1.Visible = false;
toolStripSeparator2.Visible = false;
toolStripMenuItemShutdown.Text = "Shutdown";
}
}
private void serviceControlMenuItem_Click(object sender, EventArgs e)
{
var consolePath = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "JackettConsole.exe");
if (windowsService.ServiceRunning())
{
if (ServerUtil.IsUserAdministrator())
{
windowsService.Stop();
}
else
{
try
{
processService.StartProcessAndLog(consolePath, "--Stop", true);
}
catch
{
MessageBox.Show("Failed to get admin rights to stop the service.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
else
{
if (ServerUtil.IsUserAdministrator())
{
windowsService.Start();
}
else
{
try
{
processService.StartProcessAndLog(consolePath, "--Start", true);
}
catch
{
MessageBox.Show("Failed to get admin rights to start the service.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}
private void CloseTrayApplication()
{
closeApplicationInitiated = true;
logger.Info("Close of tray application initiated");
//Clears notify icon, otherwise icon will still appear on taskbar until you hover the mouse over
notifyIcon1.Icon = null;
notifyIcon1.Dispose();
Application.DoEvents();
if (consoleProcess != null && !consoleProcess.HasExited)
{
consoleProcess.StandardInput.Close();
System.Threading.Thread.Sleep(1000);
if (consoleProcess != null && !consoleProcess.HasExited)
{
consoleProcess.Kill();
}
}
Application.Exit();
}
private void StartConsoleApplication()
{
var applicationFolder = Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath);
var exePath = Path.Combine(applicationFolder, "JackettConsole.exe");
var startInfo = new ProcessStartInfo()
{
CreateNoWindow = true,
UseShellExecute = false,
FileName = exePath,
RedirectStandardInput = true,
RedirectStandardError = true
};
consoleProcess = Process.Start(startInfo);
consoleProcess.EnableRaisingEvents = true;
consoleProcess.Exited += ProcessExited;
consoleProcess.ErrorDataReceived += ProcessErrorDataReceived;
}
private void ProcessErrorDataReceived(object sender, DataReceivedEventArgs e) => logger.Error(e.Data);
private void ProcessExited(object sender, EventArgs e)
{
if (!closeApplicationInitiated)
{
logger.Info("Tray icon not responsible for process exit");
CloseTrayApplication();
}
}
//Used for creating the Windows startup shortcut
[ComImport]
[Guid("00021401-0000-0000-C000-000000000046")]
internal class ShellLink
{
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("000214F9-0000-0000-C000-000000000046")]
internal interface IShellLink
{
void GetPath([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, out IntPtr pfd, int fFlags);
void GetIDList(out IntPtr ppidl);
void SetIDList(IntPtr pidl);
void GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName);
void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName);
void GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath);
void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir);
void GetArguments([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath);
void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs);
void GetHotkey(out short pwHotkey);
void SetHotkey(short wHotkey);
void GetShowCmd(out int piShowCmd);
void SetShowCmd(int iShowCmd);
void GetIconLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, int cchIconPath, out int piIcon);
void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon);
void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, int dwReserved);
void Resolve(IntPtr hwnd, int fFlags);
void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile);
}
}
}