using System.Net.Http; using System.Threading.Tasks; using Serilog; using SPTInstaller.Models; namespace SPTInstaller.Helpers; public static class DownloadCacheHelper { private static HttpClient _httpClient = new() { Timeout = TimeSpan.FromHours(1) }; public static string CachePath = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "spt-installer/cache"); public static string GetCacheSizeText() { if (!Directory.Exists(CachePath)) { var message = "No cache folder"; Log.Information(message); return message; } var cacheDir = new DirectoryInfo(CachePath); var cacheSize = DirectorySizeHelper.GetSizeOfDirectory(cacheDir); if (cacheSize == -1) { var message = "An error occurred while getting the cache size :("; Log.Error(message); return message; } if (cacheSize == 0) return "Empty"; return DirectorySizeHelper.SizeSuffix(cacheSize); } /// /// Check if a file in the cache already exists /// /// The name of the file to check for /// The expected hash of the file in the cache /// The file found in the cache; null if no file is found /// True if the file is in the cache and its hash matches the expected hash, otherwise false public static bool CheckCache(string fileName, string expectedHash, out FileInfo cachedFile) => CheckCache(new FileInfo(Path.Join(CachePath, fileName)), expectedHash, out cachedFile); private static bool CheckCache(FileInfo cacheFile, string expectedHash, out FileInfo fileInCache) { fileInCache = cacheFile; try { cacheFile.Refresh(); Directory.CreateDirectory(CachePath); if (!cacheFile.Exists || expectedHash == null) { Log.Information($"{cacheFile.Name} {(cacheFile.Exists ? "is in cache" : "NOT in cache")}"); Log.Information($"Expected hash: {(expectedHash == null ? "not provided" : expectedHash)}"); return false; } if (FileHashHelper.CheckHash(cacheFile, expectedHash)) { fileInCache = cacheFile; return true; } return false; } catch { return false; } } /// /// Download a file to the cache folder /// /// The file name to save the file as /// The url to download the file from /// A provider for progress updates /// A object of the cached file /// If the file exists, it is deleted before downloading public static async Task DownloadFileAsync(string outputFileName, string targetLink, IProgress progress) { Directory.CreateDirectory(CachePath); var outputFile = new FileInfo(Path.Join(CachePath, outputFileName)); try { if (outputFile.Exists) outputFile.Delete(); // Use the provided extension method using (var file = new FileStream(outputFile.FullName, FileMode.Create, FileAccess.Write, FileShare.None)) await _httpClient.DownloadDataAsync(targetLink, file, progress); outputFile.Refresh(); if (!outputFile.Exists) { Log.Error("Failed to download file from url: {name} :: {url}", outputFileName, targetLink); return null; } return outputFile; } catch (Exception ex) { Log.Error(ex, "Failed to download file from url: {name} :: {url}", outputFileName, targetLink); return null; } } /// /// Download a file to the cache folder /// /// The file name to save the file as /// The stream the download the file from /// A object of the cached file /// If the file exists, it is deleted before downloading public static async Task DownloadFileAsync(string outputFileName, Stream downloadStream) { Directory.CreateDirectory(CachePath); var outputFile = new FileInfo(Path.Join(CachePath, outputFileName)); try { if (outputFile.Exists) outputFile.Delete(); using var patcherFileStream = outputFile.Open(FileMode.Create); { await downloadStream.CopyToAsync(patcherFileStream); } patcherFileStream.Close(); outputFile.Refresh(); if (!outputFile.Exists) { Log.Error("Failed to download file from stream: {name}", outputFileName); return null; } return outputFile; } catch(Exception ex) { Log.Error(ex, "Failed to download file from stream: {fileName}", outputFileName); return null; } } /// /// Get the file from cache or download it /// /// The name of the file to check for in the cache /// The url to download from if the file doesn't exist in the cache /// A provider for progress updates /// The expected hash of the cached file /// A object of the cached file /// Use if you don't have an expected cache file hash public static async Task GetOrDownloadFileAsync(string fileName, string targetLink, IProgress progress, string expectedHash) { try { if (CheckCache(fileName, expectedHash, out var cacheFile)) return cacheFile; return await DownloadFileAsync(fileName, targetLink, progress); } catch (Exception ex) { Log.Error(ex, $"Error while getting file: {fileName}"); return null; } } /// /// Get the file from cache or download it /// /// The name of the file to check for in the cache /// The stream to download from if the file doesn't exist in the cache /// The expected hash of the cached file /// A object of the cached file /// Use if you don't have an expected cache file hash public static async Task GetOrDownloadFileAsync(string fileName, Stream fileDownloadStream, string expectedHash) { try { if (CheckCache(fileName, expectedHash, out var cacheFile)) return cacheFile; return await DownloadFileAsync(fileName, fileDownloadStream); } catch (Exception ex) { Log.Error(ex, $"Error while getting file: {fileName}"); return null; } } }