feat: batch-delete recording segments whose files are missing
Add a segment-level cleanup alongside the existing session-level one. DeleteMissingFileTasksAsync scans all non-active record tasks, keeps those whose video file no longer exists on disk, and deletes them individually via DeleteTasksAsync (which also drops any session left empty). Exposed as POST /record-tasks/delete-missing-files and a new "清理无文件分片" action in the record tasks view. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -108,6 +108,11 @@ public sealed class DeleteCompletedRecordTasksRequest
|
||||
public bool DeleteFiles { get; set; }
|
||||
}
|
||||
|
||||
public sealed class DeleteMissingFileRecordTasksRequest
|
||||
{
|
||||
public bool DeleteFiles { get; set; }
|
||||
}
|
||||
|
||||
public sealed class DeleteCompletedRecordTasksResultDto
|
||||
{
|
||||
public required IReadOnlyList<Guid> DeletedTaskIds { get; init; }
|
||||
|
||||
@@ -504,6 +504,32 @@ public sealed class RecordService
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<DeleteCompletedRecordTasksResultDto> DeleteMissingFileTasksAsync(
|
||||
DeleteMissingFileRecordTasksRequest request,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(request);
|
||||
|
||||
var tasks = await _recordTaskRepository.ListAsync(null, cancellationToken);
|
||||
var missingFileTaskIds = tasks
|
||||
.Where(static task => !IsActiveTaskStatus(task.Status) && !HasExistingVideoFile(task))
|
||||
.Select(static task => task.Id)
|
||||
.ToArray();
|
||||
|
||||
if (missingFileTaskIds.Length == 0)
|
||||
{
|
||||
return CreateEmptyDeleteResult();
|
||||
}
|
||||
|
||||
return await DeleteTasksAsync(
|
||||
new DeleteCompletedRecordTasksRequest
|
||||
{
|
||||
TaskIds = missingFileTaskIds,
|
||||
DeleteFiles = request.DeleteFiles
|
||||
},
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<RecordPreviewTicketDto> CreatePreviewTicketAsync(Guid id, string mediaBaseUrl, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var previewTicket = await _recordMediaService.CreatePreviewTicketAsync(id, cancellationToken);
|
||||
@@ -823,6 +849,24 @@ public sealed class RecordService
|
||||
or RecordTaskStatus.Stopping
|
||||
or RecordTaskStatus.Processing;
|
||||
|
||||
private static bool HasExistingVideoFile(RecordTask task)
|
||||
{
|
||||
var candidatePath = !string.IsNullOrWhiteSpace(task.Result?.FilePath)
|
||||
? task.Result!.FilePath
|
||||
: task.OutputFilePath;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(candidatePath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var resolvedPath = Path.IsPathRooted(candidatePath)
|
||||
? candidatePath
|
||||
: Path.GetFullPath(candidatePath, AppContext.BaseDirectory);
|
||||
|
||||
return File.Exists(resolvedPath);
|
||||
}
|
||||
|
||||
private static bool IsActiveSessionStatus(RecordSessionStatus status) =>
|
||||
status is RecordSessionStatus.Starting or RecordSessionStatus.Running or RecordSessionStatus.Stopping;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user