mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-13 20:56:19 -08:00
* chore(cleanup): delete nine dead components, orphaned i18n keys and unused deps Removes verified-dead components (0 class/selector references outside their own files): EpgListComponent (+ epg-list-item), EpgViewComponent, LiveEpgPanelComponent, StalkerCollectionChannelsListComponent, NavigationComponent, FilterSortMenuComponent, video-player ToolbarComponent, PortalCollectionShellComponent and LoadingOverlayComponent, together with their barrel exports. Alive code extracted from the deleted trees: - LiveEpgPanelSummary -> libs/ui/shared-portals/src/lib/live-epg-panel-summary.ts - EpgProgramActivationEvent -> libs/ui/epg/src/lib/epg-program-activation-event.ts - epg-list.utils.ts trimmed to the three timeline-used helpers and moved to libs/ui/epg/src/lib/epg-program.utils.ts - epg-item-description/ moved up out of the deleted epg-list/ folder Also removes 18 i18n keys now unused (from all 18 locales), dead CSS selectors targeting the deleted elements, and unused dependencies: lodash (+ @types/lodash), semver, @ngrx/component-store and @videojs/http-streaming (videojs-quality-selector-hls declares no peer dependency on it; video.js 8 bundles VHS). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore(coverage): move shared-portals to Tier C, fix stale doc references The Tier A gate failed in CI because deleting the dead epg-view and live-epg-panel components removed the only specs in libs/ui/shared-portals. The lib now contains a single type-only interface (LiveEpgPanelSummary), so there is no runtime code to unit test; reclassify it to Tier C with a documented reason, matching the gate's own guidance. Also update remaining doc references to the deleted components in docs/architecture/stalker-epg.md, iptvnator-ui-guidelines.md and CLAUDE.md. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
93 lines
2.6 KiB
TypeScript
93 lines
2.6 KiB
TypeScript
import { format } from 'date-fns';
|
|
import type { EpgProgram } from '@iptvnator/shared/interfaces';
|
|
import { EPG_DATE_KEY_FORMAT } from './epg-date';
|
|
|
|
export function getProgramTimeMs(
|
|
isoValue: string,
|
|
timestampValue?: number | null
|
|
): number {
|
|
if (Number.isFinite(timestampValue) && Number(timestampValue) > 0) {
|
|
return Number(timestampValue) * 1000;
|
|
}
|
|
|
|
return Date.parse(isoValue);
|
|
}
|
|
|
|
export function getProgramDateKey(
|
|
isoValue: string,
|
|
timestampValue?: number | null
|
|
): string {
|
|
const programTimeMs = getProgramTimeMs(isoValue, timestampValue);
|
|
|
|
if (!Number.isFinite(programTimeMs)) {
|
|
return '';
|
|
}
|
|
|
|
return format(new Date(programTimeMs), EPG_DATE_KEY_FORMAT);
|
|
}
|
|
|
|
export function deduplicateProgramsByTimeSlot(
|
|
programs: EpgProgram[]
|
|
): EpgProgram[] {
|
|
const programsByTimeSlot = new Map<string, EpgProgram>();
|
|
|
|
for (const program of programs) {
|
|
const timeSlotKey = buildProgramTimeSlotKey(program);
|
|
const existingProgram = programsByTimeSlot.get(timeSlotKey);
|
|
|
|
programsByTimeSlot.set(
|
|
timeSlotKey,
|
|
existingProgram
|
|
? selectMoreInformativeProgram(existingProgram, program)
|
|
: program
|
|
);
|
|
}
|
|
|
|
return Array.from(programsByTimeSlot.values());
|
|
}
|
|
|
|
export function areProgramsSame(
|
|
left: EpgProgram,
|
|
right: EpgProgram
|
|
): boolean {
|
|
return (
|
|
(left.channel ?? '') === (right.channel ?? '') &&
|
|
getProgramTimeMs(left.start, left.startTimestamp) ===
|
|
getProgramTimeMs(right.start, right.startTimestamp) &&
|
|
getProgramTimeMs(left.stop, left.stopTimestamp) ===
|
|
getProgramTimeMs(right.stop, right.stopTimestamp)
|
|
);
|
|
}
|
|
|
|
function buildProgramTimeSlotKey(program: EpgProgram): string {
|
|
return [
|
|
getProgramTimeMs(program.start, program.startTimestamp),
|
|
getProgramTimeMs(program.stop, program.stopTimestamp),
|
|
].join('|');
|
|
}
|
|
|
|
function selectMoreInformativeProgram(
|
|
existingProgram: EpgProgram,
|
|
candidateProgram: EpgProgram
|
|
): EpgProgram {
|
|
return getProgramMetadataScore(candidateProgram) >
|
|
getProgramMetadataScore(existingProgram)
|
|
? candidateProgram
|
|
: existingProgram;
|
|
}
|
|
|
|
function getProgramMetadataScore(program: EpgProgram): number {
|
|
return (
|
|
getTextScore(program.desc) * 8 +
|
|
getTextScore(program.category) * 4 +
|
|
getTextScore(program.iconUrl) * 2 +
|
|
getTextScore(program.rating) * 2 +
|
|
getTextScore(program.episodeNum) * 2 +
|
|
getTextScore(program.title)
|
|
);
|
|
}
|
|
|
|
function getTextScore(value: string | null | undefined): number {
|
|
return value?.trim() ? 1 : 0;
|
|
}
|