Crunchy-Downloader/CRD/Utils/QueueManagement/QueuePersistenceManager.cs
Elwador d9813191ad - Added **Global Pause button** for the download queue [#418](https://github.com/Crunchy-DL/Crunchy-Downloader/issues/418)
- Added **fallback for sync failures** [#407](https://github.com/Crunchy-DL/Crunchy-Downloader/issues/407)
- Added **history setting** to remove non-existent series/episodes on refresh [#420](https://github.com/Crunchy-DL/Crunchy-Downloader/issues/420)
- Added **movies to history**
- Added **queue persistence**
- Changed **download item state handling**
- Changed **download item removal processing**
- Made small changes to **font detection**
- Changed **rate limit error handling**
- Fixed issue where **files were not always cleaned up** for removed downloads
- Fixed **crash when the queue list was modified**
- Fixed **changelog parsing** not handling versions like `vX.X.X.X`, which caused changes to be re-added on every restart
2026-04-20 15:40:58 +02:00

117 lines
3.4 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using CRD.Downloader;
using CRD.Downloader.Crunchyroll;
using CRD.Utils;
using CRD.Utils.Files;
using CRD.Utils.Structs;
namespace CRD.Utils.QueueManagement;
public sealed class QueuePersistenceManager : IDisposable{
private readonly object syncLock = new();
private readonly QueueManager queueManager;
private Timer? saveTimer;
public QueuePersistenceManager(QueueManager queueManager){
this.queueManager = queueManager ?? throw new ArgumentNullException(nameof(queueManager));
this.queueManager.QueueStateChanged += OnQueueStateChanged;
}
public void RestoreQueue(){
var options = CrunchyrollManager.Instance.CrunOptions;
if (!options.PersistQueue){
CfgManager.DeleteFileIfExists(CfgManager.PathCrQueue);
return;
}
if (!CfgManager.CheckIfFileExists(CfgManager.PathCrQueue))
return;
var savedQueue = CfgManager.ReadJsonFromFile<List<CrunchyEpMeta>>(CfgManager.PathCrQueue);
if (savedQueue == null || savedQueue.Count == 0){
CfgManager.DeleteFileIfExists(CfgManager.PathCrQueue);
return;
}
queueManager.ReplaceQueue(savedQueue.Select(PrepareRestoredItem));
}
public void SaveNow(){
lock (syncLock){
saveTimer?.Change(Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan);
}
PersistQueueSnapshot();
}
public void ScheduleSave(){
lock (syncLock){
if (saveTimer == null){
saveTimer = new Timer(_ => PersistQueueSnapshot(), null, TimeSpan.FromMilliseconds(750), Timeout.InfiniteTimeSpan);
return;
}
saveTimer.Change(TimeSpan.FromMilliseconds(750), Timeout.InfiniteTimeSpan);
}
}
private void OnQueueStateChanged(object? sender, EventArgs e){
ScheduleSave();
}
private void PersistQueueSnapshot(){
var options = CrunchyrollManager.Instance.CrunOptions;
if (!options.PersistQueue){
CfgManager.DeleteFileIfExists(CfgManager.PathCrQueue);
return;
}
var queue = queueManager.Queue;
if (queue.Count == 0){
CfgManager.DeleteFileIfExists(CfgManager.PathCrQueue);
return;
}
var snapshot = queue
.Select(CloneForPersistence)
.Where(item => item != null)
.ToList();
if (snapshot.Count == 0){
CfgManager.DeleteFileIfExists(CfgManager.PathCrQueue);
return;
}
CfgManager.WriteJsonToFile(CfgManager.PathCrQueue, snapshot);
}
private static CrunchyEpMeta PrepareRestoredItem(CrunchyEpMeta item){
item.Data ??= [];
item.DownloadSubs ??= [];
item.downloadedFiles ??= [];
item.DownloadProgress ??= new DownloadProgress();
if (!item.DownloadProgress.IsFinished){
item.DownloadProgress.ResetForRetry();
}
item.RenewCancellationToken();
return item;
}
private static CrunchyEpMeta? CloneForPersistence(CrunchyEpMeta item){
return Helpers.DeepCopy(item);
}
public void Dispose(){
lock (syncLock){
saveTimer?.Dispose();
saveTimer = null;
}
queueManager.QueueStateChanged -= OnQueueStateChanged;
}
}