37
37
cli . parse ( process . argv ) ;
38
38
const options = cli . opts ( ) as { [ key : string ] : any } ;
39
39
40
+ // Helper to mimic Perl dd_progid emission: (..########)(####) -> XX########.####
41
+ function toDdProgid ( rawId : string | undefined | null ) : string | null {
42
+ if ( ! rawId ) return null ;
43
+ const m = rawId . match ( / ^ ( .{ 2 } \d { 8 } ) ( \d { 4 } ) $ / ) ;
44
+ return m ? `${ m [ 1 ] } .${ m [ 2 ] } ` : null ;
45
+ }
46
+
40
47
export function buildChannelsXml ( data : GridApiResponse ) : string {
41
48
let xml = "" ;
42
49
@@ -156,29 +163,26 @@ export function buildProgramsXml(data: GridApiResponse): string {
156
163
xml += ` <icon src="${ escapeXml ( src ) } " />\n` ;
157
164
}
158
165
159
- // Optional series link
160
- if ( event . program . seriesId && event . program . tmsId ) {
161
- const encodedUrl = `https://tvlistings.gracenote.com//overview.html?programSeriesId=${ event . program . seriesId } &tmsId=${ event . program . tmsId } ` ;
166
+ if ( event . program . seriesId && ( event . program as any ) . tmsId ) {
167
+ const encodedUrl = `https://tvlistings.gracenote.com//overview.html?programSeriesId=${ event . program . seriesId } &tmsId=${ ( event . program as any ) . tmsId } ` ;
162
168
xml += ` <url>${ encodedUrl } </url>\n` ;
163
169
}
164
170
165
171
const skipXmltvNs = genreSet . has ( "movie" ) || genreSet . has ( "sports" ) ;
166
172
const episodeNumTags : string [ ] = [ ] ;
167
173
174
+ // ---- dd_progid (Perl behavior) — compute once, independent of season/episode presence
175
+ const ddProgid = toDdProgid ( event . program . id ) ;
176
+ if ( ddProgid ) {
177
+ episodeNumTags . push ( ` <episode-num system="dd_progid">${ escapeXml ( ddProgid ) } </episode-num>\n` ) ;
178
+ }
179
+ // ----------------------------------------------------------------------
180
+
168
181
if ( event . program . season && event . program . episode && ! skipXmltvNs ) {
169
- const onscreen = `S${ event . program . season . padStart ( 2 , "0" ) } E${ event . program . episode . padStart (
170
- 2 ,
171
- "0"
172
- ) } `;
182
+ const onscreen = `S${ event . program . season . padStart ( 2 , "0" ) } E${ event . program . episode . padStart ( 2 , "0" ) } ` ;
173
183
episodeNumTags . push ( ` <episode-num system="onscreen">${ escapeXml ( onscreen ) } </episode-num>\n` ) ;
174
184
episodeNumTags . push ( ` <episode-num system="common">${ escapeXml ( onscreen ) } </episode-num>\n` ) ;
175
185
176
- if ( / \. \d { 8 } \d { 4 } / . test ( event . program . id ) ) {
177
- episodeNumTags . push (
178
- ` <episode-num system="dd_progid">${ escapeXml ( event . program . id ) } </episode-num>\n`
179
- ) ;
180
- }
181
-
182
186
const seasonNum = parseInt ( event . program . season , 10 ) ;
183
187
const episodeNum = parseInt ( event . program . episode , 10 ) ;
184
188
if ( ! isNaN ( seasonNum ) && ! isNaN ( episodeNum ) && seasonNum >= 1 && episodeNum >= 1 ) {
@@ -208,13 +212,7 @@ export function buildProgramsXml(data: GridApiResponse): string {
208
212
}
209
213
}
210
214
} else if ( ! event . program . episode && event . program . id ) {
211
- const match = event . program . id . match ( / ^ ( .\d { 8 } ) ( \d { 4 } ) / ) ;
212
- if ( match ) {
213
- episodeNumTags . push (
214
- ` <episode-num system="dd_progid">${ match [ 1 ] } .${ match [ 2 ] } </episode-num>\n`
215
- ) ;
216
- }
217
-
215
+ // No season/episode — xmltv_ns based on MMDD (only if not movie/sports)
218
216
const nyFormatter = new Intl . DateTimeFormat ( "en-US" , {
219
217
timeZone : "America/New_York" ,
220
218
year : "numeric" ,
@@ -241,9 +239,7 @@ export function buildProgramsXml(data: GridApiResponse): string {
241
239
xml += episodeNumTags . join ( "" ) ;
242
240
243
241
if ( event . program . originalAirDate || event . program . episodeAirDate ) {
244
- const airDate = new Date (
245
- event . program . episodeAirDate || event . program . originalAirDate || ""
246
- ) ;
242
+ const airDate = new Date ( event . program . episodeAirDate || event . program . originalAirDate || "" ) ;
247
243
if ( ! isNaN ( airDate . getTime ( ) ) ) {
248
244
xml += ` <episode-num system="original-air-date">${ airDate
249
245
. toISOString ( )
@@ -290,8 +286,7 @@ export function buildXmltv(data: GridApiResponse): string {
290
286
console . log ( "Building XMLTV file" ) ;
291
287
292
288
let xml = '<?xml version="1.0" encoding="UTF-8"?>\n' ;
293
- xml +=
294
- '<tv generator-info-name="jef/zap2xml" generator-info-url="https://github.com/jef/zap2xml">\n' ;
289
+ xml += '<tv generator-info-name="jef/zap2xml" generator-info-url="https://github.com/jef/zap2xml">\n' ;
295
290
xml += buildChannelsXml ( data ) ;
296
291
xml += buildProgramsXml ( data ) ;
297
292
xml += "</tv>\n" ;
0 commit comments