using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.ScheduledTasks.Tasks;
///
/// Deletes all transcoding temp files.
///
public class DeleteTranscodeFileTask : IScheduledTask, IConfigurableScheduledTask
{
private readonly ILogger _logger;
private readonly IConfigurationManager _configurationManager;
private readonly IFileSystem _fileSystem;
private readonly ILocalizationManager _localization;
private readonly ITranscodeSessionStore _sessionStore;
///
/// Initializes a new instance of the class.
///
/// Instance of the interface.
/// Instance of the interface.
/// Instance of the interface.
/// Instance of the interface.
/// Instance of the interface.
public DeleteTranscodeFileTask(
ILogger logger,
IFileSystem fileSystem,
IConfigurationManager configurationManager,
ILocalizationManager localization,
ITranscodeSessionStore sessionStore)
{
_logger = logger;
_fileSystem = fileSystem;
_configurationManager = configurationManager;
_localization = localization;
_sessionStore = sessionStore;
}
///
public string Name => _localization.GetLocalizedString("TaskCleanTranscode");
///
public string Description => _localization.GetLocalizedString("TaskCleanTranscodeDescription");
///
public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory");
///
public string Key => "DeleteTranscodeFiles";
///
public bool IsHidden => false;
///
public bool IsEnabled => true;
///
public bool IsLogged => true;
///
public IEnumerable GetDefaultTriggers()
{
yield return new TaskTriggerInfo
{
Type = TaskTriggerInfoType.StartupTrigger
};
yield return new TaskTriggerInfo
{
Type = TaskTriggerInfoType.IntervalTrigger,
IntervalTicks = TimeSpan.FromHours(24).Ticks
};
}
///
public async Task ExecuteAsync(IProgress progress, CancellationToken cancellationToken)
{
var minDateModified = DateTime.UtcNow.AddDays(-1);
progress.Report(50);
IEnumerable activeSessions;
try
{
activeSessions = await _sessionStore.GetActiveSessionsAsync(cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to retrieve active transcode sessions. Skipping deletion to avoid removing files in use.");
progress.Report(100);
return;
}
DeleteTempFilesFromDirectory(_configurationManager.GetTranscodePath(), minDateModified, activeSessions, progress, cancellationToken);
}
///
/// Deletes the transcoded temp files from directory with a last write time less than a given date,
/// skipping any files that belong to an active transcode session.
///
/// The directory.
/// The min date modified.
/// The currently active transcode sessions.
/// The progress.
/// The task cancellation token.
private void DeleteTempFilesFromDirectory(string directory, DateTime minDateModified, IEnumerable activeSessions, IProgress progress, CancellationToken cancellationToken)
{
var activeSessionList = activeSessions.ToList();
var filesToDelete = _fileSystem.GetFiles(directory, true)
.Where(f => _fileSystem.GetLastWriteTimeUtc(f) < minDateModified)
.ToList();
var index = 0;
foreach (var file in filesToDelete)
{
double percent = index;
percent /= filesToDelete.Count;
progress.Report(100 * percent);
cancellationToken.ThrowIfCancellationRequested();
if (IsFileProtectedByActiveSession(file.FullName, activeSessionList))
{
_logger.LogDebug("Skipping deletion of {FilePath} as it belongs to an active transcode session.", file.FullName);
index++;
continue;
}
FileSystemHelper.DeleteFile(_fileSystem, file.FullName, _logger);
index++;
}
FileSystemHelper.DeleteEmptyFolders(_fileSystem, directory, _logger);
progress.Report(100);
}
private static bool IsFileProtectedByActiveSession(string filePath, IList activeSessions)
{
foreach (var session in activeSessions)
{
if (!string.IsNullOrEmpty(session.ManifestPath) &&
string.Equals(filePath, session.ManifestPath, StringComparison.OrdinalIgnoreCase))
{
return true;
}
if (!string.IsNullOrEmpty(session.SegmentPathPrefix) &&
filePath.StartsWith(session.SegmentPathPrefix, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
}