Compare commits
10 Commits
a655a3e84c
...
6e6fa26b75
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e6fa26b75 | ||
|
|
421d0ad616 | ||
|
|
1696b15712 | ||
|
|
ad7aa8e581 | ||
|
|
e077f2721c | ||
|
|
3ab0370d72 | ||
|
|
60321a37e6 | ||
|
|
4ac37de08e | ||
|
|
b5cec7c951 | ||
|
|
f52018fa62 |
27
.github/ISSUE_TEMPLATE/bug-remote.md
vendored
27
.github/ISSUE_TEMPLATE/bug-remote.md
vendored
@@ -1,27 +0,0 @@
|
||||
---
|
||||
name: 🐛 Bug report
|
||||
about: Report a bug for this project
|
||||
---
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
<!-- Tell us what should happen -->
|
||||
|
||||
## Current Behavior
|
||||
|
||||
<!-- Tell us what happens instead of the expected behavior -->
|
||||
|
||||
## Steps to Reproduce
|
||||
|
||||
<!-- Provide a link to a live example, or an unambiguous set of steps to reproduce this bug. -->
|
||||
<!-- Include code to reproduce, if relevant -->
|
||||
|
||||
## Environment
|
||||
|
||||
- OS:
|
||||
|
||||
</details>
|
||||
|
||||
## Logs
|
||||
|
||||
<!-- Provide a brief log -->
|
||||
23
.github/ISSUE_TEMPLATE/bug.yaml
vendored
Normal file
23
.github/ISSUE_TEMPLATE/bug.yaml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
name: 🐛 Bug Report
|
||||
description: File a bug report.
|
||||
title: "[Bug]: "
|
||||
labels: ["bug"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Thanks for taking the time to fill out this bug report!
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
label: What happened?
|
||||
description: Also tell us, what did you expect to happen?
|
||||
placeholder: Tell us what you see!
|
||||
value: "A bug happened!"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Relevant log output
|
||||
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
||||
render: shell
|
||||
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 💡 Have an idea for a new feature?
|
||||
url: https://github.com/jef/zap2xml/discussions
|
||||
about: Create a new idea discussion!
|
||||
- name: 🙇 Need help?
|
||||
url: https://github.com/jef/zap2xml/discussions
|
||||
about: Create a new help discussion if it hasn't been asked before!
|
||||
12
.github/ISSUE_TEMPLATE/feature-request.md
vendored
12
.github/ISSUE_TEMPLATE/feature-request.md
vendored
@@ -1,12 +0,0 @@
|
||||
---
|
||||
name: 🚀 Feature request
|
||||
about: Suggest a new idea
|
||||
---
|
||||
|
||||
### Description
|
||||
|
||||
<!-- Describe the feature here. -->
|
||||
|
||||
### Possible solution
|
||||
|
||||
<!-- Describe the possible solution here. -->
|
||||
14
.github/workflows/ci.yaml
vendored
14
.github/workflows/ci.yaml
vendored
@@ -27,6 +27,7 @@ jobs:
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: package.json
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
@@ -37,5 +38,16 @@ jobs:
|
||||
- name: Lint
|
||||
run: npm run lint
|
||||
|
||||
- name: Test
|
||||
- name: Unit tests
|
||||
run: npm run test:run
|
||||
|
||||
- name: Integration tests
|
||||
run: |
|
||||
node dist/index.js --lineupId=USA-DITV751-X --timespan=3 --postalCode=80020 --outputFile=dtv.xml
|
||||
node dist/index.js --lineupId=USA-OTA80020 --timespan=3 --postalCode=80020 --outputFile=ota.xml
|
||||
|
||||
# Error if they are the same
|
||||
if cmp -s dtv.xml ota.xml; then
|
||||
echo "DTV and OTA outputs are the same, which is unexpected."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
19
.github/workflows/pr-lint.yaml
vendored
Normal file
19
.github/workflows/pr-lint.yaml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
name: Pull Request Title Linter
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- edited
|
||||
- reopened
|
||||
- synchronize
|
||||
|
||||
jobs:
|
||||
pr_lint:
|
||||
name: Lint pull request title
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Lint pull request title
|
||||
uses: jef/conventional-commits-pr-action@v1
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -1,6 +1,9 @@
|
||||
dist/
|
||||
node_modules/
|
||||
.idea
|
||||
.vscode
|
||||
|
||||
node_modules
|
||||
dist
|
||||
|
||||
|
||||
compose.yaml
|
||||
|
||||
xmltv.xml
|
||||
|
||||
1
.prettierignore
Normal file
1
.prettierignore
Normal file
@@ -0,0 +1 @@
|
||||
dist
|
||||
1
.prettierrc
Normal file
1
.prettierrc
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
33
CHANGELOG.md
33
CHANGELOG.md
@@ -1,5 +1,38 @@
|
||||
# Changelog
|
||||
|
||||
## [2.2.0](https://github.com/jef/zap2xml/compare/v2.1.1...v2.2.0) (2025-07-22)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* update rating, new, stereo, and cc ([e077f27](https://github.com/jef/zap2xml/commit/e077f2721c78d278db14037776ebdeb4cdee660d))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add thumbnails for programs ([3ab0370](https://github.com/jef/zap2xml/commit/3ab0370d725c029d64441febb981eeec04f2e1ef))
|
||||
* args work now ([#54](https://github.com/jef/zap2xml/issues/54)) ([ad7aa8e](https://github.com/jef/zap2xml/commit/ad7aa8e5815b80f6fcb5ae118f29281d481e03d1))
|
||||
* headendId when OTA, add tests ([1696b15](https://github.com/jef/zap2xml/commit/1696b15712753039d896a6fcbe3145331f9b5b76))
|
||||
|
||||
|
||||
### Continuous Integration
|
||||
|
||||
* clean up and conventions ([#52](https://github.com/jef/zap2xml/issues/52)) ([60321a3](https://github.com/jef/zap2xml/commit/60321a37e6410f120be4c8198d39896b8ebea017))
|
||||
|
||||
|
||||
### Documentation
|
||||
|
||||
* add FAQ ([4ac37de](https://github.com/jef/zap2xml/commit/4ac37de08e6e4adaeb060465a246558bdc6c2bb7))
|
||||
* include so links to wiki, update SLEEP_TIME default ([b5cec7c](https://github.com/jef/zap2xml/commit/b5cec7c951da794041820407860bcee8e0c5b24a))
|
||||
|
||||
## [2.1.1](https://github.com/jef/zap2xml/compare/v2.1.0...v2.1.1) (2025-07-19)
|
||||
|
||||
|
||||
### Documentation
|
||||
|
||||
* fix defaults ([a655a3e](https://github.com/jef/zap2xml/commit/a655a3e84c2bc7191803d48d581c20b340f3c4e6))
|
||||
* make ref to historical perl ([2756b07](https://github.com/jef/zap2xml/commit/2756b0766f9e62c85cd8b25178763819cfe8cc51))
|
||||
|
||||
## [2.1.0](https://github.com/jef/zap2xml/compare/v2.0.0...v2.1.0) (2025-07-19)
|
||||
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ COPY package-lock.json package-lock.json
|
||||
RUN npm ci
|
||||
|
||||
COPY tsconfig.json tsconfig.json
|
||||
COPY rollup.config.ts rollup.config.ts
|
||||
COPY entrypoint.sh entrypoint.sh
|
||||
COPY src/ src/
|
||||
|
||||
|
||||
30
README.md
30
README.md
@@ -6,10 +6,6 @@ I also _somewhat_ maintain a version of the original in the [historical-perl bra
|
||||
|
||||
## How to use
|
||||
|
||||
### Retrieving your Lineup ID
|
||||
|
||||
Visit the [Retrieving Lineup ID](https://github.com/jef/zap2xml/wiki/Retrieving-Lineup-ID) in the Wiki.
|
||||
|
||||
### Node.js
|
||||
|
||||
```bash
|
||||
@@ -54,7 +50,7 @@ See [Environment variables](#environment-variables) for configuration options.
|
||||
| `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: 10800, Only used with Docker.) | 10800 |
|
||||
| `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 |
|
||||
|
||||
### Command line arguments
|
||||
@@ -69,3 +65,27 @@ See [Environment variables](#environment-variables) for configuration options.
|
||||
| `--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 |
|
||||
|
||||
## Setup and running in intervals
|
||||
|
||||
### Running natively
|
||||
|
||||
You can run zap2xml natively on your system. It is recommended to use a task scheduler to run it in intervals.
|
||||
|
||||
Here are some links to get you started on your machine:
|
||||
|
||||
- Linux and Raspberry Pi: https://github.com/jef/zap2xml/wiki/Running-on-Linux-and-Raspberry-Pi
|
||||
- macOS: https://github.com/jef/zap2xml/wiki/Running-on-macOS
|
||||
- Windows: https://github.com/jef/zap2xml/wiki/Running-on-Windows
|
||||
|
||||
If you want to run zap2xml in intervals, you can use a task scheduler like `cron` on Linux or the Task Scheduler on Windows. Each of the wiki pages above has a section on how to set up zap2xml to run in intervals.
|
||||
|
||||
### Running in Docker
|
||||
|
||||
You can run zap2xml in a Docker container. The `SLEEP_TIME` environment variable can be used to set the interval between runs. The default is 21600 seconds (6 hours).
|
||||
|
||||
## FAQ
|
||||
|
||||
### How do I get my Lineup ID?
|
||||
|
||||
Visit https://github.com/jef/zap2xml/wiki/Retrieving-Lineup-ID
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
SLEEP_TIME=${SLEEP_TIME:-10800}
|
||||
SLEEP_TIME=${SLEEP_TIME:-21600}
|
||||
|
||||
while true; do
|
||||
DATE=$(date)
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
// @ts-check
|
||||
|
||||
import eslint from "@eslint/js";
|
||||
import { defineConfig } from "eslint/config";
|
||||
import js from "@eslint/js";
|
||||
import globals from "globals";
|
||||
import tseslint from "typescript-eslint";
|
||||
import prettierConfig from "eslint-plugin-prettier/recommended";
|
||||
import eslintConfigPrettier from "eslint-config-prettier/flat";
|
||||
|
||||
export default tseslint.config(
|
||||
eslint.configs.recommended,
|
||||
tseslint.configs.strict,
|
||||
tseslint.configs.stylistic,
|
||||
prettierConfig,
|
||||
export default defineConfig([
|
||||
{
|
||||
ignores: ["dist/**", "node_modules/**"],
|
||||
files: ["**/*.{js,mjs,cjs,ts,mts,cts}"],
|
||||
plugins: { js },
|
||||
extends: ["js/recommended"],
|
||||
},
|
||||
);
|
||||
{
|
||||
files: ["**/*.{js,mjs,cjs,ts,mts,cts}"],
|
||||
languageOptions: { globals: globals.browser },
|
||||
},
|
||||
tseslint.configs.recommended,
|
||||
eslintConfigPrettier,
|
||||
{
|
||||
ignores: ["dist"],
|
||||
},
|
||||
]);
|
||||
|
||||
370
package-lock.json
generated
370
package-lock.json
generated
@@ -1,18 +1,24 @@
|
||||
{
|
||||
"name": "@jef/zap2xml",
|
||||
"version": "2.1.0",
|
||||
"version": "2.2.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@jef/zap2xml",
|
||||
"version": "2.1.0",
|
||||
"version": "2.2.0",
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.31.0",
|
||||
"@types/node": "^24.0.14",
|
||||
"@rollup/plugin-commonjs": "^28.0.6",
|
||||
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||
"@rollup/plugin-typescript": "^12.1.4",
|
||||
"@types/node": "^24.0.15",
|
||||
"eslint": "^9.31.0",
|
||||
"eslint-plugin-prettier": "^5.5.3",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"globals": "^16.3.0",
|
||||
"prettier": "^3.6.2",
|
||||
"rollup": "^4.45.1",
|
||||
"tslib": "^2.8.1",
|
||||
"tsx": "^4.20.3",
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.37.0",
|
||||
@@ -568,6 +574,19 @@
|
||||
"url": "https://opencollective.com/eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/eslintrc/node_modules/globals": {
|
||||
"version": "14.0.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
|
||||
"integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "9.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz",
|
||||
@@ -716,17 +735,161 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/@pkgr/core": {
|
||||
"version": "0.2.9",
|
||||
"resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz",
|
||||
"integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==",
|
||||
"node_modules/@rollup/plugin-commonjs": {
|
||||
"version": "28.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.6.tgz",
|
||||
"integrity": "sha512-XSQB1K7FUU5QP+3lOQmVCE3I0FcbbNvmNT4VJSj93iUjayaARrTQeoRdiYQoftAJBLrR9t2agwAd3ekaTgHNlw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@rollup/pluginutils": "^5.0.1",
|
||||
"commondir": "^1.0.1",
|
||||
"estree-walker": "^2.0.2",
|
||||
"fdir": "^6.2.0",
|
||||
"is-reference": "1.2.1",
|
||||
"magic-string": "^0.30.3",
|
||||
"picomatch": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.0.0 || 14 >= 14.17"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"rollup": "^2.68.0||^3.0.0||^4.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"rollup": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/plugin-commonjs/node_modules/estree-walker": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@rollup/plugin-commonjs/node_modules/fdir": {
|
||||
"version": "6.4.6",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz",
|
||||
"integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"picomatch": "^3 || ^4"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"picomatch": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/plugin-commonjs/node_modules/picomatch": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.18.0 || >=16.0.0"
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/pkgr"
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/plugin-node-resolve": {
|
||||
"version": "16.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.1.tgz",
|
||||
"integrity": "sha512-tk5YCxJWIG81umIvNkSod2qK5KyQW19qcBF/B78n1bjtOON6gzKoVeSzAE8yHCZEDmqkHKkxplExA8KzdJLJpA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@rollup/pluginutils": "^5.0.1",
|
||||
"@types/resolve": "1.20.2",
|
||||
"deepmerge": "^4.2.2",
|
||||
"is-module": "^1.0.0",
|
||||
"resolve": "^1.22.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"rollup": "^2.78.0||^3.0.0||^4.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"rollup": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/plugin-typescript": {
|
||||
"version": "12.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-12.1.4.tgz",
|
||||
"integrity": "sha512-s5Hx+EtN60LMlDBvl5f04bEiFZmAepk27Q+mr85L/00zPDn1jtzlTV6FWn81MaIwqfWzKxmOJrBWHU6vtQyedQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@rollup/pluginutils": "^5.1.0",
|
||||
"resolve": "^1.22.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"rollup": "^2.14.0||^3.0.0||^4.0.0",
|
||||
"tslib": "*",
|
||||
"typescript": ">=3.7.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"rollup": {
|
||||
"optional": true
|
||||
},
|
||||
"tslib": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/pluginutils": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.2.0.tgz",
|
||||
"integrity": "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/estree": "^1.0.0",
|
||||
"estree-walker": "^2.0.2",
|
||||
"picomatch": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"rollup": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/pluginutils/node_modules/estree-walker": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@rollup/pluginutils/node_modules/picomatch": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
@@ -1050,6 +1213,13 @@
|
||||
"undici-types": "~7.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/resolve": {
|
||||
"version": "1.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
|
||||
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.37.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz",
|
||||
@@ -1611,6 +1781,13 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/commondir": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
|
||||
"integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
@@ -1668,6 +1845,16 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/deepmerge": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
|
||||
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/es-module-lexer": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
|
||||
@@ -1791,35 +1978,20 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-prettier": {
|
||||
"version": "5.5.3",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.3.tgz",
|
||||
"integrity": "sha512-NAdMYww51ehKfDyDhv59/eIItUVzU0Io9H2E8nHNGKEeeqlnci+1gCvrHib6EmZdf6GxF+LCV5K7UC65Ezvw7w==",
|
||||
"node_modules/eslint-config-prettier": {
|
||||
"version": "10.1.8",
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz",
|
||||
"integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prettier-linter-helpers": "^1.0.0",
|
||||
"synckit": "^0.11.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.0.0"
|
||||
"bin": {
|
||||
"eslint-config-prettier": "bin/cli.js"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/eslint-plugin-prettier"
|
||||
"url": "https://opencollective.com/eslint-config-prettier"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/eslint": ">=8.0.0",
|
||||
"eslint": ">=8.0.0",
|
||||
"eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0",
|
||||
"prettier": ">=3.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/eslint": {
|
||||
"optional": true
|
||||
},
|
||||
"eslint-config-prettier": {
|
||||
"optional": true
|
||||
}
|
||||
"eslint": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-scope": {
|
||||
@@ -1943,13 +2115,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-diff": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
|
||||
"integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/fast-glob": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
|
||||
@@ -2083,6 +2248,16 @@
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-tsconfig": {
|
||||
"version": "4.10.1",
|
||||
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz",
|
||||
@@ -2110,9 +2285,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/globals": {
|
||||
"version": "14.0.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
|
||||
"integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
|
||||
"version": "16.3.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz",
|
||||
"integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -2139,6 +2314,19 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/hasown": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/ignore": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
|
||||
@@ -2176,6 +2364,22 @@
|
||||
"node": ">=0.8.19"
|
||||
}
|
||||
},
|
||||
"node_modules/is-core-module": {
|
||||
"version": "2.16.1",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
|
||||
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"hasown": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-extglob": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
@@ -2199,6 +2403,13 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-module": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
|
||||
"integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-number": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
@@ -2209,6 +2420,16 @@
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-reference": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
|
||||
"integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/estree": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/isexe": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||
@@ -2474,6 +2695,13 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/path-parse": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pathe": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
|
||||
@@ -2566,19 +2794,6 @@
|
||||
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/prettier-linter-helpers": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
|
||||
"integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-diff": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/punycode": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||
@@ -2610,6 +2825,27 @@
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
"version": "1.22.10",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
|
||||
"integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-core-module": "^2.16.0",
|
||||
"path-parse": "^1.0.7",
|
||||
"supports-preserve-symlinks-flag": "^1.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"resolve": "bin/resolve"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve-from": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||
@@ -2811,20 +3047,17 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/synckit": {
|
||||
"version": "0.11.11",
|
||||
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz",
|
||||
"integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==",
|
||||
"node_modules/supports-preserve-symlinks-flag": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@pkgr/core": "^0.2.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.0.0"
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/synckit"
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/tinybench": {
|
||||
@@ -2942,6 +3175,13 @@
|
||||
"typescript": ">=4.8.4"
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"dev": true,
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/tsx": {
|
||||
"version": "4.20.3",
|
||||
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.3.tgz",
|
||||
|
||||
17
package.json
17
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@jef/zap2xml",
|
||||
"version": "2.1.0",
|
||||
"version": "2.2.0",
|
||||
"description": "JavaScript implementation of zap2xml",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
@@ -18,17 +18,24 @@
|
||||
"scripts": {
|
||||
"test": "vitest",
|
||||
"test:run": "vitest run",
|
||||
"build": "tsc",
|
||||
"dev": "tsx src/index.ts",
|
||||
"lint": "eslint .",
|
||||
"lint:fix": "eslint . --fix"
|
||||
"lint:fix": "eslint . --fix",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"build": "npm run typecheck && rollup --config rollup.config.ts --configPlugin @rollup/plugin-typescript"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.31.0",
|
||||
"@types/node": "^24.0.14",
|
||||
"@rollup/plugin-commonjs": "^28.0.6",
|
||||
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||
"@rollup/plugin-typescript": "^12.1.4",
|
||||
"@types/node": "^24.0.15",
|
||||
"eslint": "^9.31.0",
|
||||
"eslint-plugin-prettier": "^5.5.3",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"globals": "^16.3.0",
|
||||
"prettier": "^3.6.2",
|
||||
"rollup": "^4.45.1",
|
||||
"tslib": "^2.8.1",
|
||||
"tsx": "^4.20.3",
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.37.0",
|
||||
|
||||
16
rollup.config.ts
Normal file
16
rollup.config.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import commonjs from "@rollup/plugin-commonjs";
|
||||
import nodeResolve from "@rollup/plugin-node-resolve";
|
||||
import typescript from "@rollup/plugin-typescript";
|
||||
|
||||
const config = {
|
||||
input: "src/index.ts",
|
||||
output: {
|
||||
esModule: true,
|
||||
file: "dist/index.js",
|
||||
format: "es",
|
||||
sourcemap: true,
|
||||
},
|
||||
plugins: [typescript(), nodeResolve({ preferBuiltins: true }), commonjs()],
|
||||
};
|
||||
|
||||
export default config;
|
||||
40
src/config.test.ts
Normal file
40
src/config.test.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { processLineupId, getHeadendId } from "./config.js";
|
||||
|
||||
describe("processLineupId", () => {
|
||||
it("returns env LINEUP_ID if set", () => {
|
||||
process.env.LINEUP_ID = "USA-12345";
|
||||
expect(processLineupId()).toBe("USA-12345");
|
||||
delete process.env.LINEUP_ID;
|
||||
});
|
||||
|
||||
it("returns argv --lineupId if set", () => {
|
||||
process.argv.push("--lineupId=USA-54321");
|
||||
expect(processLineupId()).toBe("USA-54321");
|
||||
process.argv = process.argv.filter((arg) => !arg.startsWith("--lineupId="));
|
||||
});
|
||||
|
||||
it("returns default if nothing set", () => {
|
||||
expect(processLineupId()).toBe("USA-lineupId-DEFAULT");
|
||||
});
|
||||
|
||||
it("returns default if lineupId contains OTA", () => {
|
||||
process.env.LINEUP_ID = "USA-OTA12345";
|
||||
expect(processLineupId()).toBe("USA-lineupId-DEFAULT");
|
||||
delete process.env.LINEUP_ID;
|
||||
});
|
||||
});
|
||||
|
||||
describe("getHeadendId", () => {
|
||||
it("extracts headend from valid lineupId", () => {
|
||||
expect(getHeadendId("USA-OTA12345")).toBe("lineupId");
|
||||
expect(getHeadendId("USA-NY31587-L")).toBe("NY31587");
|
||||
expect(getHeadendId("CAN-OTAT1L0A1")).toBe("lineupId");
|
||||
expect(getHeadendId("CAN-0008861-X")).toBe("0008861");
|
||||
});
|
||||
|
||||
it("returns 'lineup' if no match", () => {
|
||||
expect(getHeadendId("INVALID")).toBe("lineup");
|
||||
expect(getHeadendId("")).toBe("lineup");
|
||||
});
|
||||
});
|
||||
@@ -1,38 +1,68 @@
|
||||
import { UserAgent } from "./useragents.js";
|
||||
|
||||
export const config = {
|
||||
baseUrl: "https://tvlistings.gracenote.com/api/grid",
|
||||
lineupId:
|
||||
export function processLineupId(): string {
|
||||
const lineupId =
|
||||
process.env["LINEUP_ID"] ||
|
||||
process.argv.find((arg) => arg.startsWith("--lineupId="))?.split("=")[1] ||
|
||||
"USA-lineupId-DEFAULT",
|
||||
timespan:
|
||||
process.env["TIMESPAN"] ||
|
||||
process.argv.find((arg) => arg.startsWith("--timespan="))?.split("=")[1] ||
|
||||
"6",
|
||||
country:
|
||||
process.env["COUNTRY"] ||
|
||||
process.argv.find((arg) => arg.startsWith("--country="))?.split("=")[1] ||
|
||||
"USA",
|
||||
postalCode:
|
||||
process.env["POSTAL_CODE"] ||
|
||||
process.argv
|
||||
.find((arg) => arg.startsWith("--postalCode="))
|
||||
?.split("=")[1] ||
|
||||
"30309",
|
||||
pref:
|
||||
process.env["PREF"] ||
|
||||
process.argv.find((arg) => arg.startsWith("--pref="))?.split("=")[1] ||
|
||||
"",
|
||||
timezone: process.env.TZ || "America/New_York",
|
||||
userAgent:
|
||||
process.env["USER_AGENT"] ||
|
||||
process.argv.find((arg) => arg.startsWith("--userAgent="))?.split("=")[1] ||
|
||||
UserAgent,
|
||||
outputFile:
|
||||
process.env["OUTPUT_FILE"] ||
|
||||
process.argv
|
||||
.find((arg) => arg.startsWith("--outputFile="))
|
||||
?.split("=")[1] ||
|
||||
"xmltv.xml",
|
||||
};
|
||||
"USA-lineupId-DEFAULT";
|
||||
|
||||
if (lineupId.includes("OTA")) {
|
||||
return "USA-lineupId-DEFAULT";
|
||||
}
|
||||
|
||||
return lineupId;
|
||||
}
|
||||
|
||||
export function getHeadendId(lineupId: string): string {
|
||||
if (lineupId.includes("OTA")) {
|
||||
return "lineupId";
|
||||
}
|
||||
|
||||
const match = lineupId.match(/^(USA|CAN)-(.*?)(?:-[A-Z]+)?$/);
|
||||
|
||||
return match?.[2] || "lineup";
|
||||
}
|
||||
|
||||
export function getConfig() {
|
||||
const lineupId = processLineupId();
|
||||
const headendId = getHeadendId(lineupId);
|
||||
|
||||
return {
|
||||
baseUrl: "https://tvlistings.gracenote.com/api/grid",
|
||||
lineupId,
|
||||
headendId,
|
||||
timespan:
|
||||
process.env["TIMESPAN"] ||
|
||||
process.argv
|
||||
.find((arg) => arg.startsWith("--timespan="))
|
||||
?.split("=")[1] ||
|
||||
"6",
|
||||
country:
|
||||
process.env["COUNTRY"] ||
|
||||
process.argv.find((arg) => arg.startsWith("--country="))?.split("=")[1] ||
|
||||
"USA",
|
||||
postalCode:
|
||||
process.env["POSTAL_CODE"] ||
|
||||
process.argv
|
||||
.find((arg) => arg.startsWith("--postalCode="))
|
||||
?.split("=")[1] ||
|
||||
"30309",
|
||||
pref:
|
||||
process.env["PREF"] ||
|
||||
process.argv.find((arg) => arg.startsWith("--pref="))?.split("=")[1] ||
|
||||
"",
|
||||
timezone: process.env.TZ || "America/New_York",
|
||||
userAgent:
|
||||
process.env["USER_AGENT"] ||
|
||||
process.argv
|
||||
.find((arg) => arg.startsWith("--userAgent="))
|
||||
?.split("=")[1] ||
|
||||
UserAgent,
|
||||
outputFile:
|
||||
process.env["OUTPUT_FILE"] ||
|
||||
process.argv
|
||||
.find((arg) => arg.startsWith("--outputFile="))
|
||||
?.split("=")[1] ||
|
||||
"xmltv.xml",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { writeFileSync } from "node:fs";
|
||||
import { getTVListings } from "./tvlistings.js";
|
||||
import { buildXmltv } from "./xmltv.js";
|
||||
import { config } from "./config.js";
|
||||
import { getConfig } from "./config.js";
|
||||
|
||||
const config = getConfig();
|
||||
|
||||
function isHelp() {
|
||||
if (process.argv.includes("--help")) {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { config } from "./config.js";
|
||||
import { getConfig } from "./config.js";
|
||||
|
||||
const config = getConfig();
|
||||
|
||||
export interface Program {
|
||||
/** "title": "GMA3" */
|
||||
@@ -82,7 +84,7 @@ function buildUrl(time: number, timespan: number): string {
|
||||
const params = {
|
||||
lineupId: config.lineupId,
|
||||
timespan: timespan.toString(),
|
||||
headendId: "lineupId",
|
||||
headendId: config.headendId,
|
||||
country: config.country,
|
||||
timezone: config.timezone,
|
||||
postalCode: config.postalCode,
|
||||
@@ -91,6 +93,8 @@ function buildUrl(time: number, timespan: number): string {
|
||||
aid: "orbebb",
|
||||
languagecode: "en-us",
|
||||
time: time.toString(),
|
||||
device: "X",
|
||||
userId: "-",
|
||||
};
|
||||
|
||||
const urlParams = new URLSearchParams(params).toString();
|
||||
@@ -119,7 +123,7 @@ export async function getTVListings(): Promise<GridApiResponse> {
|
||||
}).then(async (response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Failed to fetch: ${response.status} ${response.statusText}`
|
||||
`Failed to fetch: ${response.status} ${response.statusText}`,
|
||||
);
|
||||
}
|
||||
const chunkData = (await response.json()) as GridApiResponse;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest";
|
||||
import type { GridApiResponse } from "./tvlistings.js";
|
||||
import {
|
||||
buildChannelsXml,
|
||||
buildProgrammesXml,
|
||||
buildProgramsXml,
|
||||
buildXmltv,
|
||||
escapeXml,
|
||||
formatDate,
|
||||
@@ -57,7 +57,9 @@ describe("buildXmltv", () => {
|
||||
it("should generate valid XML structure", () => {
|
||||
const result = buildXmltv(mockData);
|
||||
expect(result).toContain('<?xml version="1.0" encoding="UTF-8"?>');
|
||||
expect(result).toContain('<tv generator-info-name="zap2it-grid">');
|
||||
expect(result).toContain(
|
||||
'<tv generator-info-name="jef/zap2xml" generator-info-url="https://github.com/jef/zap2xml">',
|
||||
);
|
||||
expect(result).toContain("</tv>");
|
||||
});
|
||||
|
||||
@@ -85,14 +87,16 @@ describe("buildXmltv", () => {
|
||||
|
||||
it("should include rating information", () => {
|
||||
const result = buildXmltv(mockData);
|
||||
expect(result).toContain("<rating><value>TV-PG</value></rating>");
|
||||
expect(result).toContain(
|
||||
'<rating system="MPAA"><value>TV-PG</value></rating>',
|
||||
);
|
||||
});
|
||||
|
||||
it("should include categories from flags and tags", () => {
|
||||
const result = buildXmltv(mockData);
|
||||
expect(result).toContain("<category>New</category>");
|
||||
expect(result).toContain("<category>Stereo</category>");
|
||||
expect(result).toContain("<category>CC</category>");
|
||||
expect(result).toContain("<new />");
|
||||
expect(result).toContain('<audio type="stereo" />');
|
||||
expect(result).toContain('<audio type="cc" />');
|
||||
});
|
||||
|
||||
it("should include episode information", () => {
|
||||
@@ -108,7 +112,9 @@ describe("buildXmltv", () => {
|
||||
const emptyData: GridApiResponse = { channels: [] };
|
||||
const result = buildXmltv(emptyData);
|
||||
expect(result).toContain('<?xml version="1.0" encoding="UTF-8"?>');
|
||||
expect(result).toContain('<tv generator-info-name="zap2it-grid">');
|
||||
expect(result).toContain(
|
||||
'<tv generator-info-name="jef/zap2xml" generator-info-url="https://github.com/jef/zap2xml">',
|
||||
);
|
||||
expect(result).toContain("</tv>");
|
||||
expect(result).not.toContain("<channel");
|
||||
expect(result).not.toContain("<programme");
|
||||
@@ -252,9 +258,9 @@ describe("buildChannelsXml", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("buildProgrammesXml", () => {
|
||||
describe("buildProgramsXml", () => {
|
||||
it("should build programme XML correctly", () => {
|
||||
const result = buildProgrammesXml(mockData);
|
||||
const result = buildProgramsXml(mockData);
|
||||
expect(result).toContain(
|
||||
'<programme start="20250718190000 +0000" stop="20250718200000 +0000" channel="19629">',
|
||||
);
|
||||
@@ -263,16 +269,20 @@ describe("buildProgrammesXml", () => {
|
||||
expect(result).toContain(
|
||||
"<desc>BIA performs; comic Zarna Garg; lifestyle contributor Lori Bergamotto; ABC News chief medical correspondent Dr. Tara Narula.</desc>",
|
||||
);
|
||||
expect(result).toContain("<rating><value>TV-PG</value></rating>");
|
||||
expect(result).toContain("<category>New</category>");
|
||||
expect(result).toContain("<category>Stereo</category>");
|
||||
expect(result).toContain("<category>CC</category>");
|
||||
expect(result).toContain(
|
||||
'<rating system="MPAA"><value>TV-PG</value></rating>',
|
||||
);
|
||||
expect(result).toContain("<new />");
|
||||
expect(result).toContain('<audio type="stereo" />');
|
||||
expect(result).toContain('<audio type="cc" />');
|
||||
expect(result).toContain('<episode-num system="season">5</episode-num>');
|
||||
expect(result).toContain('<episode-num system="episode">217</episode-num>');
|
||||
expect(result).toContain(
|
||||
'<episode-num system="series">SH05918266</episode-num>',
|
||||
);
|
||||
expect(result).toContain('<icon src="p30687311_b_v13_aa" />');
|
||||
expect(result).toContain(
|
||||
'<icon src="https://zap2it.tmsimg.com/assets/p30687311_b_v13_aa.jpg" />',
|
||||
);
|
||||
});
|
||||
|
||||
it("should handle programmes without optional fields", () => {
|
||||
@@ -318,7 +328,7 @@ describe("buildProgrammesXml", () => {
|
||||
},
|
||||
],
|
||||
};
|
||||
const result = buildProgrammesXml(minimalProgramme);
|
||||
const result = buildProgramsXml(minimalProgramme);
|
||||
expect(result).toContain(
|
||||
'<programme start="20250718190000 +0000" stop="20250718193000 +0000" channel="123">',
|
||||
);
|
||||
|
||||
73
src/xmltv.ts
73
src/xmltv.ts
@@ -55,7 +55,7 @@ export function buildChannelsXml(data: GridApiResponse): string {
|
||||
return xml;
|
||||
}
|
||||
|
||||
export function buildProgrammesXml(data: GridApiResponse): string {
|
||||
export function buildProgramsXml(data: GridApiResponse): string {
|
||||
let xml = "";
|
||||
|
||||
for (const channel of data.channels) {
|
||||
@@ -79,43 +79,71 @@ export function buildProgrammesXml(data: GridApiResponse): string {
|
||||
}
|
||||
|
||||
if (event.rating) {
|
||||
xml += ` <rating><value>${escapeXml(
|
||||
xml += ` <rating system="MPAA"><value>${escapeXml(
|
||||
event.rating,
|
||||
)}</value></rating>\n`;
|
||||
}
|
||||
|
||||
if (event.flag && event.flag.length > 0) {
|
||||
for (const flag of event.flag) {
|
||||
xml += ` <category>${escapeXml(flag)}</category>\n`;
|
||||
if (event.flag.includes("New")) {
|
||||
xml += ` <new />\n`;
|
||||
}
|
||||
|
||||
if (event.flag.includes("Live")) {
|
||||
xml += ` <live />\n`;
|
||||
}
|
||||
|
||||
if (event.flag.includes("Premiere")) {
|
||||
xml += ` <premiere />\n`;
|
||||
}
|
||||
|
||||
if (event.flag.includes("Finale")) {
|
||||
xml += ` <last-chance />\n`;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
!event.flag ||
|
||||
(event.flag &&
|
||||
event.flag.length > 0 &&
|
||||
!event.flag.includes("New") &&
|
||||
!event.flag.includes("Live"))
|
||||
) {
|
||||
xml += ` <previously-shown />\n`;
|
||||
}
|
||||
|
||||
if (event.tags && event.tags.length > 0) {
|
||||
for (const tag of event.tags) {
|
||||
xml += ` <category>${escapeXml(tag)}</category>\n`;
|
||||
if (event.tags.includes("Stereo")) {
|
||||
xml += ` <audio type="stereo" />\n`;
|
||||
}
|
||||
if (event.tags.includes("CC")) {
|
||||
xml += ` <subtitles type="teletext" />\n`;
|
||||
}
|
||||
}
|
||||
|
||||
if (event.program.season) {
|
||||
xml += ` <episode-num system="season">${escapeXml(
|
||||
event.program.season,
|
||||
if (event.program.season && event.program.episode) {
|
||||
xml += ` <episode-num system="onscreen">${escapeXml(
|
||||
`S${event.program.season.padStart(2, "0")}E${event.program.episode.padStart(2, "0")}`,
|
||||
)}</episode-num>\n`;
|
||||
}
|
||||
|
||||
if (event.program.episode) {
|
||||
xml += ` <episode-num system="episode">${escapeXml(
|
||||
event.program.episode,
|
||||
xml += ` <episode-num system="common">${escapeXml(
|
||||
`S${event.program.season.padStart(2, "0")}E${event.program.episode.padStart(2, "0")}`,
|
||||
)}</episode-num>\n`;
|
||||
}
|
||||
|
||||
if (event.program.seriesId) {
|
||||
xml += ` <episode-num system="series">${escapeXml(
|
||||
event.program.seriesId,
|
||||
)}</episode-num>\n`;
|
||||
if (/..\d{8}\d{4}/.test(event.program.id)) {
|
||||
xml += ` <episode-num system="dd_progid">${escapeXml(event.program.id)}</episode-num>\n`;
|
||||
}
|
||||
|
||||
xml += ` <episode-num system="xmltv_ns">${escapeXml(
|
||||
`${event.program.season} . ${event.program.episode}`,
|
||||
)}.</episode-num>\n`;
|
||||
}
|
||||
|
||||
if (event.thumbnail) {
|
||||
xml += ` <icon src="${escapeXml(event.thumbnail)}" />\n`;
|
||||
const src = event.thumbnail.startsWith("http")
|
||||
? event.thumbnail
|
||||
: "https://zap2it.tmsimg.com/assets/" + event.thumbnail + ".jpg";
|
||||
xml += ` <icon src="${escapeXml(src)}" />\n`;
|
||||
}
|
||||
|
||||
xml += " </programme>\n";
|
||||
@@ -128,10 +156,11 @@ export function buildProgrammesXml(data: GridApiResponse): string {
|
||||
export function buildXmltv(data: GridApiResponse): string {
|
||||
console.log("Building XMLTV file");
|
||||
|
||||
let xml =
|
||||
'<?xml version="1.0" encoding="UTF-8"?>\n<tv generator-info-name="zap2it-grid">\n';
|
||||
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 += buildProgrammesXml(data);
|
||||
xml += buildProgramsXml(data);
|
||||
xml += "</tv>\n";
|
||||
|
||||
return xml;
|
||||
|
||||
@@ -29,13 +29,11 @@
|
||||
"useDefineForClassFields": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"src/**/*.test.ts",
|
||||
"node_modules/**"
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user