2
0
forked from sim0n00ps/OF-DL
OF-DL/OF DL/Services/StartupService.cs

255 lines
8.5 KiB
C#

using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using Newtonsoft.Json;
using OF_DL.Helpers;
using OF_DL.Models;
using Serilog;
using WidevineConstants = OF_DL.Widevine.Constants;
namespace OF_DL.Services;
public class StartupService(IConfigService configService, IAuthService authService) : IStartupService
{
public async Task<StartupResult> ValidateEnvironmentAsync()
{
StartupResult result = new();
// OS validation
OperatingSystem os = Environment.OSVersion;
result.OsVersionString = os.VersionString;
Log.Debug($"Operating system information: {os.VersionString}");
if (os.Platform == PlatformID.Win32NT && os.Version.Major < 10)
{
result.IsWindowsVersionValid = false;
Log.Error("Windows version prior to 10.x: {0}", os.VersionString);
}
// FFmpeg detection
DetectFfmpeg(result);
if (result.FfmpegFound)
{
// Escape backslashes for Windows
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) &&
result.FfmpegPath!.Contains(@":\") &&
!result.FfmpegPath.Contains(@":\\"))
{
result.FfmpegPath = result.FfmpegPath.Replace(@"\", @"\\");
configService.CurrentConfig!.FFmpegPath = result.FfmpegPath;
}
// Get FFmpeg version
result.FfmpegVersion = await GetFfmpegVersionAsync(result.FfmpegPath!);
}
// Widevine device checks
result.ClientIdBlobMissing = !File.Exists(Path.Join(WidevineConstants.DEVICES_FOLDER,
WidevineConstants.DEVICE_NAME, "device_client_id_blob"));
result.DevicePrivateKeyMissing = !File.Exists(Path.Join(WidevineConstants.DEVICES_FOLDER,
WidevineConstants.DEVICE_NAME, "device_private_key"));
if (!result.ClientIdBlobMissing)
{
Log.Debug("device_client_id_blob found");
}
else
{
Log.Debug("device_client_id_blob missing");
}
if (!result.DevicePrivateKeyMissing)
{
Log.Debug("device_private_key found");
}
else
{
Log.Debug("device_private_key missing");
}
// rules.json validation
if (File.Exists("rules.json"))
{
result.RulesJsonExists = true;
try
{
JsonConvert.DeserializeObject<DynamicRules>(File.ReadAllText("rules.json"));
Log.Debug("Rules.json: ");
Log.Debug(JsonConvert.SerializeObject(File.ReadAllText("rules.json"), Formatting.Indented));
result.RulesJsonValid = true;
}
catch (Exception e)
{
result.RulesJsonError = e.Message;
Log.Error("rules.json processing failed.", e.Message);
}
}
return result;
}
public async Task<VersionCheckResult> CheckVersionAsync()
{
VersionCheckResult result = new();
#if !DEBUG
try
{
result.LocalVersion = Assembly.GetEntryAssembly()?.GetName().Version;
using CancellationTokenSource cts = new(TimeSpan.FromSeconds(30));
string? latestReleaseTag = null;
try
{
latestReleaseTag = await VersionHelper.GetLatestReleaseTag(cts.Token);
}
catch (OperationCanceledException)
{
result.TimedOut = true;
Log.Warning("Version check timed out after 30 seconds");
return result;
}
if (latestReleaseTag == null)
{
result.CheckFailed = true;
Log.Error("Failed to get the latest release tag.");
return result;
}
result.LatestVersion = new Version(latestReleaseTag.Replace("OFDLV", ""));
int versionComparison = result.LocalVersion!.CompareTo(result.LatestVersion);
result.IsUpToDate = versionComparison >= 0;
Log.Debug("Detected client running version " +
$"{result.LocalVersion.Major}.{result.LocalVersion.Minor}.{result.LocalVersion.Build}");
Log.Debug("Latest release version " +
$"{result.LatestVersion.Major}.{result.LatestVersion.Minor}.{result.LatestVersion.Build}");
}
catch (Exception e)
{
result.CheckFailed = true;
Log.Error("Error checking latest release on GitHub.", e.Message);
}
#else
Log.Debug("Running in Debug/Local mode. Version check skipped.");
result.IsUpToDate = true;
#endif
return result;
}
private void DetectFfmpeg(StartupResult result)
{
if (!string.IsNullOrEmpty(configService.CurrentConfig!.FFmpegPath) &&
ValidateFilePath(configService.CurrentConfig.FFmpegPath))
{
result.FfmpegFound = true;
result.FfmpegPath = configService.CurrentConfig.FFmpegPath;
Log.Debug($"FFMPEG found: {result.FfmpegPath}");
Log.Debug("FFMPEG path set in config.conf");
}
else if (!string.IsNullOrEmpty(authService.CurrentAuth?.FfmpegPath) &&
ValidateFilePath(authService.CurrentAuth.FfmpegPath))
{
result.FfmpegFound = true;
result.FfmpegPath = authService.CurrentAuth.FfmpegPath;
configService.CurrentConfig.FFmpegPath = result.FfmpegPath;
Log.Debug($"FFMPEG found: {result.FfmpegPath}");
Log.Debug("FFMPEG path set in auth.json");
}
else if (string.IsNullOrEmpty(configService.CurrentConfig.FFmpegPath))
{
string? ffmpegPath = GetFullPath("ffmpeg") ?? GetFullPath("ffmpeg.exe");
if (ffmpegPath != null)
{
result.FfmpegFound = true;
result.FfmpegPathAutoDetected = true;
result.FfmpegPath = ffmpegPath;
configService.CurrentConfig.FFmpegPath = ffmpegPath;
Log.Debug($"FFMPEG found: {ffmpegPath}");
Log.Debug("FFMPEG path found via PATH or current directory");
}
}
if (!result.FfmpegFound)
{
Log.Error($"Cannot locate FFmpeg with path: {configService.CurrentConfig.FFmpegPath}");
}
}
private static async Task<string?> GetFfmpegVersionAsync(string ffmpegPath)
{
try
{
ProcessStartInfo processStartInfo = new()
{
FileName = ffmpegPath,
Arguments = "-version",
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
using Process? process = Process.Start(processStartInfo);
if (process != null)
{
string output = await process.StandardOutput.ReadToEndAsync();
await process.WaitForExitAsync();
Log.Information("FFmpeg version output:\n{Output}", output);
string firstLine = output.Split('\n')[0].Trim();
if (firstLine.StartsWith("ffmpeg version"))
{
int versionStart = "ffmpeg version ".Length;
int copyrightIndex = firstLine.IndexOf(" Copyright");
return copyrightIndex > versionStart
? firstLine.Substring(versionStart, copyrightIndex - versionStart)
: firstLine.Substring(versionStart);
}
}
}
catch (Exception ex)
{
Log.Warning(ex, "Failed to get FFmpeg version");
}
return null;
}
private static bool ValidateFilePath(string path)
{
char[] invalidChars = Path.GetInvalidPathChars();
if (path.Any(c => invalidChars.Contains(c)))
{
return false;
}
return File.Exists(path);
}
public static string? GetFullPath(string filename)
{
if (File.Exists(filename))
{
return Path.GetFullPath(filename);
}
string pathEnv = Environment.GetEnvironmentVariable("PATH") ?? "";
foreach (string path in pathEnv.Split(Path.PathSeparator))
{
string fullPath = Path.Combine(path, filename);
if (File.Exists(fullPath))
{
return fullPath;
}
}
return null;
}
}