Normalize line endings to LF via .gitattributes

Репозиторий хранил фронтенд в CRLF, а часть бэкенда — вперемешку, хотя CI и Docker-сборка
работают под Linux. Прибиваем LF атрибутом `* text=auto eol=lf` и разово нормализуем дерево,
чтобы форматтеры не переписывали файлы целиком на каждом прогоне.

Коммит чисто механический: изменений содержимого нет, только концы строк.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Leonid Pershin
2026-07-27 01:37:33 +03:00
co-authored by Claude Opus 5
parent 9d1c6d2fc3
commit 0442056367
109 changed files with 13469 additions and 13459 deletions
@@ -1,93 +1,93 @@
using LiteCqrs;
using Microsoft.EntityFrameworkCore;
using TeleWave.Application.Common.Interfaces;
using TeleWave.Domain.Broadcast;
namespace TeleWave.Application.Streaming.ListPublicChannels;
public sealed class ListPublicChannelsQueryHandler(IAppDbContext dbContext)
: IQueryHandler<ListPublicChannelsQuery, IReadOnlyList<PublicChannelDto>>
{
public async Task<IReadOnlyList<PublicChannelDto>> Handle(
ListPublicChannelsQuery query,
CancellationToken cancellationToken
)
{
var channels = await dbContext
.Channels.AsNoTracking()
.Where(c => c.IsEnabled)
// Каналы без номера — в конец: на телевизоре порядок задаёт номер, а имя лишь
// разрешает ничью между ненумерованными.
.OrderBy(c => c.Number == null)
.ThenBy(c => c.Number)
.ThenBy(c => c.Name)
.Select(c => new
{
c.Id,
c.Slug,
c.Name,
c.Number,
c.LogoImageId,
c.LogoCorner,
c.LogoOpacity,
c.ShowClock,
c.AnalogFilterStrength,
})
.ToListAsync(cancellationToken);
if (channels.Count == 0)
return [];
var now = DateTimeOffset.UtcNow;
var channelIds = channels.Select(c => c.Id).ToList();
// Что идёт прямо сейчас на каждом канале (программа) — для постера-обложки плитки.
var currentByChannel = await dbContext
.ScheduleEntries.AsNoTracking()
.Where(e =>
channelIds.Contains(e.ChannelId)
&& e.Kind == ScheduleEntryKind.Program
&& e.StartsAtUtc <= now
&& e.EndsAtUtc > now
&& e.ShowId != null
)
.Select(e => new { e.ChannelId, ShowId = e.ShowId!.Value })
.ToListAsync(cancellationToken);
var currentShowByChannel = currentByChannel
.GroupBy(x => x.ChannelId)
.ToDictionary(g => g.Key, g => g.First().ShowId);
var showIds = currentShowByChannel.Values.Distinct().ToList();
var shows = await dbContext
.Shows.AsNoTracking()
.Where(s => showIds.Contains(s.Id))
.Select(s => new
{
s.Id,
s.Name,
s.PosterImageId,
})
.ToDictionaryAsync(s => s.Id, cancellationToken);
return channels
.Select(c =>
{
var showId = currentShowByChannel.GetValueOrDefault(c.Id);
var show = showId != Guid.Empty ? shows.GetValueOrDefault(showId) : null;
return new PublicChannelDto(
c.Id,
c.Slug,
c.Name,
c.Number,
show is null ? null : showId,
show?.Name,
show?.PosterImageId,
c.LogoImageId,
c.LogoCorner,
c.LogoOpacity,
c.ShowClock,
c.AnalogFilterStrength
);
})
.ToList();
}
}
using LiteCqrs;
using Microsoft.EntityFrameworkCore;
using TeleWave.Application.Common.Interfaces;
using TeleWave.Domain.Broadcast;
namespace TeleWave.Application.Streaming.ListPublicChannels;
public sealed class ListPublicChannelsQueryHandler(IAppDbContext dbContext)
: IQueryHandler<ListPublicChannelsQuery, IReadOnlyList<PublicChannelDto>>
{
public async Task<IReadOnlyList<PublicChannelDto>> Handle(
ListPublicChannelsQuery query,
CancellationToken cancellationToken
)
{
var channels = await dbContext
.Channels.AsNoTracking()
.Where(c => c.IsEnabled)
// Каналы без номера — в конец: на телевизоре порядок задаёт номер, а имя лишь
// разрешает ничью между ненумерованными.
.OrderBy(c => c.Number == null)
.ThenBy(c => c.Number)
.ThenBy(c => c.Name)
.Select(c => new
{
c.Id,
c.Slug,
c.Name,
c.Number,
c.LogoImageId,
c.LogoCorner,
c.LogoOpacity,
c.ShowClock,
c.AnalogFilterStrength,
})
.ToListAsync(cancellationToken);
if (channels.Count == 0)
return [];
var now = DateTimeOffset.UtcNow;
var channelIds = channels.Select(c => c.Id).ToList();
// Что идёт прямо сейчас на каждом канале (программа) — для постера-обложки плитки.
var currentByChannel = await dbContext
.ScheduleEntries.AsNoTracking()
.Where(e =>
channelIds.Contains(e.ChannelId)
&& e.Kind == ScheduleEntryKind.Program
&& e.StartsAtUtc <= now
&& e.EndsAtUtc > now
&& e.ShowId != null
)
.Select(e => new { e.ChannelId, ShowId = e.ShowId!.Value })
.ToListAsync(cancellationToken);
var currentShowByChannel = currentByChannel
.GroupBy(x => x.ChannelId)
.ToDictionary(g => g.Key, g => g.First().ShowId);
var showIds = currentShowByChannel.Values.Distinct().ToList();
var shows = await dbContext
.Shows.AsNoTracking()
.Where(s => showIds.Contains(s.Id))
.Select(s => new
{
s.Id,
s.Name,
s.PosterImageId,
})
.ToDictionaryAsync(s => s.Id, cancellationToken);
return channels
.Select(c =>
{
var showId = currentShowByChannel.GetValueOrDefault(c.Id);
var show = showId != Guid.Empty ? shows.GetValueOrDefault(showId) : null;
return new PublicChannelDto(
c.Id,
c.Slug,
c.Name,
c.Number,
show is null ? null : showId,
show?.Name,
show?.PosterImageId,
c.LogoImageId,
c.LogoCorner,
c.LogoOpacity,
c.ShowClock,
c.AnalogFilterStrength
);
})
.ToList();
}
}
@@ -1,42 +1,42 @@
using TeleWave.Domain.Broadcast;
namespace TeleWave.Application.Streaming;
public sealed record PublicChannelDto(
Guid Id,
string Slug,
string Name,
/// <summary>Номер канала для переключения по номерам или null, если не задан.</summary>
int? Number,
Guid? CurrentShowId,
string? CurrentShowName,
Guid? CurrentShowPosterImageId,
/// <summary>Что рисовать поверх картинки — оверлеи считает клиент, ffmpeg их не касается.</summary>
Guid? LogoImageId,
LogoCorner LogoCorner,
double LogoOpacity,
bool ShowClock,
double AnalogFilterStrength
);
/// <summary>Запись публичного телегида с метаданными (без имён файлов и номеров серий).</summary>
public sealed record PublicEpgEntryDto(
ScheduleEntryKind Kind,
DateTimeOffset StartsAtUtc,
DateTimeOffset EndsAtUtc,
Guid? ShowId,
string? ShowName,
Guid? ShowPosterImageId,
Guid? EpisodeId,
string? EpisodeTitle,
string? EpisodeOverview,
Guid? EpisodeStillImageId
);
public sealed record LiveSegmentDto(Guid AssetId, int LocalIndex, bool Discontinuity);
public sealed record LivePlaylistDto(
long MediaSequence,
int TargetDuration,
IReadOnlyList<LiveSegmentDto> Segments
);
using TeleWave.Domain.Broadcast;
namespace TeleWave.Application.Streaming;
public sealed record PublicChannelDto(
Guid Id,
string Slug,
string Name,
/// <summary>Номер канала для переключения по номерам или null, если не задан.</summary>
int? Number,
Guid? CurrentShowId,
string? CurrentShowName,
Guid? CurrentShowPosterImageId,
/// <summary>Что рисовать поверх картинки — оверлеи считает клиент, ffmpeg их не касается.</summary>
Guid? LogoImageId,
LogoCorner LogoCorner,
double LogoOpacity,
bool ShowClock,
double AnalogFilterStrength
);
/// <summary>Запись публичного телегида с метаданными (без имён файлов и номеров серий).</summary>
public sealed record PublicEpgEntryDto(
ScheduleEntryKind Kind,
DateTimeOffset StartsAtUtc,
DateTimeOffset EndsAtUtc,
Guid? ShowId,
string? ShowName,
Guid? ShowPosterImageId,
Guid? EpisodeId,
string? EpisodeTitle,
string? EpisodeOverview,
Guid? EpisodeStillImageId
);
public sealed record LiveSegmentDto(Guid AssetId, int LocalIndex, bool Discontinuity);
public sealed record LivePlaylistDto(
long MediaSequence,
int TargetDuration,
IReadOnlyList<LiveSegmentDto> Segments
);