From 5d33db08b8a70c850b99418481f5557605983e52 Mon Sep 17 00:00:00 2001 From: shay <43248357+shayypy@users.noreply.github.com> Date: Thu, 21 Nov 2024 18:30:57 -0600 Subject: [PATCH 1/2] feat: `nhl.com` site I was having trouble with the extant sites for this channel so I decided to add a source for the league's own guide. Unfortunately it's missing images since I don't know where they need to be hosted :( --- SITES.md | 1 + sites/nhl.com/__data__/content.json | 1 + sites/nhl.com/nhl.com.channels.xml | 10 +++++++ sites/nhl.com/nhl.com.config.js | 45 +++++++++++++++++++++++++++++ sites/nhl.com/nhl.com.test.js | 44 ++++++++++++++++++++++++++++ sites/nhl.com/readme.md | 15 ++++++++++ 6 files changed, 116 insertions(+) create mode 100644 sites/nhl.com/__data__/content.json create mode 100644 sites/nhl.com/nhl.com.channels.xml create mode 100644 sites/nhl.com/nhl.com.config.js create mode 100644 sites/nhl.com/nhl.com.test.js create mode 100644 sites/nhl.com/readme.md diff --git a/SITES.md b/SITES.md index c29cadf4b..e90c69682 100644 --- a/SITES.md +++ b/SITES.md @@ -91,6 +91,7 @@ | [mytvsuper.com](sites/mytvsuper.com) | 🟢 | | | [nhk.or.jp](sites/nhk.or.jp) | 🟢 | | | [nhkworldpremium.com](sites/nhkworldpremium.com) | 🟢 | | +| [nhl.com](sites/nhl.com) | 🟢 | | | [nostv.pt](sites/nostv.pt) | 🟢 | | | [novacyprus.com](sites/novacyprus.com) | 🟢 | | | [novasports.gr](sites/novasports.gr) | 🟢 | | diff --git a/sites/nhl.com/__data__/content.json b/sites/nhl.com/__data__/content.json new file mode 100644 index 000000000..929f68c0c --- /dev/null +++ b/sites/nhl.com/__data__/content.json @@ -0,0 +1 @@ +{"date":"2024-11-22","startDate":"2024-11-08","endDate":"2024-12-05","broadcasts":[{"startTime":"2024-11-22T00:00:00","endTime":"2024-11-22T01:00:00","durationSeconds":3600,"title":"On The Fly","description":"On The Fly","houseNumber":"HOTF25R112124SOR","broadcastType":"HD","broadcastStatus":"LIVE","broadcastImageUrl":"onthefly.png"},{"startTime":"2024-11-22T01:00:00","endTime":"2024-11-22T02:00:00","durationSeconds":3600,"title":"On The Fly","description":"On The Fly","houseNumber":"HOTF25R112124CC","broadcastType":"HD","broadcastStatus":"","broadcastImageUrl":"onthefly.png"},{"startTime":"2024-11-22T02:00:00","endTime":"2024-11-22T03:00:00","durationSeconds":3600,"title":"On The Fly","description":"On The Fly","houseNumber":"HOTF25R112124CC","broadcastType":"HD","broadcastStatus":"","broadcastImageUrl":"onthefly.png"},{"startTime":"2024-11-22T03:00:00","endTime":"2024-11-22T04:00:00","durationSeconds":3600,"title":"On The Fly","description":"On The Fly","houseNumber":"HOTF25R112124CC","broadcastType":"HD","broadcastStatus":"","broadcastImageUrl":"onthefly.png"},{"startTime":"2024-11-22T04:00:00","endTime":"2024-11-22T05:00:00","durationSeconds":3600,"title":"On The Fly","description":"On The Fly","houseNumber":"HOTF25R112124CC","broadcastType":"HD","broadcastStatus":"","broadcastImageUrl":"onthefly.png"},{"startTime":"2024-11-22T05:00:00","endTime":"2024-11-22T06:00:00","durationSeconds":3600,"title":"On The Fly","description":"On The Fly","houseNumber":"HOTF25R112124CC","broadcastType":"HD","broadcastStatus":"","broadcastImageUrl":"onthefly.png"},{"startTime":"2024-11-22T06:00:00","endTime":"2024-11-22T07:00:00","durationSeconds":3600,"title":"On The Fly","description":"On The Fly","houseNumber":"HOTF25R112124CC","broadcastType":"HD","broadcastStatus":"","broadcastImageUrl":"onthefly.png"},{"startTime":"2024-11-22T07:00:00","endTime":"2024-11-22T08:00:00","durationSeconds":3600,"title":"On The Fly","description":"On The Fly","houseNumber":"HOTF25R112124CC","broadcastType":"HD","broadcastStatus":"","broadcastImageUrl":"onthefly.png"},{"startTime":"2024-11-22T08:00:00","endTime":"2024-11-22T09:00:00","durationSeconds":3600,"title":"On The Fly","description":"On The Fly","houseNumber":"HOTF25R112124CC","broadcastType":"HD","broadcastStatus":"","broadcastImageUrl":"onthefly.png"},{"startTime":"2024-11-22T09:00:00","endTime":"2024-11-22T10:00:00","durationSeconds":3600,"title":"On The Fly","description":"On The Fly","houseNumber":"HOTF25R112124CC","broadcastType":"HD","broadcastStatus":"","broadcastImageUrl":"onthefly.png"},{"startTime":"2024-11-22T10:00:00","endTime":"2024-11-22T11:00:00","durationSeconds":3600,"title":"On The Fly","description":"On The Fly","houseNumber":"HOTF25R112124CC","broadcastType":"HD","broadcastStatus":"","broadcastImageUrl":"onthefly.png"},{"startTime":"2024-11-22T11:00:00","endTime":"2024-11-22T12:00:00","durationSeconds":3600,"title":"On The Fly","description":"On The Fly","houseNumber":"HOTF25R112124CC","broadcastType":"HD","broadcastStatus":"","broadcastImageUrl":"onthefly.png"},{"startTime":"2024-11-22T12:00:00","endTime":"2024-11-22T14:00:00","durationSeconds":7200,"title":"NHL Game","description":"New York Rangers at Calgary Flames on 11/21/2024 From Scotiabank Saddledome","houseNumber":"H120NYRCGY11212024","broadcastType":"HD","broadcastStatus":"","broadcastImageUrl":"nhl.png"},{"startTime":"2024-11-22T14:00:00","endTime":"2024-11-22T16:00:00","durationSeconds":7200,"title":"NHL Game","description":"Carolina Hurricanes at New Jersey Devils on 11/21/2024 From Prudential Center","houseNumber":"H120CARNJD11212024","broadcastType":"HD","broadcastStatus":"","broadcastImageUrl":"nhl.png"},{"startTime":"2024-11-22T16:00:00","endTime":"2024-11-22T17:00:00","durationSeconds":3600,"title":"NHL Tonight: First Shift","description":"NHL Tonight: First Shift","houseNumber":"HNHLTFS25112224LV","broadcastType":"HD","broadcastStatus":"LIVE","broadcastImageUrl":""},{"startTime":"2024-11-22T17:00:00","endTime":"2024-11-22T19:00:00","durationSeconds":7200,"title":"NHL Now","description":"NHL Now","houseNumber":"HNOW25R112224LV","broadcastType":"HD","broadcastStatus":"LIVE","broadcastImageUrl":"nhlnow.png"},{"startTime":"2024-11-22T19:00:00","endTime":"2024-11-22T22:00:00","durationSeconds":10800,"title":"Regular Season Hockey on NHL Network","description":"Winnipeg Jets at Pittsburgh Penguins on 11/22/2024 From PPG Paints Arena","houseNumber":"H180WPGPIT11222024","broadcastType":"HD","broadcastStatus":"LIVE","broadcastImageUrl":"nhl.png"},{"startTime":"2024-11-22T22:00:00","endTime":"2024-11-22T22:30:00","durationSeconds":1800,"title":"NHL Tonight","description":"NHL Tonight","houseNumber":"HNHLT25RS112224LV","broadcastType":"HD","broadcastStatus":"LIVE","broadcastImageUrl":"nhltonight.png"},{"startTime":"2024-11-22T22:30:00","endTime":"2024-11-22T23:00:00","durationSeconds":1800,"title":"NHL Tonight","description":"NHL Tonight","houseNumber":"HNHLT25RS112224CC","broadcastType":"HD","broadcastStatus":"","broadcastImageUrl":"nhltonight.png"},{"startTime":"2024-11-22T23:00:00","endTime":"2024-11-22T23:30:00","durationSeconds":1800,"title":"NHL Tonight","description":"NHL Tonight","houseNumber":"HNHLT25RS112224CC","broadcastType":"HD","broadcastStatus":"","broadcastImageUrl":"nhltonight.png"},{"startTime":"2024-11-22T23:30:00","endTime":"2024-11-23T00:00:00","durationSeconds":1800,"title":"NHL Tonight","description":"NHL Tonight","houseNumber":"HNHLT25RS112224CC","broadcastType":"HD","broadcastStatus":"","broadcastImageUrl":"nhltonight.png"}]} \ No newline at end of file diff --git a/sites/nhl.com/nhl.com.channels.xml b/sites/nhl.com/nhl.com.channels.xml new file mode 100644 index 000000000..28f85d9f7 --- /dev/null +++ b/sites/nhl.com/nhl.com.channels.xml @@ -0,0 +1,10 @@ + + + + NHL Network + diff --git a/sites/nhl.com/nhl.com.config.js b/sites/nhl.com/nhl.com.config.js new file mode 100644 index 000000000..41354d786 --- /dev/null +++ b/sites/nhl.com/nhl.com.config.js @@ -0,0 +1,45 @@ +const dayjs = require('dayjs') + +module.exports = { + site: 'nhl.com', + // I'm not sure what `endDate` represents but they only return 1 day of + // results, with `endTime`s ocassionally in the following day. + days: 1, + url: ({ date }) => `https://api-web.nhle.com/v1/network/tv-schedule/${date.toJSON().split("T")[0]}`, + parser({ content }) { + const programs = [] + const items = parseItems(content) + for (const item of items) { + programs.push({ + title: item.title, + description: item.description === item.title ? undefined : item.description, + category: item.title === "NHL Game" ? "Sports event" : "Sports non-event", + // image: parseImage(item), + start: parseStart(item), + stop: parseStop(item) + }) + } + + return programs + } +} + +// Unfortunately I couldn't determine how these are +// supposed to be formatted. Pointers appreciated! +// function parseImage(item) { +// const uri = item.broadcastImageUrl + +// return uri ? `https://???/${uri}` : null +// } + +function parseStart(item) { + return dayjs(item.startTime) +} + +function parseStop(item) { + return dayjs(item.endTime) +} + +function parseItems(content) { + return JSON.parse(content).broadcasts +} diff --git a/sites/nhl.com/nhl.com.test.js b/sites/nhl.com/nhl.com.test.js new file mode 100644 index 000000000..4a1b4732f --- /dev/null +++ b/sites/nhl.com/nhl.com.test.js @@ -0,0 +1,44 @@ +const { parser, url } = require('./nhl.com.config.js') +const fs = require('fs') +const path = require('path') +const dayjs = require('dayjs') +const utc = require('dayjs/plugin/utc') +const customParseFormat = require('dayjs/plugin/customParseFormat') +dayjs.extend(customParseFormat) +dayjs.extend(utc) + +const date = dayjs.utc('2024-11-21', 'YYYY-MM-DD').startOf('d') + +it('can generate valid url', () => { + expect(url({ date })).toBe( + 'https://api-web.nhle.com/v1/network/tv-schedule/2024-11-21' + ) +}) + +it('can parse response', () => { + const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json')) + let results = parser({ content, date }) + results = results.map(p => { + p.start = p.start.toJSON() + p.stop = p.stop.toJSON() + return p + }) + + expect(results[0]).toMatchObject({ + start: '2024-11-21T12:00:00.000Z', + stop: '2024-11-21T13:00:00.000Z', + title: 'On The Fly', + category: 'Sports non-event', + }) +}) + +it('can handle empty guide', () => { + const results = parser({ content: JSON.stringify({ + // extra props not necessary but they form a valid response + date: "2024-11-21", + startDate: "2024-11-07", + endDate: "2024-12-05", + broadcasts: [], + }) }) + expect(results).toMatchObject([]) +}) diff --git a/sites/nhl.com/readme.md b/sites/nhl.com/readme.md new file mode 100644 index 000000000..562599a06 --- /dev/null +++ b/sites/nhl.com/readme.md @@ -0,0 +1,15 @@ +# nhl.com + +https://www.nhl.com/nhl-network/programming-schedule + +### Download the guide + +```sh +npm run grab -- --site=nhl.com +``` + +### Test + +```sh +npm test -- nhl.com +``` From b49162069719b5b6c701d7ec4f0572f76d0cb4ae Mon Sep 17 00:00:00 2001 From: shay <43248357+shayypy@users.noreply.github.com> Date: Thu, 21 Nov 2024 18:51:19 -0600 Subject: [PATCH 2/2] chore: remove confusing category determination it's all sports in the end --- sites/nhl.com/nhl.com.config.js | 2 +- sites/nhl.com/nhl.com.test.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sites/nhl.com/nhl.com.config.js b/sites/nhl.com/nhl.com.config.js index 41354d786..c3c014085 100644 --- a/sites/nhl.com/nhl.com.config.js +++ b/sites/nhl.com/nhl.com.config.js @@ -13,7 +13,7 @@ module.exports = { programs.push({ title: item.title, description: item.description === item.title ? undefined : item.description, - category: item.title === "NHL Game" ? "Sports event" : "Sports non-event", + category: "Sports", // image: parseImage(item), start: parseStart(item), stop: parseStop(item) diff --git a/sites/nhl.com/nhl.com.test.js b/sites/nhl.com/nhl.com.test.js index 4a1b4732f..a12db69a6 100644 --- a/sites/nhl.com/nhl.com.test.js +++ b/sites/nhl.com/nhl.com.test.js @@ -28,7 +28,7 @@ it('can parse response', () => { start: '2024-11-21T12:00:00.000Z', stop: '2024-11-21T13:00:00.000Z', title: 'On The Fly', - category: 'Sports non-event', + category: 'Sports', }) })