OneDrive Backup
OneDrive Backup
This commit is contained in:
@@ -0,0 +1,207 @@
|
||||
using hassio_onedrive_backup.Contracts;
|
||||
using hassio_onedrive_backup.Graph;
|
||||
using hassio_onedrive_backup.Hass;
|
||||
using Microsoft.Extensions.FileSystemGlobbing;
|
||||
using Microsoft.Graph;
|
||||
using onedrive_backup.Graph;
|
||||
using System.Collections;
|
||||
using System.Diagnostics;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace hassio_onedrive_backup.Sync
|
||||
{
|
||||
internal class SyncManager
|
||||
{
|
||||
private const string HashAlgo = "SHA256";
|
||||
private const string OneDriveFileSyncRootDir = "FileSync";
|
||||
private AddonOptions _addonOptions;
|
||||
private IGraphHelper _graphHelper;
|
||||
private IHassioClient _hassIoClient;
|
||||
private BitArray _allowedHours;
|
||||
private HassOnedriveFileSyncEntityState _hassEntityState;
|
||||
private IWebHostEnvironment? _environment;
|
||||
private TransferSpeedHelper _transferSpeedHelper;
|
||||
private Matcher _fileMatcher;
|
||||
|
||||
public SyncManager(IServiceProvider serviceProvider, BitArray allowedHours, TransferSpeedHelper? transferSpeedHelper)
|
||||
{
|
||||
_addonOptions = serviceProvider.GetService<AddonOptions>();
|
||||
_graphHelper = serviceProvider.GetService<IGraphHelper>();
|
||||
_hassIoClient = serviceProvider.GetService<IHassioClient>();
|
||||
_hassEntityState = serviceProvider.GetService<HassOnedriveFileSyncEntityState>();
|
||||
_environment = serviceProvider.GetService<IWebHostEnvironment>();
|
||||
_transferSpeedHelper = transferSpeedHelper;
|
||||
_allowedHours = allowedHours;
|
||||
_fileMatcher = new();
|
||||
_fileMatcher.AddIncludePatterns(_addonOptions.SyncPaths);
|
||||
}
|
||||
|
||||
public async void SyncLoop(CancellationToken ct)
|
||||
{
|
||||
while (true && ct.IsCancellationRequested == false)
|
||||
{
|
||||
try
|
||||
{
|
||||
var now = DateTimeHelper.Instance!.Now;
|
||||
|
||||
// Check if we are in the allowed hours
|
||||
if (_allowedHours[now.Hour] == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_hassEntityState.State = HassOnedriveFileSyncEntityState.FileState.Syncing;
|
||||
await _hassEntityState.UpdateBackupEntityInHass();
|
||||
|
||||
var matchingFiles = _fileMatcher.GetResultsInFullPath("/");
|
||||
foreach (var matchingFile in matchingFiles)
|
||||
{
|
||||
try
|
||||
{
|
||||
await SyncFile(matchingFile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ConsoleLogger.LogError($"Error syncing file {matchingFile}: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
if (_addonOptions.FileSyncRemoveDeleted)
|
||||
{
|
||||
await DeleteRemovedFilesFromOneDrive($"{OneDriveFileSyncRootDir}", "/");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ConsoleLogger.LogError($"Error Syncing: {ex}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_hassEntityState.State = HassOnedriveFileSyncEntityState.FileState.Synced;
|
||||
await _hassEntityState.UpdateBackupEntityInHass();
|
||||
await Task.Delay(TimeSpan.FromMinutes(5));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SyncFile(string path)
|
||||
{
|
||||
var fileInfo = new FileInfo(path);
|
||||
if (fileInfo.Length == 0)
|
||||
{
|
||||
ConsoleLogger.LogWarning($"Skipping 0-byte file: {path}");
|
||||
return;
|
||||
}
|
||||
|
||||
var now = DateTimeHelper.Instance!.Now;
|
||||
string fileHash = CalculateFileHash(path);
|
||||
var localFileSyncData = new SyncFileData(path, fileHash, fileInfo.Length);
|
||||
|
||||
string remotePath = $"/{OneDriveFileSyncRootDir}{path}".Replace("//", "/").Replace(@"\\", @"\");
|
||||
if (_environment!.IsDevelopment())
|
||||
{
|
||||
remotePath = remotePath.Replace(@"c:\", "/", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
DriveItem? remoteFile = await _graphHelper.GetItemInAppFolderAsync(remotePath);
|
||||
bool requiresUpload =
|
||||
remoteFile == null
|
||||
|| remoteFile.Size != fileInfo.Length
|
||||
|| remoteFile.File.Hashes.Sha256Hash.Equals(fileHash, StringComparison.OrdinalIgnoreCase) == false;
|
||||
|
||||
if (requiresUpload == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ConsoleLogger.LogInfo($"File {path} out of sync. Starting Upload");
|
||||
var uploadSuccessful = await _graphHelper.UploadFileAsync(
|
||||
path,
|
||||
now,
|
||||
_addonOptions.InstanceName,
|
||||
_transferSpeedHelper,
|
||||
remotePath,
|
||||
async (prog, speed) =>
|
||||
{
|
||||
_hassEntityState.UploadPercentage = prog;
|
||||
_hassEntityState.UploadSpeed = speed / 1024;
|
||||
await _hassEntityState.UpdateBackupEntityInHass();
|
||||
Debug.WriteLine($"Progress: {prog}");
|
||||
|
||||
},
|
||||
flatten: false
|
||||
);
|
||||
}
|
||||
|
||||
private string CalculateFileHash(string path)
|
||||
{
|
||||
using (var hasher = HashAlgorithm.Create(HashAlgo))
|
||||
{
|
||||
using (var fileStream = System.IO.File.OpenRead(path))
|
||||
{
|
||||
byte[] hash = hasher!.ComputeHash(fileStream);
|
||||
return BitConverter.ToString(hash).Replace("-", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DeleteRemovedFilesFromOneDrive(string remotePath, string localPath)
|
||||
{
|
||||
var item = await _graphHelper.GetItemInAppFolderAsync(remotePath);
|
||||
if (item == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ConsoleLogger.LogVerbose($"Evaulating {remotePath}");
|
||||
localPath = Path.Combine(localPath, item.Name);
|
||||
if (localPath.StartsWith($"/{OneDriveFileSyncRootDir}"))
|
||||
{
|
||||
localPath = "/";
|
||||
}
|
||||
|
||||
if (item.Folder != null)
|
||||
{
|
||||
if (System.IO.Directory.Exists(localPath) == false)
|
||||
{
|
||||
ConsoleLogger.LogInfo($"{localPath} does not exist locally. Deleting from OneDrive");
|
||||
await _graphHelper.DeleteItemFromAppFolderAsync(remotePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Recursive call for files in Folder
|
||||
var folderItems = await _graphHelper.GetItemsInAppFolderAsync(remotePath);
|
||||
foreach (var folderItem in folderItems)
|
||||
{
|
||||
await DeleteRemovedFilesFromOneDrive(Path.Combine(remotePath, folderItem.Name), localPath);
|
||||
}
|
||||
|
||||
// If no content left in folder then remove it
|
||||
folderItems = await _graphHelper.GetItemsInAppFolderAsync(remotePath);
|
||||
if (folderItems.Count == 0)
|
||||
{
|
||||
await _graphHelper.DeleteItemFromAppFolderAsync(remotePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (item.File != null)
|
||||
{
|
||||
if (System.IO.File.Exists(localPath) == false)
|
||||
{
|
||||
ConsoleLogger.LogInfo($"{localPath} does not exist locally. Deleting from OneDrive");
|
||||
await _graphHelper.DeleteItemFromAppFolderAsync(remotePath);
|
||||
}
|
||||
else if (_fileMatcher.Match(localPath).HasMatches == false)
|
||||
{
|
||||
ConsoleLogger.LogInfo($"{localPath} not included in Sync Paths. Deleting from OneDrive");
|
||||
await _graphHelper.DeleteItemFromAppFolderAsync(remotePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
ConsoleLogger.LogVerbose($"{remotePath} in sync with {localPath}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user