@@ -16,63 +16,108 @@ interface Config {
1616 dataDir ?: string
1717}
1818
19- export async function loadData ( config : Config = { } ) {
19+ type ProcessedData = {
20+ countries : Country [ ]
21+ channels : Channel [ ]
22+ feeds : Feed [ ]
23+ streams : Stream [ ]
24+ channelsKeyById : Map < string , Channel >
25+ }
26+
27+ export let processedData : ProcessedData | undefined
28+
29+ export async function loadData ( config : Config = { } ) : Promise < ProcessedData > {
2030 const dataDir = config . dataDir || DATA_DIR
2131
2232 const dataManager = new sdk . DataManager ( { dataDir } )
2333 await dataManager . downloadToMemory ( )
2434 dataManager . loadFromMemory ( )
2535 const rawData = dataManager . getRawData ( )
36+ processedData = processData ( rawData )
2637
27- return processData ( rawData )
38+ return processedData
2839}
2940
30- export async function loadDataFromDisk ( config : Config = { } ) {
41+ export async function loadDataFromDisk ( config : Config = { } ) : Promise < ProcessedData > {
3142 const dataDir = config . dataDir || DATA_DIR
3243
3344 const dataManager = new sdk . DataManager ( { dataDir } )
3445 await dataManager . loadFromDisk ( )
3546 const rawData = dataManager . getRawData ( )
47+ processedData = processData ( rawData )
3648
37- return processData ( rawData )
49+ return processedData
3850}
3951
40- function processData ( rawData : sdk . Types . RawData ) {
41- const feedsKeyByStreamId = rawData . feeds . reduce ( ( acc , data : sdk . Types . FeedData ) => {
42- const feed = new Feed ( data )
43- acc . set ( feed . getStreamId ( ) , feed )
44- return acc
45- } , new Map ( ) )
46- const logosGroupedByChannel2 = Map . groupBy (
47- rawData . logos . map ( ( data : sdk . Types . LogoData ) => new Logo ( data ) ) ,
52+ function processData ( rawData : sdk . Types . RawData ) : ProcessedData {
53+ const channelsGroupedByName = Map . groupBy (
54+ rawData . channels ,
55+ ( channel : sdk . Types . ChannelData ) => channel . name
56+ )
57+ const feedsKeyByStreamId : Map < string , Feed > = rawData . feeds . reduce (
58+ ( acc , data : sdk . Types . FeedData ) => {
59+ const feed = new Feed ( data )
60+ acc . set ( feed . getStreamId ( ) , feed )
61+ return acc
62+ } ,
63+ new Map ( )
64+ )
65+ const channelsKeyById2 : Map < string , Channel > = rawData . channels . reduce (
66+ ( acc , data : sdk . Types . ChannelData ) => {
67+ const channel = new Channel ( data )
68+ channel . hasUniqueName = channelsGroupedByName . get ( channel . name ) . length === 1
69+ acc . set ( channel . id , channel )
70+ return acc
71+ } ,
72+ new Map ( )
73+ )
74+ const logosGroupedByChannel2 : Map < string , Logo [ ] > = Map . groupBy (
75+ rawData . logos . map ( ( data : sdk . Types . LogoData ) =>
76+ new Logo ( data ) . withChannel ( channelsKeyById2 . get ( data . channel ) )
77+ ) ,
4878 ( logo : Logo ) => logo . channel
4979 )
50- const categoriesKeyById = rawData . categories . reduce ( ( acc , data : sdk . Types . CategoryData ) => {
51- const category = new sdk . Models . Category ( data )
52- acc . set ( category . id , category )
53- return acc
54- } , new Map ( ) )
55- const countriesKeyByCode = rawData . countries . reduce ( ( acc , data : sdk . Types . CountryData ) => {
56- const country = new Country ( data )
57- acc . set ( country . code , country )
58- return acc
59- } , new Map ( ) )
60- const channelsKeyById = rawData . channels . reduce ( ( acc , data : sdk . Types . ChannelData ) => {
61- const channel = new Channel ( data )
62- const logos = logosGroupedByChannel2 . get ( channel . id )
63- const categories = channel . categories . map ( ( id : string ) => categoriesKeyById . get ( id ) )
64- const country = countriesKeyByCode . get ( channel . country )
65- channel . withLogos ( logos ) . withCategories ( categories ) . withCountry ( country )
66- acc . set ( channel . id , channel )
67- return acc
68- } , new Map ( ) )
80+ const categoriesKeyById : Map < string , sdk . Models . Category > = rawData . categories . reduce (
81+ ( acc , data : sdk . Types . CategoryData ) => {
82+ const category = new sdk . Models . Category ( data )
83+ acc . set ( category . id , category )
84+ return acc
85+ } ,
86+ new Map ( )
87+ )
88+ const countriesKeyByCode : Map < string , Country > = rawData . countries . reduce (
89+ ( acc , data : sdk . Types . CountryData ) => {
90+ const country = new Country ( data )
91+ acc . set ( country . code , country )
92+ return acc
93+ } ,
94+ new Map ( )
95+ )
96+ const channelsKeyById : Map < string , Channel > = rawData . channels . reduce (
97+ ( acc , data : sdk . Types . ChannelData ) => {
98+ const channel = new Channel ( data )
99+ const logos = logosGroupedByChannel2 . get ( channel . id )
100+ const categories = channel . categories . map ( ( id : string ) => categoriesKeyById . get ( id ) )
101+ const country = countriesKeyByCode . get ( channel . country )
102+ channel . withLogos ( logos ) . withCategories ( categories ) . withCountry ( country )
103+ channel . hasUniqueName = channelsGroupedByName . get ( channel . name ) . length === 1
104+ acc . set ( channel . id , channel )
105+ return acc
106+ } ,
107+ new Map ( )
108+ )
69109
70110 const logos = rawData . logos . map ( ( data : sdk . Types . LogoData ) => {
71111 const logo = new Logo ( data )
112+ const feed = feedsKeyByStreamId . get ( logo . getStreamId ( ) )
113+ const channel = channelsKeyById . get ( logo . channel )
114+
115+ if ( feed && channel ) feed . withChannel ( channel )
116+
117+ if ( feed ) logo . withFeed ( feed )
118+ if ( channel ) logo . withChannel ( channel )
72119
73120 return logo
74- . withFeed ( feedsKeyByStreamId . get ( logo . getStreamId ( ) ) )
75- . withChannel ( channelsKeyById . get ( logo . channel ) )
76121 } )
77122
78123 const logosGroupedByStreamId = Map . groupBy ( logos , ( logo : Logo ) => logo . getStreamId ( ) )
@@ -100,7 +145,7 @@ function processData(rawData: sdk.Types.RawData) {
100145 acc . set ( city . code , city )
101146 return acc
102147 } , new Map ( ) )
103- const subdivisionsKeyByCode = rawData . subdivisions . reduce (
148+ const subdivisionsKeyByCode : Map < string , sdk . Models . Subdivision > = rawData . subdivisions . reduce (
104149 ( acc , data : sdk . Types . SubdivisionData ) => {
105150 const subdivision = new sdk . Models . Subdivision ( data )
106151 acc . set ( subdivision . code , subdivision )
@@ -109,16 +154,22 @@ function processData(rawData: sdk.Types.RawData) {
109154 new Map ( )
110155 )
111156
112- const regionsKeyByCode = rawData . regions . reduce ( ( acc , data : sdk . Types . RegionData ) => {
113- const region = new sdk . Models . Region ( data )
114- acc . set ( region . code , region )
115- return acc
116- } , new Map ( ) )
117- const timezonesKeyById = rawData . timezones . reduce ( ( acc , data : sdk . Types . TimezoneData ) => {
118- const timezone = new sdk . Models . Timezone ( data )
119- acc . set ( timezone . id , timezone )
120- return acc
121- } , new Map ( ) )
157+ const regionsKeyByCode : Map < string , sdk . Models . Region > = rawData . regions . reduce (
158+ ( acc , data : sdk . Types . RegionData ) => {
159+ const region = new sdk . Models . Region ( data )
160+ acc . set ( region . code , region )
161+ return acc
162+ } ,
163+ new Map ( )
164+ )
165+ const timezonesKeyById : Map < string , sdk . Models . Timezone > = rawData . timezones . reduce (
166+ ( acc , data : sdk . Types . TimezoneData ) => {
167+ const timezone = new sdk . Models . Timezone ( data )
168+ acc . set ( timezone . id , timezone )
169+ return acc
170+ } ,
171+ new Map ( )
172+ )
122173 const feeds = rawData . feeds . map ( ( data : sdk . Types . FeedData ) => {
123174 const feed = new Feed ( data )
124175
@@ -130,7 +181,7 @@ function processData(rawData: sdk.Types.RawData) {
130181 const streams = streamsGroupedById . get ( feed . getStreamId ( ) )
131182 const guides = guidesGroupedByStreamId . get ( feed . getStreamId ( ) )
132183 const locations = feed . broadcast_area . map ( ( rawCode : string ) => {
133- let name
184+ let name : string | undefined
134185 const [ type , code ] = rawCode . split ( '/' )
135186 switch ( type ) {
136187 case 'ct' : {
@@ -170,6 +221,16 @@ function processData(rawData: sdk.Types.RawData) {
170221 . withChannel ( channel )
171222 } )
172223
224+ const graph = buildGraph ( rawData . channels )
225+
226+ const edgesTo = new Map < string , string [ ] > ( )
227+ const edgesFrom = new Map < string , string > ( )
228+ for ( const edge of graph . edges ) {
229+ if ( ! edgesTo . has ( edge . to ) ) edgesTo . set ( edge . to , [ ] )
230+ edgesTo . get ( edge . to ) ! . push ( edge . from )
231+ edgesFrom . set ( edge . from , edge . to )
232+ }
233+
173234 const logosGroupedByChannel = Map . groupBy ( logos , ( logo : Logo ) => logo . channel )
174235 const feedsGroupedByChannel = Map . groupBy ( feeds , ( feed : Feed ) => feed . channel )
175236 const blocklistRecords = rawData . blocklist . map (
@@ -179,15 +240,28 @@ function processData(rawData: sdk.Types.RawData) {
179240 blocklistRecords ,
180241 ( record : BlocklistRecord ) => record . channel
181242 )
182- const channels = rawData . channels . map ( ( data : sdk . Types . ChannelData ) => {
183- const channel = new Channel ( data )
243+
244+ const finalChannelsKeyById = new Map < string , Channel > ( )
245+ const channels = rawData . channels . map ( ( channelData : sdk . Types . ChannelData ) => {
246+ const channel = new Channel ( channelData )
184247 const categories = channel . categories . map ( ( id : string ) => categoriesKeyById . get ( id ) )
185- return channel
248+ const history = getChannelHistory ( channel . id , edgesTo , edgesFrom , channelsKeyById )
249+
250+ channel
186251 . withLogos ( logosGroupedByChannel . get ( channel . id ) )
187252 . withFeeds ( feedsGroupedByChannel . get ( channel . id ) )
188253 . withCategories ( categories )
189254 . withCountry ( countriesKeyByCode . get ( channel . country ) )
190255 . withBlocklistRecords ( blocklistRecordsGroupedByChannel . get ( channel . id ) )
256+
257+ if ( history . length > 1 ) {
258+ channel . withHistory ( history )
259+ }
260+
261+ channel . hasUniqueName = channelsGroupedByName . get ( channel . name ) . length === 1
262+
263+ finalChannelsKeyById . set ( channel . id , channel )
264+ return channel
191265 } )
192266
193267 const channelsGroupedByCountry = Map . groupBy ( channels , ( channel : Channel ) => channel . country )
@@ -200,6 +274,77 @@ function processData(rawData: sdk.Types.RawData) {
200274 countries,
201275 channels,
202276 feeds,
203- streams
277+ streams,
278+ channelsKeyById : finalChannelsKeyById
279+ }
280+ }
281+
282+ function buildGraph ( channels : sdk . Types . ChannelData [ ] ) {
283+ const graph = {
284+ edges : [ ] ,
285+ nodes : [ ]
204286 }
287+
288+ channels . forEach ( ( channel : sdk . Types . ChannelData ) => {
289+ graph . nodes . push ( { id : channel . id , label : channel . name } )
290+
291+ if ( channel . replaced_by ) {
292+ const target = channel . replaced_by . split ( '@' ) [ 0 ]
293+
294+ if ( target !== channel . id ) {
295+ graph . edges . push ( { from : channel . id , to : target } )
296+ }
297+ }
298+ } )
299+
300+ return graph
301+ }
302+
303+ function getChannelHistory (
304+ targetId : string ,
305+ edgesTo : Map < string , string [ ] > ,
306+ edgesFrom : Map < string , string > ,
307+ channelsKeyById : Map < string , Channel >
308+ ) : ( Channel | Channel [ ] ) [ ] {
309+ const visited = new Set < string > ( )
310+
311+ const getAncestors = ( id : string ) : ( Channel | Channel [ ] ) [ ] => {
312+ if ( visited . has ( id ) ) return [ ]
313+ visited . add ( id )
314+
315+ const parentIds = edgesTo . get ( id ) || [ ]
316+ if ( parentIds . length === 0 ) return [ ]
317+
318+ if ( parentIds . length > 1 ) {
319+ const multiParents = parentIds
320+ . map ( pId => channelsKeyById . get ( pId ) )
321+ . filter ( ( p ) : p is Channel => ! ! p )
322+ return multiParents . length > 0 ? [ multiParents ] : [ ]
323+ }
324+
325+ const parentId = parentIds [ 0 ]
326+ const parentNode = channelsKeyById . get ( parentId )
327+ const ancestors = getAncestors ( parentId )
328+
329+ return parentNode ? [ ...ancestors , parentNode ] : ancestors
330+ }
331+
332+ const successors : Channel [ ] = [ ]
333+ const visitedSuccessors = new Set < string > ( [ targetId ] )
334+ let current = targetId
335+
336+ while ( true ) {
337+ const nextId = edgesFrom . get ( current )
338+ const nextNode = nextId ? channelsKeyById . get ( nextId ) : null
339+ if ( ! nextId || ! nextNode || visitedSuccessors . has ( nextId ) ) break
340+
341+ successors . push ( nextNode )
342+ visitedSuccessors . add ( nextId )
343+ current = nextId
344+ }
345+
346+ const targetNode = channelsKeyById . get ( targetId )
347+ const ancestors = getAncestors ( targetId )
348+
349+ return [ ...ancestors , targetNode , ...successors ] . filter ( ( n ) : n is Channel | Channel [ ] => ! ! n )
205350}
0 commit comments