Refactor various classes to initialize lists with empty array syntax for improved clarity and consistency. Update Program.cs to remove unnecessary partial class declaration due to changes in ASP.NET Core 10. Enhance MediaPathResolver by making AssetRelativePath static for better accessibility. Adjust MediaClaimingBackgroundService to centralize idle polling logic. Improve SignalQueue's WaitAsync method for clearer intent in signal draining.
ci / build-backend (push) Successful in 1m46s
ci / build-frontend (push) Successful in 51s
ci / tests (push) Successful in 1m38s
ci / sonar (push) Successful in 5m1s

This commit is contained in:
Leonid Pershin
2026-07-26 23:36:55 +03:00
parent 59cc453026
commit 940b622c03
18 changed files with 34 additions and 23 deletions
@@ -109,7 +109,7 @@ public sealed class FfmpegBumperRenderer(
segmentCount,
spec.Width,
spec.Height,
paths.AssetRelativePath(assetId)
MediaPathResolver.AssetRelativePath(assetId)
);
}
finally
@@ -81,7 +81,7 @@ public sealed class FfmpegMediaProcessor(
outHeight,
"h264",
"aac",
paths.AssetRelativePath(assetId)
MediaPathResolver.AssetRelativePath(assetId)
);
}
@@ -26,8 +26,10 @@ internal abstract class MediaClaimingBackgroundService<TJob>(
) : BackgroundService
where TJob : class
{
// Периодически перепроверяем БД, даже если сигнал не пришёл — страховка на любой случай.
private static readonly TimeSpan IdlePoll = TimeSpan.FromSeconds(30);
// Период опроса вынесен в неродовой класс: статическое поле обобщённого типа существует
// отдельной копией на каждую замкнутую специализацию, то есть выглядит общей константой,
// не будучи ею. Здесь значение одинаковое и вреда нет, но читателя это вводит в заблуждение.
private static TimeSpan IdlePoll => MediaClaimingDefaults.IdlePoll;
/// <summary>true — воркер обслуживает только сгенерированные ассеты, false — только остальные.</summary>
protected abstract bool HandlesGenerated { get; }
@@ -238,3 +240,10 @@ internal abstract class MediaClaimingBackgroundService<TJob>(
? x => x.Status == status && x.Source == MediaSource.Generated
: x => x.Status == status && x.Source != MediaSource.Generated;
}
/// <summary>Общие для всех воркеров значения — одно на процесс, а не на специализацию.</summary>
internal static class MediaClaimingDefaults
{
/// <summary>Периодически перепроверяем БД, даже если сигнал не пришёл — страховка на любой случай.</summary>
public static readonly TimeSpan IdlePoll = TimeSpan.FromSeconds(30);
}
@@ -64,7 +64,7 @@ public sealed class MediaPathResolver
public string AssetDir(Guid assetId) =>
EnsureWithinRoot(Path.Combine(AssetsDir, assetId.ToString("N")));
public string AssetRelativePath(Guid assetId) => $"assets/{assetId:N}";
public static string AssetRelativePath(Guid assetId) => $"assets/{assetId:N}";
/// <summary>Путь к файлу сегмента внутри каталога ассета (имя файла проверяется на traversal).</summary>
public string SegmentPath(Guid assetId, string fileName)
@@ -20,7 +20,10 @@ public abstract class SignalQueue
public async ValueTask WaitAsync(CancellationToken cancellationToken)
{
await _channel.Reader.ReadAsync(cancellationToken);
// Сдренировать накопившиеся сигналы — работу всё равно берём из БД пачкой.
while (_channel.Reader.TryRead(out _)) { }
while (_channel.Reader.TryRead(out _))
{
// Тело пустое намеренно: сигналы дренируются ради побочного эффекта чтения — работу
// обработчик всё равно берёт из БД пачкой, и сами значения не нужны.
}
}
}
@@ -7,7 +7,7 @@ namespace TeleWave.Infrastructure.Metadata;
public sealed class MetadataProviderResolver : IMetadataProviderResolver
{
private readonly Dictionary<string, IMetadataProvider> _byKey;
private readonly List<string> _available = new();
private readonly List<string> _available = [];
public MetadataProviderResolver(
IEnumerable<IMetadataProvider> providers,