add intro UI for first time registration, add FileCacheManager to scan and rescan for file changes, fix namings, polish UI for normal usage
This commit is contained in:
187
MareSynchronos/Managers/FileCacheManager.cs
Normal file
187
MareSynchronos/Managers/FileCacheManager.cs
Normal file
@@ -0,0 +1,187 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Dalamud.Logging;
|
||||
using MareSynchronos.Factories;
|
||||
using MareSynchronos.FileCacheDB;
|
||||
|
||||
namespace MareSynchronos.Managers
|
||||
{
|
||||
internal class FileCacheManager : IDisposable
|
||||
{
|
||||
private const int MinutesForScan = 10;
|
||||
private readonly FileCacheFactory _fileCacheFactory;
|
||||
private readonly IpcManager _ipcManager;
|
||||
private readonly Configuration _pluginConfiguration;
|
||||
private CancellationTokenSource? _scanCancellationTokenSource;
|
||||
private System.Timers.Timer? _scanScheduler;
|
||||
private Task? _scanTask;
|
||||
private Stopwatch? _timerStopWatch;
|
||||
public FileCacheManager(FileCacheFactory fileCacheFactory, IpcManager ipcManager, Configuration pluginConfiguration)
|
||||
{
|
||||
_fileCacheFactory = fileCacheFactory;
|
||||
_ipcManager = ipcManager;
|
||||
_pluginConfiguration = pluginConfiguration;
|
||||
|
||||
if (_ipcManager.CheckPenumbraApi()
|
||||
&& _pluginConfiguration.AcceptedAgreement
|
||||
&& !string.IsNullOrEmpty(_pluginConfiguration.CacheFolder)
|
||||
&& _pluginConfiguration.ClientSecret.ContainsKey(_pluginConfiguration.ApiUri)
|
||||
&& !string.IsNullOrEmpty(_ipcManager.PenumbraModDirectory()))
|
||||
{
|
||||
StartInitialScan();
|
||||
}
|
||||
}
|
||||
|
||||
public long CurrentFileProgress { get; private set; }
|
||||
public bool IsScanRunning => !_scanTask?.IsCompleted ?? false;
|
||||
|
||||
public TimeSpan TimeToNextScan => TimeSpan.FromMinutes(MinutesForScan).Subtract(_timerStopWatch?.Elapsed ?? TimeSpan.FromMinutes(MinutesForScan));
|
||||
public long TotalFiles { get; private set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
PluginLog.Debug("Disposing File Cache Manager");
|
||||
_scanScheduler?.Stop();
|
||||
_scanCancellationTokenSource?.Cancel();
|
||||
}
|
||||
|
||||
public void StartInitialScan()
|
||||
{
|
||||
_scanCancellationTokenSource = new CancellationTokenSource();
|
||||
_scanTask = StartFileScan(_scanCancellationTokenSource.Token);
|
||||
}
|
||||
|
||||
private async Task StartFileScan(CancellationToken ct)
|
||||
{
|
||||
_scanCancellationTokenSource = new CancellationTokenSource();
|
||||
var penumbraDir = _ipcManager.PenumbraModDirectory()!;
|
||||
PluginLog.Debug("Getting files from " + penumbraDir);
|
||||
var scannedFiles = new ConcurrentDictionary<string, bool>(
|
||||
Directory.EnumerateFiles(penumbraDir, "*.*", SearchOption.AllDirectories)
|
||||
.Select(s => s.ToLowerInvariant())
|
||||
.Where(f => f.Contains(@"\chara\") && (f.EndsWith(".tex") || f.EndsWith(".mdl") || f.EndsWith(".mtrl")))
|
||||
.Select(p => new KeyValuePair<string, bool>(p, false)));
|
||||
List<FileCache> fileCaches;
|
||||
await using (FileCacheContext db = new())
|
||||
{
|
||||
fileCaches = db.FileCaches.ToList();
|
||||
}
|
||||
|
||||
TotalFiles = scannedFiles.Count;
|
||||
|
||||
var fileCachesToUpdate = new ConcurrentBag<FileCache>();
|
||||
var fileCachesToDelete = new ConcurrentBag<FileCache>();
|
||||
var fileCachesToAdd = new ConcurrentBag<FileCache>();
|
||||
|
||||
PluginLog.Debug("Getting file list from Database");
|
||||
// scan files from database
|
||||
Parallel.ForEach(fileCaches, new ParallelOptions()
|
||||
{
|
||||
MaxDegreeOfParallelism = _pluginConfiguration.MaxParallelScan,
|
||||
CancellationToken = ct,
|
||||
},
|
||||
cache =>
|
||||
{
|
||||
if (ct.IsCancellationRequested) return;
|
||||
if (!File.Exists(cache.Filepath))
|
||||
{
|
||||
fileCachesToDelete.Add(cache);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (scannedFiles.ContainsKey(cache.Filepath))
|
||||
{
|
||||
scannedFiles[cache.Filepath] = true;
|
||||
}
|
||||
FileInfo fileInfo = new(cache.Filepath);
|
||||
if (fileInfo.LastWriteTimeUtc.Ticks == long.Parse(cache.LastModifiedDate)) return;
|
||||
_fileCacheFactory.UpdateFileCache(cache);
|
||||
fileCachesToUpdate.Add(cache);
|
||||
}
|
||||
|
||||
var files = CurrentFileProgress;
|
||||
Interlocked.Increment(ref files);
|
||||
CurrentFileProgress = files;
|
||||
});
|
||||
|
||||
if (ct.IsCancellationRequested) return;
|
||||
|
||||
// scan new files
|
||||
Parallel.ForEach(scannedFiles.Where(c => c.Value == false), new ParallelOptions()
|
||||
{
|
||||
MaxDegreeOfParallelism = _pluginConfiguration.MaxParallelScan,
|
||||
CancellationToken = ct
|
||||
},
|
||||
file =>
|
||||
{
|
||||
fileCachesToAdd.Add(_fileCacheFactory.Create(file.Key));
|
||||
|
||||
var files = CurrentFileProgress;
|
||||
Interlocked.Increment(ref files);
|
||||
CurrentFileProgress = files;
|
||||
});
|
||||
|
||||
await using (FileCacheContext db = new())
|
||||
{
|
||||
if (fileCachesToAdd.Any() || fileCachesToUpdate.Any() || fileCachesToDelete.Any())
|
||||
{
|
||||
db.FileCaches.AddRange(fileCachesToAdd);
|
||||
db.FileCaches.UpdateRange(fileCachesToUpdate);
|
||||
db.FileCaches.RemoveRange(fileCachesToDelete);
|
||||
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
}
|
||||
|
||||
PluginLog.Debug("Scan complete");
|
||||
TotalFiles = 0;
|
||||
CurrentFileProgress = 0;
|
||||
|
||||
if (!_pluginConfiguration.InitialScanComplete)
|
||||
{
|
||||
_pluginConfiguration.InitialScanComplete = true;
|
||||
_pluginConfiguration.Save();
|
||||
_timerStopWatch = Stopwatch.StartNew();
|
||||
StartScheduler();
|
||||
}
|
||||
else if (_timerStopWatch == null)
|
||||
{
|
||||
StartScheduler();
|
||||
_timerStopWatch = Stopwatch.StartNew();
|
||||
}
|
||||
}
|
||||
|
||||
private void StartScheduler()
|
||||
{
|
||||
PluginLog.Debug("Scheduling next scan for in " + MinutesForScan + " minutes");
|
||||
_scanScheduler = new System.Timers.Timer(TimeSpan.FromMinutes(MinutesForScan).TotalMilliseconds)
|
||||
{
|
||||
AutoReset = false,
|
||||
Enabled = false,
|
||||
};
|
||||
_scanScheduler.AutoReset = true;
|
||||
_scanScheduler.Elapsed += (_, _) =>
|
||||
{
|
||||
_timerStopWatch?.Stop();
|
||||
if (_scanTask?.IsCompleted ?? false)
|
||||
{
|
||||
PluginLog.Warning("Scanning task is still running, not reinitiating.");
|
||||
return;
|
||||
}
|
||||
|
||||
PluginLog.Debug("Initiating periodic scan for mod changes");
|
||||
_scanTask = StartFileScan(_scanCancellationTokenSource!.Token);
|
||||
_timerStopWatch = Stopwatch.StartNew();
|
||||
};
|
||||
|
||||
_scanScheduler.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user