From 6096bee441bb731490a907eb9a08d660d63855a3 Mon Sep 17 00:00:00 2001 From: Toha Date: Tue, 7 Nov 2023 14:40:01 +0700 Subject: [PATCH 1/2] Fix firstmedia.com schedule mislead. The retrieved schedules from firstmedia.com api indeed confusing. Taken for example the following snippet: ```json { channelNo: '245', title: 'News Bulletin', date: '2023-11-07 17:00:00', startTime: '2023-11-07 17:00:00', endTime: '2023-11-07 17:30:00', description: 'News Bulletin', long_description: 'Hourly update of international news with an emphasis on the Arab world.' } ``` Neither `startTime` nor `endTime` is an actual time but an offset from `date`. If its an actual time then it would overlap with each others. The workaround is to calculate the start and stop time offset, sort the schedules based on those offset, and last skip overlapped schedules. Signed-off-by: Toha --- sites/firstmedia.com/firstmedia.com.config.js | 52 ++++++++++++++----- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/sites/firstmedia.com/firstmedia.com.config.js b/sites/firstmedia.com/firstmedia.com.config.js index 12cdd715..633e0b00 100644 --- a/sites/firstmedia.com/firstmedia.com.config.js +++ b/sites/firstmedia.com/firstmedia.com.config.js @@ -7,24 +7,36 @@ dayjs.extend(utc) module.exports = { site: 'firstmedia.com', - days: 1, - url: function ({ channel, date }) { + days: 2, + url({ channel, date }) { return `https://api.firstmedia.com/api/content/tv-guide/list?date=${date.format('DD/MM/YYYY')}&channel=${ channel.site_id }&startTime=0&endTime=24` }, - parser: function ({ content, channel }) { + parser({ content, channel, date }) { if (!content || !channel) return [] - let programs = [] + const programs = [] const items = parseItems(content, channel.site_id) - items.forEach(item => { - programs.push({ - title: parseTitle(item), - description: parseDescription(item), - start: parseStart(item).toISOString(), - stop: parseStop(item).toISOString() + .map(item => { + item.start = toDelta(item.date, item.startTime) + item.stop = toDelta(item.date, item.endTime) + return item }) + .sort((a, b) => a.start - b.start) + + const dt = date.tz('Asia/Jakarta').startOf('d') + let lastStop + items.forEach(item => { + if (lastStop === undefined || item.start >= lastStop) { + lastStop = item.stop + programs.push({ + title: parseTitle(item), + description: parseDescription(item), + start: asDate(parseStart({ item, date: dt })), + stop: asDate(parseStop({ item, date: dt })) + }) + } }) return programs @@ -66,10 +78,22 @@ function parseDescription(item) { return item.long_description } -function parseStart(item) { - return dayjs.tz(item.startTime, 'YYYY-MM-DD HH:mm:ss', 'Asia/Jakarta') +function parseStart({ item, date }) { + return date.add(item.start, 'ms') } -function parseStop(item) { - return dayjs.tz(item.endTime, 'YYYY-MM-DD HH:mm:ss', 'Asia/Jakarta') +function parseStop({ item, date }) { + return date.add(item.stop, 'ms') +} + +function toDelta(from, to) { + return toDate(to).diff(toDate(from), 'milliseconds') +} + +function toDate(date) { + return dayjs(date, 'YYYY-MM-DD HH:mm:ss') +} + +function asDate(date) { + return date.toISOString() } From 4540181187f1dd23bc1a0d8bcc9661d76d67090f Mon Sep 17 00:00:00 2001 From: Toha Date: Tue, 7 Nov 2023 14:40:01 +0700 Subject: [PATCH 2/2] Fix firstmedia.com schedule mislead. The retrieved schedules from firstmedia.com api indeed confusing. Taken for example the following snippet: ``` { channelNo: '245', title: 'News Bulletin', date: '2023-11-07 17:00:00', startTime: '2023-11-07 17:00:00', endTime: '2023-11-07 17:30:00', description: 'News Bulletin', long_description: 'Hourly update of international news with an emphasis on the Arab world.' } ``` Neither `startTime` nor `endTime` is an actual time but an offset from `date`. If its an actual time then it would overlap with each others. The workaround is to calculate the start and stop time offset, sort the schedules based on those offset, and last skip overlapped schedules. Signed-off-by: Toha --- sites/firstmedia.com/firstmedia.com.config.js | 54 +++++++++++++------ sites/firstmedia.com/firstmedia.com.test.js | 20 +++---- 2 files changed, 50 insertions(+), 24 deletions(-) diff --git a/sites/firstmedia.com/firstmedia.com.config.js b/sites/firstmedia.com/firstmedia.com.config.js index 12cdd715..e4c7a9e8 100644 --- a/sites/firstmedia.com/firstmedia.com.config.js +++ b/sites/firstmedia.com/firstmedia.com.config.js @@ -7,24 +7,36 @@ dayjs.extend(utc) module.exports = { site: 'firstmedia.com', - days: 1, - url: function ({ channel, date }) { + days: 2, + url({ channel, date }) { return `https://api.firstmedia.com/api/content/tv-guide/list?date=${date.format('DD/MM/YYYY')}&channel=${ channel.site_id }&startTime=0&endTime=24` }, - parser: function ({ content, channel }) { - if (!content || !channel) return [] + parser({ content, channel, date }) { + if (!content || !channel || !date) return [] - let programs = [] + const programs = [] const items = parseItems(content, channel.site_id) - items.forEach(item => { - programs.push({ - title: parseTitle(item), - description: parseDescription(item), - start: parseStart(item).toISOString(), - stop: parseStop(item).toISOString() + .map(item => { + item.start = toDelta(item.date, item.startTime) + item.stop = toDelta(item.date, item.endTime) + return item }) + .sort((a, b) => a.start - b.start) + + const dt = date.tz('Asia/Jakarta').startOf('d') + let lastStop + items.forEach(item => { + if (lastStop === undefined || item.start >= lastStop) { + lastStop = item.stop + programs.push({ + title: parseTitle(item), + description: parseDescription(item), + start: asDate(parseStart({ item, date: dt })), + stop: asDate(parseStop({ item, date: dt })) + }) + } }) return programs @@ -66,10 +78,22 @@ function parseDescription(item) { return item.long_description } -function parseStart(item) { - return dayjs.tz(item.startTime, 'YYYY-MM-DD HH:mm:ss', 'Asia/Jakarta') +function parseStart({ item, date }) { + return date.add(item.start, 'ms') } -function parseStop(item) { - return dayjs.tz(item.endTime, 'YYYY-MM-DD HH:mm:ss', 'Asia/Jakarta') +function parseStop({ item, date }) { + return date.add(item.stop, 'ms') +} + +function toDelta(from, to) { + return toDate(to).diff(toDate(from), 'milliseconds') +} + +function toDate(date) { + return dayjs(date, 'YYYY-MM-DD HH:mm:ss') +} + +function asDate(date) { + return date.toISOString() } diff --git a/sites/firstmedia.com/firstmedia.com.test.js b/sites/firstmedia.com/firstmedia.com.test.js index ca59291a..3a7d921b 100644 --- a/sites/firstmedia.com/firstmedia.com.test.js +++ b/sites/firstmedia.com/firstmedia.com.test.js @@ -3,27 +3,29 @@ const dayjs = require('dayjs') const utc = require('dayjs/plugin/utc') dayjs.extend(utc) -const date = dayjs.utc('2023-11-04', 'DD/MM/YYYY').startOf('d') +const date = dayjs.utc('2023-11-08').startOf('d') const channel = { site_id: '243', xmltv_id: 'AlJazeeraEnglish.qa', lang: 'id' } it('can generate valid url', () => { expect(url({ channel, date })).toBe( - 'https://api.firstmedia.com/api/content/tv-guide/list?date=04/11/2023&channel=243&startTime=0&endTime=24' + 'https://api.firstmedia.com/api/content/tv-guide/list?date=08/11/2023&channel=243&startTime=0&endTime=24' ) }) it('can parse response', () => { const content = - '{"data":{"entries":{"243":[{"createdAt":"2023-10-29T17:01:52.000Z","updatedAt":"2023-10-29T17:01:52.000Z","id":"044aebf7-7e14-4a8b-a7da-c401498d83f2","channelNo":"243","programmeId":null,"title":"People and Power: The Launderer P3","episode":null,"slug":"people-and-power-the-launderer-p3","date":"2023-11-03 17:00:00","startTime":"2023-11-04 12:30:00","endTime":"2023-11-04 13:00:00","length":1800,"description":"People and Power: The Launderer P3","long_description":"The concluding episode of a three part investigation into Mafia money laundering.","status":true,"channel":{"id":"7fd7a9a6-af32-c861-d2b0-4ddc7846fad2","key":"AljaInt","no":243,"name":"Al Jazeera International","slug":"al-jazeera-international","website":null,"description":"

An international 24-hour English-language It is the first English-language news channel brings you the latest global news stories, analysis from the Middle East & worldwide.

","shortDescription":null,"logo":"files/logos/channels/11-NEWS/AlJazeera Int SD-FirstMedia-Chl-243.jpg","externalId":"132","type":"radio","status":true,"chanel":"SD","locale":"id","relationId":"5a6ea4ae-a008-4889-9c68-7a6f1838e81d","onlyfm":null,"genress":[{"id":"1db3bb43-b00d-49af-b272-6c058a8c0b49","name":"International Free View"},{"id":"2e81a4bd-9719-4186-820a-7e035e07be13","name":"News"}]}}]}}}' - const results = parser({ content, channel }) + '{"data":{"entries":{"243":[{"createdAt":"2023-11-05T17:09:34.000Z","updatedAt":"2023-11-05T17:09:34.000Z","id":"009f3a34-8164-4ff9-b981-9dcab1a518fc","channelNo":"243","programmeId":null,"title":"News Live","episode":null,"slug":"news-live","date":"2023-11-08 17:00:00","startTime":"2023-11-08 20:00:00","endTime":"2023-11-08 20:30:00","length":1800,"description":"News Live","long_description":"Up to date news and analysis from around the world.","status":true,"channel":{"id":"7fd7a9a6-af32-c861-d2b0-4ddc7846fad2","key":"AljaInt","no":243,"name":"Al Jazeera International","slug":"al-jazeera-international","website":null,"description":"

An international 24-hour English-language It is the first English-language news channel brings you the latest global news stories, analysis from the Middle East & worldwide.

","shortDescription":null,"logo":"files/logos/channels/11-NEWS/AlJazeera Int SD-FirstMedia-Chl-243.jpg","externalId":"132","type":"radio","status":true,"chanel":"SD","locale":"id","relationId":"5a6ea4ae-a008-4889-9c68-7a6f1838e81d","onlyfm":null,"genress":[{"id":"1db3bb43-b00d-49af-b272-6c058a8c0b49","name":"International Free View"},{"id":"2e81a4bd-9719-4186-820a-7e035e07be13","name":"News"}]}}]}}}' + const results = parser({ content, channel, date }) + // All time in Asia/Jakarta + // 2023-11-08 17:00:00 -> 2023-11-08 20:00:00 = 2023-11-08 03:00:00 + // 2023-11-08 17:00:00 -> 2023-11-08 20:30:00 = 2023-11-08 03:30:00 expect(results).toMatchObject([ { - start: '2023-11-04T05:30:00.000Z', - stop: '2023-11-04T06:00:00.000Z', - title: 'People and Power: The Launderer P3', - description: - "The concluding episode of a three part investigation into Mafia money laundering." + start: '2023-11-07T20:00:00.000Z', + stop: '2023-11-07T20:30:00.000Z', + title: 'News Live', + description: 'Up to date news and analysis from around the world.' } ]) })