Refactor code formatting and improve readability: standardize line breaks and indentation across various endpoint files, enhancing code clarity and maintainability. Update package version formatting in Directory.Packages.props for consistency.

This commit is contained in:
Leonid Pershin
2026-07-25 19:55:01 +03:00
parent 1f6fa6f1ae
commit f57b7503ed
122 changed files with 1856 additions and 892 deletions
@@ -60,11 +60,15 @@ public sealed class FfmpegBumperRenderer(
var playlist = Path.Combine(assetDir, "index.m3u8");
if (!File.Exists(playlist))
throw new InvalidOperationException("ffmpeg не создал плейлист заставки index.m3u8.");
throw new InvalidOperationException(
"ffmpeg не создал плейлист заставки index.m3u8."
);
var segmentCount = Directory.GetFiles(assetDir, "seg*.ts").Length;
if (segmentCount == 0)
throw new InvalidOperationException("ffmpeg не создал ни одного сегмента заставки.");
throw new InvalidOperationException(
"ffmpeg не создал ни одного сегмента заставки."
);
return new BumperRenderResult(
TimeSpan.FromSeconds(target),
@@ -168,17 +172,33 @@ public sealed class FfmpegBumperRenderer(
var line2Size = FitSize(spec.FreeLine2, titleSize, textWidth);
var line1Y = (int)(h * 0.40);
var line2Y = line1Y + (int)(line2Size * 1.2);
vchain.Append(',').Append(DrawTitle(font, nowFile, spec.AccentColor, line1Size, line1Y, 0.2));
vchain.Append(',').Append(DrawTitle(font, nextFile, spec.TextColor, line2Size, line2Y, 0.5));
vchain
.Append(',')
.Append(DrawTitle(font, nowFile, spec.AccentColor, line1Size, line1Y, 0.2));
vchain
.Append(',')
.Append(DrawTitle(font, nextFile, spec.TextColor, line2Size, line2Y, 0.5));
}
else
{
var nowSize = FitSize(spec.NowTitle, titleSize, textWidth);
var nextSize = FitSize(spec.NextTitle, titleSize, textWidth);
vchain.Append(',').Append(DrawLabel(font, spec.NowLabel, spec.AccentColor, labelSize, nowLabelY, 0.2));
vchain.Append(',').Append(DrawTitle(font, nowFile, spec.TextColor, nowSize, nowTitleY, 0.3));
vchain.Append(',').Append(DrawLabel(font, spec.NextLabel, spec.AccentColor, labelSize, nextLabelY, 1.0));
vchain.Append(',').Append(DrawTitle(font, nextFile, spec.TextColor, nextSize, nextTitleY, 1.1));
vchain
.Append(',')
.Append(
DrawLabel(font, spec.NowLabel, spec.AccentColor, labelSize, nowLabelY, 0.2)
);
vchain
.Append(',')
.Append(DrawTitle(font, nowFile, spec.TextColor, nowSize, nowTitleY, 0.3));
vchain
.Append(',')
.Append(
DrawLabel(font, spec.NextLabel, spec.AccentColor, labelSize, nextLabelY, 1.0)
);
vchain
.Append(',')
.Append(DrawTitle(font, nextFile, spec.TextColor, nextSize, nextTitleY, 1.1));
}
vchain.Append("[v]");
@@ -186,28 +206,47 @@ public sealed class FfmpegBumperRenderer(
var args = new List<string> { "-hide_banner", "-nostdin", "-y" };
args.AddRange(inputs);
args.AddRange(
[
"-filter_complex", filterComplex,
"-map", "[v]",
"-map", "[a]",
"-threads", _media.TranscodeThreads.ToString(CultureInfo.InvariantCulture),
"-c:v", "libx264",
"-preset", "veryfast",
"-crf", "21",
"-pix_fmt", "yuv420p",
"-force_key_frames", $"expr:gte(t,n_forced*{seg.ToString(CultureInfo.InvariantCulture)})",
"-sc_threshold", "0",
"-c:a", "aac",
"-b:a", "128k",
"-ac", "2",
"-ar", "48000",
"-t", Fmt(target),
"-f", "hls",
"-hls_time", seg.ToString(CultureInfo.InvariantCulture),
"-hls_playlist_type", "vod",
"-hls_list_size", "0",
"-hls_segment_filename", Path.Combine(assetDir, "seg%05d.ts"),
args.AddRange([
"-filter_complex",
filterComplex,
"-map",
"[v]",
"-map",
"[a]",
"-threads",
_media.TranscodeThreads.ToString(CultureInfo.InvariantCulture),
"-c:v",
"libx264",
"-preset",
"veryfast",
"-crf",
"21",
"-pix_fmt",
"yuv420p",
"-force_key_frames",
$"expr:gte(t,n_forced*{seg.ToString(CultureInfo.InvariantCulture)})",
"-sc_threshold",
"0",
"-c:a",
"aac",
"-b:a",
"128k",
"-ac",
"2",
"-ar",
"48000",
"-t",
Fmt(target),
"-f",
"hls",
"-hls_time",
seg.ToString(CultureInfo.InvariantCulture),
"-hls_playlist_type",
"vod",
"-hls_list_size",
"0",
"-hls_segment_filename",
Path.Combine(assetDir, "seg%05d.ts"),
Path.Combine(assetDir, "index.m3u8"),
]);
return args;
@@ -261,8 +300,7 @@ public sealed class FfmpegBumperRenderer(
/// <summary>Экранирование пути для значения опции фильтра (Windows-разделители → прямые слэши,
/// двоеточие экранируется). На Linux (контейнере) — фактически no-op.</summary>
private static string EscapePath(string path) =>
path.Replace('\\', '/').Replace(":", "\\:");
private static string EscapePath(string path) => path.Replace('\\', '/').Replace(":", "\\:");
/// <summary>Экранирование литерального текста подписи внутри значения опции drawtext.</summary>
private static string EscapeText(string text) =>
@@ -146,21 +146,19 @@ public sealed class FfmpegMediaProcessor(
args.Add(Fmt(target));
}
args.AddRange(
[
"-f",
"hls",
"-hls_time",
segmentSeconds.ToString(CultureInfo.InvariantCulture),
"-hls_playlist_type",
"vod",
"-hls_list_size",
"0",
"-hls_segment_filename",
Path.Combine(assetDir, "seg%05d.ts"),
Path.Combine(assetDir, "index.m3u8"),
]
);
args.AddRange([
"-f",
"hls",
"-hls_time",
segmentSeconds.ToString(CultureInfo.InvariantCulture),
"-hls_playlist_type",
"vod",
"-hls_list_size",
"0",
"-hls_segment_filename",
Path.Combine(assetDir, "seg%05d.ts"),
Path.Combine(assetDir, "index.m3u8"),
]);
return args;
}
@@ -169,15 +167,7 @@ public sealed class FfmpegMediaProcessor(
{
var result = await ProcessRunner.RunAsync(
_media.FfprobePath,
[
"-v",
"quiet",
"-print_format",
"json",
"-show_format",
"-show_streams",
path,
],
["-v", "quiet", "-print_format", "json", "-show_format", "-show_streams", path],
lowPriority: false,
cancellationToken
);
@@ -58,9 +58,10 @@ public sealed class FileSystemMediaStorage(MediaPathResolver paths) : IMediaStor
CancellationToken cancellationToken
)
{
var sourcePath = source == MediaSource.Inbox
? paths.InboxPath(sourceToken)
: paths.UploadPath(sourceToken);
var sourcePath =
source == MediaSource.Inbox
? paths.InboxPath(sourceToken)
: paths.UploadPath(sourceToken);
if (!File.Exists(sourcePath))
throw new FileNotFoundException("Исходный файл не найден в хранилище.", sourcePath);
@@ -5,7 +5,10 @@ namespace TeleWave.Infrastructure.Media;
/// <summary>Скачивает изображение по URL через HTTP-клиент «metadata» и определяет расширение.</summary>
public sealed class ImageDownloader(IHttpClientFactory httpFactory) : IImageDownloader
{
public async Task<DownloadedImage?> DownloadAsync(string url, CancellationToken cancellationToken)
public async Task<DownloadedImage?> DownloadAsync(
string url,
CancellationToken cancellationToken
)
{
try
{
@@ -18,7 +21,8 @@ public sealed class ImageDownloader(IHttpClientFactory httpFactory) : IImageDown
var bytes = await response.Content.ReadAsByteArrayAsync(cancellationToken);
return bytes.Length == 0 ? null : new DownloadedImage(bytes, ext);
}
catch (Exception ex) when (ex is HttpRequestException or TaskCanceledException or IOException)
catch (Exception ex)
when (ex is HttpRequestException or TaskCanceledException or IOException)
{
return null;
}
@@ -29,7 +29,9 @@ public sealed class InboxScannerBackgroundService(
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
paths.EnsureDirectories();
using var timer = new PeriodicTimer(TimeSpan.FromSeconds(Math.Max(1, _media.InboxScanSeconds)));
using var timer = new PeriodicTimer(
TimeSpan.FromSeconds(Math.Max(1, _media.InboxScanSeconds))
);
while (await timer.WaitForNextTickAsync(stoppingToken))
{
@@ -53,7 +55,8 @@ public sealed class InboxScannerBackgroundService(
if (!Directory.Exists(paths.InboxDir))
return;
var files = Directory.EnumerateFiles(paths.InboxDir)
var files = Directory
.EnumerateFiles(paths.InboxDir)
.Where(f => MediaFormats.IsAllowed(f))
.ToList();
@@ -108,11 +111,19 @@ public sealed class InboxScannerBackgroundService(
if (result.IsSuccess)
{
queue.Enqueue(result.Value);
logger.LogInformation("Из inbox зарегистрирован ассет {AssetId} ({File})", result.Value, fileName);
logger.LogInformation(
"Из inbox зарегистрирован ассет {AssetId} ({File})",
result.Value,
fileName
);
}
else
{
logger.LogWarning("Не удалось зарегистрировать {File} из inbox: {Error}", fileName, result.Error.Code);
logger.LogWarning(
"Не удалось зарегистрировать {File} из inbox: {Error}",
fileName,
result.Error.Code
);
}
}
}
@@ -72,10 +72,7 @@ public sealed class MediaProcessingBackgroundService(
CancellationToken.None
);
inFlight[task] = 0;
_ = task.ContinueWith(
t => inFlight.TryRemove(t, out _),
TaskScheduler.Default
);
_ = task.ContinueWith(t => inFlight.TryRemove(t, out _), TaskScheduler.Default);
}
// Работы нет — ждём сигнала о новой либо периодического опроса.
@@ -118,8 +115,8 @@ public sealed class MediaProcessingBackgroundService(
await using var scope = scopeFactory.CreateAsyncScope();
var db = scope.ServiceProvider.GetRequiredService<IAppDbContext>();
var interrupted = await db.MediaAssets
.Where(x => x.Status == MediaAssetStatus.Processing)
var interrupted = await db
.MediaAssets.Where(x => x.Status == MediaAssetStatus.Processing)
.ToListAsync(cancellationToken);
if (interrupted.Count == 0)
return;
@@ -134,13 +131,15 @@ public sealed class MediaProcessingBackgroundService(
/// либо null если работы нет. Вызывается только диспетчером последовательно, поэтому два транскода
/// не возьмут один ассет.
/// </summary>
private async Task<(Guid Id, string Extension)?> ClaimNextAsync(CancellationToken cancellationToken)
private async Task<(Guid Id, string Extension)?> ClaimNextAsync(
CancellationToken cancellationToken
)
{
await using var scope = scopeFactory.CreateAsyncScope();
var db = scope.ServiceProvider.GetRequiredService<IAppDbContext>();
var asset = await db.MediaAssets
.Where(x => x.Status == MediaAssetStatus.Pending)
var asset = await db
.MediaAssets.Where(x => x.Status == MediaAssetStatus.Pending)
.OrderBy(x => x.CreatedAt)
.FirstOrDefaultAsync(cancellationToken);
if (asset is null)
@@ -152,7 +151,11 @@ public sealed class MediaProcessingBackgroundService(
}
/// <summary>Обрабатывает уже захваченный (Processing) ассет: транскод → Ready/Failed.</summary>
private async Task ProcessClaimedAsync(Guid assetId, string extension, CancellationToken cancellationToken)
private async Task ProcessClaimedAsync(
Guid assetId,
string extension,
CancellationToken cancellationToken
)
{
try
{
@@ -189,7 +192,10 @@ public sealed class MediaProcessingBackgroundService(
await using var scope = scopeFactory.CreateAsyncScope();
var db = scope.ServiceProvider.GetRequiredService<IAppDbContext>();
var asset = await db.MediaAssets.FirstOrDefaultAsync(x => x.Id == assetId, cancellationToken);
var asset = await db.MediaAssets.FirstOrDefaultAsync(
x => x.Id == assetId,
cancellationToken
);
if (asset is null)
return;
@@ -211,7 +217,10 @@ public sealed class MediaProcessingBackgroundService(
await using var scope = scopeFactory.CreateAsyncScope();
var db = scope.ServiceProvider.GetRequiredService<IAppDbContext>();
var asset = await db.MediaAssets.FirstOrDefaultAsync(x => x.Id == assetId, cancellationToken);
var asset = await db.MediaAssets.FirstOrDefaultAsync(
x => x.Id == assetId,
cancellationToken
);
if (asset is null)
return;
@@ -51,7 +51,8 @@ internal static class ProcessRunner
{
process.PriorityClass = ProcessPriorityClass.BelowNormal;
}
catch (Exception ex) when (ex is InvalidOperationException or PlatformNotSupportedException)
catch (Exception ex)
when (ex is InvalidOperationException or PlatformNotSupportedException)
{
// Процесс мог завершиться мгновенно или платформа не поддерживает — не критично.
}