Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 23 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,30 +41,32 @@ See [Environment variables](#environment-variables) for configuration options.

### Environment variables

| Variable | Description | Default |
| ------------- | --------------------------------------------------------------------------------------------------------------- | -------------------------------- |
| `LINEUP_ID` | Lineup ID; Read more in the [Wiki](https://github.com/jef/zap2xml/wiki/Retrieving-Lineup-ID) | `USA-lineupId-DEFAULT` (Attenna) |
| `TIMESPAN` | Timespan in hours (up to 360 = 15 days, default: 6) | 6 |
| `PREF` | User Preferences, comma separated list. `m` for showing music, `p` for showing pay-per-view, `h` for showing HD | (empty) |
| `COUNTRY` | Country code (default: `USA`) | USA |
| `POSTAL_CODE` | Postal code of where shows are available. | 30309 |
| `USER_AGENT` | Custom user agent string for HTTP requests. | Uses random if not specified |
| `TZ` | Timezone | System default |
| `SLEEP_TIME` | Sleep time before next run in seconds (default: 21600, Only used with Docker.) | 21600 |
| `OUTPUT_FILE` | Output file name (default: xmltv.xml) | xmltv.xml |
| Variable | Description | Default |
| ------------------------- | --------------------------------------------------------------------------------------------------------------- | -------------------------------- |
| `LINEUP_ID` | Lineup ID; Read more in the [Wiki](https://github.com/jef/zap2xml/wiki/Retrieving-Lineup-ID) | `USA-lineupId-DEFAULT` (Attenna) |
| `TIMESPAN` | Timespan in hours (up to 360 = 15 days, default: 6) | 6 |
| `PREF` | User Preferences, comma separated list. `m` for showing music, `p` for showing pay-per-view, `h` for showing HD | (empty) |
| `COUNTRY` | Country code (default: `USA`) | USA |
| `POSTAL_CODE` | Postal code of where shows are available. | 30309 |
| `USER_AGENT` | Custom user agent string for HTTP requests. | Uses random if not specified |
| `TZ` | Timezone | System default |
| `SLEEP_TIME` | Sleep time before next run in seconds (default: 21600, Only used with Docker.) | 21600 |
| `OUTPUT_FILE` | Output file name (default: xmltv.xml) | xmltv.xml |
| `CHANNEL_THUMBNAIL_WIDTH` | Width of channel thumbnails in pixels (`0` removes width filter, unset uses API default) | API default (~55) |

### Command line arguments

| Argument | Description | Default |
| -------------- | --------------------------------------------------------------------------------------------------------------- | -------------------------------- |
| `--lineupId` | Lineup ID; Read more in the [Wiki](https://github.com/jef/zap2xml/wiki/Retrieving-Lineup-ID) | `USA-lineupId-DEFAULT` (Attenna) |
| `--timespan` | Timespan in hours (up to 360 = 15 days, default: 6) | 6 |
| `--pref` | User Preferences, comma separated list. `m` for showing music, `p` for showing pay-per-view, `h` for showing HD | (empty) |
| `--country` | Country code (default: `USA`) | USA |
| `--postalCode` | Postal code of where shows are available. | 30309 |
| `--userAgent` | Custom user agent string for HTTP requests. | Uses random if not specified |
| `--timezone` | Timezone | System default |
| `--outputFile` | Output file name (default: xmltv.xml) | xmltv.xml |
| Argument | Description | Default |
| ------------------------- | --------------------------------------------------------------------------------------------------------------- | -------------------------------- |
| `--lineupId` | Lineup ID; Read more in the [Wiki](https://github.com/jef/zap2xml/wiki/Retrieving-Lineup-ID) | `USA-lineupId-DEFAULT` (Attenna) |
| `--timespan` | Timespan in hours (up to 360 = 15 days, default: 6) | 6 |
| `--pref` | User Preferences, comma separated list. `m` for showing music, `p` for showing pay-per-view, `h` for showing HD | (empty) |
| `--country` | Country code (default: `USA`) | USA |
| `--postalCode` | Postal code of where shows are available. | 30309 |
| `--userAgent` | Custom user agent string for HTTP requests. | Uses random if not specified |
| `--timezone` | Timezone | System default |
| `--outputFile` | Output file name (default: xmltv.xml) | xmltv.xml |
| `--channelThumbnailWidth` | Width of channel thumbnails in pixels (`0` removes width filter, unset uses API default) | API default (~55) |

## Setup and running in intervals

Expand Down
12 changes: 12 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,17 @@ export function getConfig() {
.find((arg) => arg.startsWith("--outputFile="))
?.split("=")[1] ||
"xmltv.xml",
channelThumbnailWidth: (() => {
const rawWidth =
process.env["CHANNEL_THUMBNAIL_WIDTH"] ||
process.argv
.find((arg) => arg.startsWith("--channelThumbnailWidth="))
?.split("=")[1];
if (rawWidth === undefined || rawWidth === null || rawWidth === "") {
return null;
}
const parsedWidth = Number(rawWidth);
return Number.isNaN(parsedWidth) ? null : parsedWidth;
})(),
};
}
21 changes: 12 additions & 9 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ function isHelp() {
Usage: node dist/index.js [options]

Options:
--help Show this help message
--lineupId=ID Lineup ID (default: USA-lineupId-DEFAULT)
--timespan=NUM Timespan in hours (up to 360 = 15 days, default: 6)
--pref=LIST User preferences, comma separated. Can be m, p, and h (default: empty)'
--country=CON Country code (default: USA)
--postalCode=ZIP Postal code (default: 30309)
--userAgent=UA Custom user agent string (default: Uses random if not specified)
--timezone=TZ Timezone (default: America/New_York)
--help Show this help message
--lineupId=ID Lineup ID (default: USA-lineupId-DEFAULT)
--timespan=NUM Timespan in hours (up to 360 = 15 days, default: 6)
--pref=LIST User preferences, comma separated. Can be m, p, and h (default: empty)'
--country=CON Country code (default: USA)
--postalCode=ZIP Postal code (default: 30309)
--userAgent=UA Custom user agent string (default: Uses random if not specified)
--timezone=TZ Timezone (default: America/New_York)
--channelThumbnailWidth=NUM Width of channel thumbnails in pixels (0 removes the width parameter for full size, unset uses API default)
`);
process.exit(0);
}
Expand All @@ -29,7 +30,9 @@ async function main() {
isHelp();

const data = await getTVListings();
const xml = buildXmltv(data);
const xml = buildXmltv(data, {
channelThumbnailWidth: config.channelThumbnailWidth,
});

console.log("Writing XMLTV file");
writeFileSync(config.outputFile, xml, { encoding: "utf-8" });
Expand Down
30 changes: 30 additions & 0 deletions src/xmltv.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,21 @@ describe("buildXmltv", () => {
expect(result).not.toContain("<episode-num");
expect(result).not.toContain("<icon");
});

it("should honor channel thumbnail width option", () => {
const result = buildXmltv(mockData, { channelThumbnailWidth: 120 });
expect(result).toContain(
'<icon src="https://zap2it.tmsimg.com/h3/NowShowing/19629/s28708_ll_h15_ac.png?w=120" />',
);
});

it("should remove width when option is zero", () => {
const result = buildXmltv(mockData, { channelThumbnailWidth: 0 });
expect(result).toContain(
'<icon src="https://zap2it.tmsimg.com/h3/NowShowing/19629/s28708_ll_h15_ac.png" />',
);
expect(result).not.toContain("?w=55");
});
});

describe("escapeXml", () => {
Expand Down Expand Up @@ -256,6 +271,21 @@ describe("buildChannelsXml", () => {
expect(result).toContain("<display-name>TEST</display-name>");
expect(result).not.toContain("<icon");
});

it("should respect channel thumbnail width option", () => {
const result = buildChannelsXml(mockData, { channelThumbnailWidth: 200 });
expect(result).toContain(
'<icon src="https://zap2it.tmsimg.com/h3/NowShowing/19629/s28708_ll_h15_ac.png?w=200" />',
);
});

it("should strip width parameter when option is zero", () => {
const result = buildChannelsXml(mockData, { channelThumbnailWidth: 0 });
expect(result).toContain(
'<icon src="https://zap2it.tmsimg.com/h3/NowShowing/19629/s28708_ll_h15_ac.png" />',
);
expect(result).not.toContain("?w=55");
});
});

describe("buildProgramsXml", () => {
Expand Down
38 changes: 30 additions & 8 deletions src/xmltv.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import type { GridApiResponse } from "./tvlistings.js";

export type XmltvOptions = {
channelThumbnailWidth?: number | null;
};

export function escapeXml(unsafe: string): string {
return unsafe
.replace(/&/g, "&amp;")
Expand All @@ -23,7 +27,12 @@ export function formatDate(dateStr: string): string {
return `${YYYY}${MM}${DD}${hh}${mm}${ss} +0000`;
}

export function buildChannelsXml(data: GridApiResponse): string {
export function buildChannelsXml(
data: GridApiResponse,
options: XmltvOptions = {},
): string {
// Handle the caller omitting XmltvOptions
const thumbnailWidth = options.channelThumbnailWidth ?? null;
let xml = "";

for (const channel of data.channels) {
Expand All @@ -43,11 +52,21 @@ export function buildChannelsXml(data: GridApiResponse): string {
}

if (channel.thumbnail) {
xml += ` <icon src="${escapeXml(
channel.thumbnail.startsWith("http")
? channel.thumbnail
: "https:" + channel.thumbnail,
)}" />\n`;
let iconUrl = channel.thumbnail.startsWith("http")
? channel.thumbnail
: "https:" + channel.thumbnail;

if (thumbnailWidth !== null) {
const parsedUrl = new URL(iconUrl);
if (thumbnailWidth === 0) {
parsedUrl.searchParams.delete("w");
} else {
parsedUrl.searchParams.set("w", String(thumbnailWidth));
}
iconUrl = parsedUrl.toString();
}

xml += ` <icon src="${escapeXml(iconUrl)}" />\n`;
}

xml += " </channel>\n";
Expand Down Expand Up @@ -153,13 +172,16 @@ export function buildProgramsXml(data: GridApiResponse): string {
return xml;
}

export function buildXmltv(data: GridApiResponse): string {
export function buildXmltv(
data: GridApiResponse,
options: XmltvOptions = {},
): string {
console.log("Building XMLTV file");

let xml = '<?xml version="1.0" encoding="UTF-8"?>\n';
xml +=
'<tv generator-info-name="jef/zap2xml" generator-info-url="https://github.com/jef/zap2xml">\n';
xml += buildChannelsXml(data);
xml += buildChannelsXml(data, options);
xml += buildProgramsXml(data);
xml += "</tv>\n";

Expand Down