123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178 |
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Threading;
- using System.Threading.Tasks;
- using MediaBrowser.Common.Configuration;
- using MediaBrowser.Model.Configuration;
- using MediaBrowser.Model.IO;
- using Microsoft.Extensions.Logging;
- namespace MediaBrowser.Controller.MediaEncoding;
- /// <summary>
- /// Transcoding segment cleaner.
- /// </summary>
- public class TranscodingSegmentCleaner : IDisposable
- {
- private readonly TranscodingJob _job;
- private readonly ILogger<TranscodingSegmentCleaner> _logger;
- private readonly IConfigurationManager _config;
- private readonly IFileSystem _fileSystem;
- private readonly IMediaEncoder _mediaEncoder;
- private Timer? _timer;
- private int _segmentLength;
- /// <summary>
- /// Initializes a new instance of the <see cref="TranscodingSegmentCleaner"/> class.
- /// </summary>
- /// <param name="job">Transcoding job dto.</param>
- /// <param name="logger">Instance of the <see cref="ILogger{TranscodingSegmentCleaner}"/> interface.</param>
- /// <param name="config">Instance of the <see cref="IConfigurationManager"/> interface.</param>
- /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
- /// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param>
- /// <param name="segmentLength">The segment length of this transcoding job.</param>
- public TranscodingSegmentCleaner(TranscodingJob job, ILogger<TranscodingSegmentCleaner> logger, IConfigurationManager config, IFileSystem fileSystem, IMediaEncoder mediaEncoder, int segmentLength)
- {
- _job = job;
- _logger = logger;
- _config = config;
- _fileSystem = fileSystem;
- _mediaEncoder = mediaEncoder;
- _segmentLength = segmentLength;
- }
- /// <summary>
- /// Start timer.
- /// </summary>
- public void Start()
- {
- _timer = new Timer(TimerCallback, null, 20000, 20000);
- }
- /// <summary>
- /// Stop cleaner.
- /// </summary>
- public void Stop()
- {
- DisposeTimer();
- }
- /// <summary>
- /// Dispose cleaner.
- /// </summary>
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
- /// <summary>
- /// Dispose cleaner.
- /// </summary>
- /// <param name="disposing">Disposing.</param>
- protected virtual void Dispose(bool disposing)
- {
- if (disposing)
- {
- DisposeTimer();
- }
- }
- private EncodingOptions GetOptions()
- {
- return _config.GetEncodingOptions();
- }
- private async void TimerCallback(object? state)
- {
- if (_job.HasExited)
- {
- DisposeTimer();
- return;
- }
- var options = GetOptions();
- var enableSegmentDeletion = options.EnableSegmentDeletion;
- var segmentKeepSeconds = Math.Max(options.SegmentKeepSeconds, 20);
- if (enableSegmentDeletion)
- {
- var downloadPositionTicks = _job.DownloadPositionTicks ?? 0;
- var downloadPositionSeconds = Convert.ToInt64(TimeSpan.FromTicks(downloadPositionTicks).TotalSeconds);
- if (downloadPositionSeconds > 0 && segmentKeepSeconds > 0 && downloadPositionSeconds > segmentKeepSeconds)
- {
- var idxMaxToDelete = (downloadPositionSeconds - segmentKeepSeconds) / _segmentLength;
- if (idxMaxToDelete > 0)
- {
- await DeleteSegmentFiles(_job, 0, idxMaxToDelete, 1500).ConfigureAwait(false);
- }
- }
- }
- }
- private async Task DeleteSegmentFiles(TranscodingJob job, long idxMin, long idxMax, int delayMs)
- {
- var path = job.Path ?? throw new ArgumentException("Path can't be null.");
- _logger.LogDebug("Deleting segment file(s) index {Min} to {Max} from {Path}", idxMin, idxMax, path);
- await Task.Delay(delayMs).ConfigureAwait(false);
- try
- {
- if (job.Type == TranscodingJobType.Hls)
- {
- DeleteHlsSegmentFiles(path, idxMin, idxMax);
- }
- }
- catch (Exception ex)
- {
- _logger.LogDebug(ex, "Error deleting segment file(s) {Path}", path);
- }
- }
- private void DeleteHlsSegmentFiles(string outputFilePath, long idxMin, long idxMax)
- {
- var directory = Path.GetDirectoryName(outputFilePath)
- ?? throw new ArgumentException("Path can't be a root directory.", nameof(outputFilePath));
- var name = Path.GetFileNameWithoutExtension(outputFilePath);
- var filesToDelete = _fileSystem.GetFilePaths(directory)
- .Where(f => long.TryParse(Path.GetFileNameWithoutExtension(f).Replace(name, string.Empty, StringComparison.Ordinal), out var idx)
- && (idx >= idxMin && idx <= idxMax));
- List<Exception>? exs = null;
- foreach (var file in filesToDelete)
- {
- try
- {
- _logger.LogDebug("Deleting HLS segment file {0}", file);
- _fileSystem.DeleteFile(file);
- }
- catch (IOException ex)
- {
- (exs ??= new List<Exception>()).Add(ex);
- _logger.LogDebug(ex, "Error deleting HLS segment file {Path}", file);
- }
- }
- if (exs is not null)
- {
- throw new AggregateException("Error deleting HLS segment files", exs);
- }
- }
- private void DisposeTimer()
- {
- if (_timer is not null)
- {
- _timer.Dispose();
- _timer = null;
- }
- }
- }
|