Skip to content

Commit ee8c32d

Browse files
committed
feat: support upto 15 days of listings
- change default timespan to 6 - parallize tv listing chunks special thanks to @majortom9 #41 (comment)
1 parent 51f66a1 commit ee8c32d

File tree

4 files changed

+65
-37
lines changed

4 files changed

+65
-37
lines changed

README.md

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -44,27 +44,27 @@ See [Environment variables](#environment-variables) for configuration options.
4444
4545
### Environment variables
4646
47-
| Variable | Description | Type | Default |
48-
| ------------- | --------------------------------------------------------------------------------------------------------------- | ------- | -------------------------------- |
49-
| `LINEUP_ID` | Lineup ID; Read more in the [Wiki](https://github.com/jef/zap2xml/wiki/Retrieving-Lineup-ID) | String | `USA-lineupId-DEFAULT` (Attenna) |
50-
| `TIMESPAN` | Either 3 or 6 hours of shows | Integer | 3 |
51-
| `PREF` | User Preferences, comma separated list. `m` for showing music, `p` for showing pay-per-view, `h` for showing HD | String | (empty) |
52-
| `COUNTRY` | Country code (default: `US`) | String | US |
53-
| `POSTAL_CODE` | Postal code of where shows are available. | Integer | 30309 |
54-
| `USER_AGENT` | Custom user agent string for HTTP requests. | String | Uses random if not specified |
55-
| `TZ` | Timezone | String | System default |
56-
| `SLEEP_TIME` | Sleep time before next run in seconds (default: 10800, Only used with Docker.) | Integer | 10800 |
57-
| `OUTPUT_FILE` | Output file name (default: xmltv.xml) | String | xmltv.xml |
47+
| Variable | Description | Type | Default |
48+
| ------------- | --------------------------------------------------------------------------------------------------------------- | ------ | -------------------------------- |
49+
| `LINEUP_ID` | Lineup ID; Read more in the [Wiki](https://github.com/jef/zap2xml/wiki/Retrieving-Lineup-ID) | String | `USA-lineupId-DEFAULT` (Attenna) |
50+
| `TIMESPAN` | Either 3 or 6 hours of shows | String | 3 |
51+
| `PREF` | User Preferences, comma separated list. `m` for showing music, `p` for showing pay-per-view, `h` for showing HD | String | (empty) |
52+
| `COUNTRY` | Country code (default: `US`) | String | US |
53+
| `POSTAL_CODE` | Postal code of where shows are available. | String | 30309 |
54+
| `USER_AGENT` | Custom user agent string for HTTP requests. | String | Uses random if not specified |
55+
| `TZ` | Timezone | String | System default |
56+
| `SLEEP_TIME` | Sleep time before next run in seconds (default: 10800, Only used with Docker.) | String | 10800 |
57+
| `OUTPUT_FILE` | Output file name (default: xmltv.xml) | String | xmltv.xml |
5858

5959
### Command line arguments
6060

61-
| Argument | Description | Type | Default |
62-
| -------------- | --------------------------------------------------------------------------------------------------------------- | ------- | -------------------------------- |
63-
| `--lineupId` | Lineup ID; Read more in the [Wiki](https://github.com/jef/zap2xml/wiki/Retrieving-Lineup-ID) | String | `USA-lineupId-DEFAULT` (Attenna) |
64-
| `--timespan` | Either 3 or 6 hours of shows | Integer | 3 |
65-
| `--pref` | User Preferences, comma separated list. `m` for showing music, `p` for showing pay-per-view, `h` for showing HD | String | (empty) |
66-
| `--country` | Country code (default: `US`) | String | US |
67-
| `--postalCode` | Postal code of where shows are available. | Integer | 30309 |
68-
| `--userAgent` | Custom user agent string for HTTP requests. | String | Uses random if not specified |
69-
| `--timezone` | Timezone | String | System default |
70-
| `--outputFile` | Output file name (default: xmltv.xml) | String | xmltv.xml |
61+
| Argument | Description | Type | Default |
62+
| -------------- | --------------------------------------------------------------------------------------------------------------- | ------ | -------------------------------- |
63+
| `--lineupId` | Lineup ID; Read more in the [Wiki](https://github.com/jef/zap2xml/wiki/Retrieving-Lineup-ID) | String | `USA-lineupId-DEFAULT` (Attenna) |
64+
| `--timespan` | Either 3 or 6 hours of shows | String | 3 |
65+
| `--pref` | User Preferences, comma separated list. `m` for showing music, `p` for showing pay-per-view, `h` for showing HD | String | (empty) |
66+
| `--country` | Country code (default: `US`) | String | US |
67+
| `--postalCode` | Postal code of where shows are available. | String | 30309 |
68+
| `--userAgent` | Custom user agent string for HTTP requests. | String | Uses random if not specified |
69+
| `--timezone` | Timezone | String | System default |
70+
| `--outputFile` | Output file name (default: xmltv.xml) | String | xmltv.xml |

src/config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export const config = {
99
timespan:
1010
process.env["TIMESPAN"] ||
1111
process.argv.find((arg) => arg.startsWith("--timespan="))?.split("=")[1] ||
12-
"3",
12+
"6",
1313
country:
1414
process.env["COUNTRY"] ||
1515
process.argv.find((arg) => arg.startsWith("--country="))?.split("=")[1] ||

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Usage: node dist/index.js [options]
1111
Options:
1212
--help Show this help message
1313
--lineupId=ID Lineup ID (default: USA-lineupId-DEFAULT)
14-
--timespan=NUM Timespan in hours (default: 3)
14+
--timespan=NUM Timespan in hours (up to 360 = 15 days, default: 6)
1515
--pref=LIST User preferences, comma separated. Can be m, p, and h (default: empty)'
1616
--country=CON Country code (default: USA)
1717
--postalCode=ZIP Postal code (default: 30309)

src/tvlistings.ts

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,10 @@ export interface GridApiResponse {
7878
channels: Channel[];
7979
}
8080

81-
function buildUrl() {
81+
function buildUrl(time: number, timespan: number): string {
8282
const params = {
8383
lineupId: config.lineupId,
84-
timespan: config.timespan,
84+
timespan: timespan.toString(),
8585
headendId: "lineupId",
8686
country: config.country,
8787
timezone: config.timezone,
@@ -90,7 +90,7 @@ function buildUrl() {
9090
pref: config.pref + "16,128" || "16,128",
9191
aid: "orbebb",
9292
languagecode: "en-us",
93-
time: Math.floor(Date.now() / 1000).toString(),
93+
time: time.toString(),
9494
};
9595

9696
const urlParams = new URLSearchParams(params).toString();
@@ -101,19 +101,47 @@ function buildUrl() {
101101
export async function getTVListings(): Promise<GridApiResponse> {
102102
console.log("Fetching TV listings");
103103

104-
const url = buildUrl();
104+
const totalHours = parseInt(config.timespan, 10);
105+
const chunkHours = 6; // Gracenote allows up to 6 hours per request
106+
const now = Math.floor(Date.now() / 1000); // Current time in UNIX timestamp
107+
const channelsMap: Map<string, Channel> = new Map();
105108

106-
const response = await fetch(url, {
107-
headers: {
108-
"User-Agent": config.userAgent || "",
109-
},
110-
});
109+
const fetchPromises: Promise<void>[] = [];
111110

112-
if (!response.ok) {
113-
throw new Error(
114-
`Failed to fetch: ${response.status} ${response.statusText}`,
115-
);
111+
for (let offset = 0; offset < totalHours; offset += chunkHours) {
112+
const time = now + offset * 3600;
113+
const url = buildUrl(time, chunkHours);
114+
115+
const fetchPromise = fetch(url, {
116+
headers: {
117+
"User-Agent": config.userAgent || "",
118+
},
119+
}).then(async (response) => {
120+
if (!response.ok) {
121+
throw new Error(
122+
`Failed to fetch: ${response.status} ${response.statusText}`
123+
);
124+
}
125+
const chunkData = (await response.json()) as GridApiResponse;
126+
127+
for (const newChannel of chunkData.channels) {
128+
if (!channelsMap.has(newChannel.channelId)) {
129+
// Clone channel with its events
130+
channelsMap.set(newChannel.channelId, {
131+
...newChannel,
132+
events: [...newChannel.events],
133+
});
134+
} else {
135+
const existingChannel = channelsMap.get(newChannel.channelId)!;
136+
existingChannel.events.push(...newChannel.events);
137+
}
138+
}
139+
});
140+
141+
fetchPromises.push(fetchPromise);
116142
}
117143

118-
return (await response.json()) as GridApiResponse;
144+
await Promise.all(fetchPromises);
145+
146+
return { channels: Array.from(channelsMap.values()) };
119147
}

0 commit comments

Comments
 (0)