Refactor IPTV endpoint logic for improved readability and maintainability
ci / build-backend (push) Successful in 1m34s
ci / build-frontend (push) Successful in 55s
ci / tests (push) Successful in 1m43s
ci / sonar (push) Successful in 4m38s

Consolidated channel and programme XML writing into dedicated methods, WriteChannel and WriteProgramme, enhancing code clarity and reducing duplication. Introduced a WriteIcon method to streamline icon handling for both channels and programmes. Updated GetChannelScheduleQueryHandler to utilize a new FranchisePosition record for better representation of franchise details in schedule entries, improving the overall structure and readability of the code.
This commit is contained in:
Leonid Pershin
2026-07-30 01:37:20 +03:00
parent 8119305c36
commit bd89abf24b
3 changed files with 91 additions and 69 deletions
@@ -97,13 +97,12 @@ public sealed class GetChannelScheduleQueryHandler(IAppDbContext dbContext)
join collection in dbContext.Collections.AsNoTracking()
on item.CollectionId equals collection.Id
where collectionIds.Contains(item.CollectionId)
select new
{
select new CollectionPart(
item.CollectionId,
item.ShowId,
item.Position,
collection.Name,
}
collection.Name
)
).ToListAsync(cancellationToken);
var partsByCollection = collectionParts
.GroupBy(p => p.CollectionId)
@@ -120,25 +119,7 @@ public sealed class GetChannelScheduleQueryHandler(IAppDbContext dbContext)
bumperText = BumperText(bumper.RenderedLinesJson);
}
// Номер части ищем по позиции шоу в коллекции, а не по индексу записи: франшизу могли
// переупорядочить, и «часть 2» обязана означать вторую в текущем составе.
int? part = null;
int? parts = null;
string? collectionName = null;
if (
e.CollectionId is { } collectionId
&& e.ShowId is { } showId
&& partsByCollection.TryGetValue(collectionId, out var ordered)
)
{
var at = ordered.FindIndex(p => p.ShowId == showId);
if (at >= 0)
{
part = at + 1;
parts = ordered.Count;
collectionName = ordered[0].Name;
}
}
var franchise = FranchisePart(partsByCollection, e.CollectionId, e.ShowId);
dtos.Add(
new ScheduleEntryDto(
@@ -155,9 +136,9 @@ public sealed class GetChannelScheduleQueryHandler(IAppDbContext dbContext)
: null,
bumperName,
bumperText,
part,
parts,
collectionName
franchise?.Part,
franchise?.Total,
franchise?.Name
)
);
}
@@ -165,6 +146,33 @@ public sealed class GetChannelScheduleQueryHandler(IAppDbContext dbContext)
return Result.Success<IReadOnlyList<ScheduleEntryDto>>(dtos);
}
/// <summary>Место записи во франшизе: «часть N из M» и её название.</summary>
private sealed record FranchisePosition(int Part, int Total, string Name);
/// <summary>
/// Какой частью коллекции шла запись. Номер ищется по позиции шоу в текущем составе, а не по
/// индексу записи: франшизу могли переупорядочить, и «часть 2» обязана означать вторую сейчас.
/// </summary>
private static FranchisePosition? FranchisePart(
IReadOnlyDictionary<Guid, List<CollectionPart>> partsByCollection,
Guid? collectionId,
Guid? showId
)
{
if (
collectionId is not { } id
|| showId is not { } show
|| !partsByCollection.TryGetValue(id, out var ordered)
)
return null;
var at = ordered.FindIndex(p => p.ShowId == show);
return at < 0 ? null : new FranchisePosition(at + 1, ordered.Count, ordered[0].Name);
}
/// <summary>Позиция шоу в коллекции вместе с её названием — то, чем считается номер части.</summary>
private sealed record CollectionPart(Guid CollectionId, Guid ShowId, int Position, string Name);
/// <summary>Строки сыгравшей заставки одной меткой для расписания; null — показывать нечего.</summary>
private static string? BumperText(string? renderedLinesJson)
{
@@ -102,11 +102,10 @@ public sealed class GetIptvGuideQueryHandler(IAppDbContext dbContext)
// Серии одного шоу встык — это один блок программы. Склеиваем по id, а не по
// названию: у разных шоу оно может совпасть. Разрыв во времени блок рвёт — между
// выходами шла реклама, и объявлять это одной передачей нельзя.
if (
lastShowId == entry.ShowId
&& programmes.Count > 0
&& programmes[^1].EndsAtUtc == entry.StartsAtUtc
)
//
// Непустоту списка не проверяем: lastShowId выставляется только вместе с добавленной
// передачей, и совпасть с ним на пустом списке нечему.
if (lastShowId == entry.ShowId && programmes[^1].EndsAtUtc == entry.StartsAtUtc)
{
programmes[^1] = programmes[^1] with { EndsAtUtc = entry.EndsAtUtc };
continue;