From d8e4372f223af4bae6c1422b1402798f3e4e061e Mon Sep 17 00:00:00 2001 From: theofficialomega <30985701+BellezaEmporium@users.noreply.github.com> Date: Wed, 16 Jul 2025 12:37:34 +0200 Subject: [PATCH] continue uniformizing + ditch lodash for native JS methods. --- scripts/core/configLoader.ts | 4 +- scripts/functions/functions.ts | 43 +++++++++++++++++++ sites/derana.lk/derana.lk.config.js | 6 ++- sites/dstv.com/dstv.com.config.js | 6 +-- sites/tv.blue.ch/__data__/content.json | 1 + .../__data__/content_invalid_siteid.json | 1 + .../__data__/content_without_image.json | 1 + sites/tv.blue.ch/__data__/no_content.json | 1 + sites/tv.blue.ch/tv.blue.ch.test.js | 14 +++--- sites/tv.lv/__data__/no_content.json | 1 + sites/tv.lv/tv.lv.test.js | 3 +- sites/tv.magenta.at/tv.magenta.at.config.js | 4 +- sites/tv.mail.ru/tv.mail.ru.config.js | 13 +----- 13 files changed, 67 insertions(+), 31 deletions(-) create mode 100644 scripts/functions/functions.ts create mode 100644 sites/tv.blue.ch/__data__/content.json create mode 100644 sites/tv.blue.ch/__data__/content_invalid_siteid.json create mode 100644 sites/tv.blue.ch/__data__/content_without_image.json create mode 100644 sites/tv.blue.ch/__data__/no_content.json create mode 100644 sites/tv.lv/__data__/no_content.json diff --git a/scripts/core/configLoader.ts b/scripts/core/configLoader.ts index 1beb3703..93e1e597 100644 --- a/scripts/core/configLoader.ts +++ b/scripts/core/configLoader.ts @@ -1,5 +1,5 @@ import { SiteConfig } from 'epg-grabber' -import _ from 'lodash' +import { deepMerge } from '../functions/functions' import { pathToFileURL } from 'url' export class ConfigLoader { @@ -28,6 +28,6 @@ export class ConfigLoader { channels: undefined } - return _.merge(defaultConfig, config) + return deepMerge(defaultConfig, config) as SiteConfig } } diff --git a/scripts/functions/functions.ts b/scripts/functions/functions.ts new file mode 100644 index 00000000..776179ee --- /dev/null +++ b/scripts/functions/functions.ts @@ -0,0 +1,43 @@ +// Made to replace lodash functions with their native alternatives. Typed for better TypeScript support. + +/** + * Creates a new array of unique items based on an specific identifier. + * This function uses a Map to ensure that each item is unique based on the result of the provided function. + * @param {Array} arr - The array to filter for unique items + * @param {Function} fn - A function that takes an item and returns a unique identifier + * @returns {Array} A new array containing only unique items based on the identifier + * @example + * const items = [{ id: 1, name: 'A' }, { id: 2, name: 'B' }, { id: 1, name: 'C' }]; + * const uniqueItems = uniqBy(items, item => item.id); + * // uniqueItems will be [{ id: 1, name: 'A' }, { id: 2, name: 'B' }] + */ +export const uniqBy = (arr: T[], fn: (item: T) => K): T[] => + Array.from(new Map(arr.map(item => [fn(item), item])).values()) + +/** + * Recursively merges multiple objects into a single object. + * If the same key exists in multiple objects and the values are both objects, + * they will be deep merged. Otherwise, the latter value will override the former. + * + * @param {...object[]} a - An array of objects to be merged + * @returns {Record} A new object containing all merged properties + * + * @example + * const obj1 = { a: { b: 2 }, c: 3 }; + * const obj2 = { a: { d: 4 }, e: 5 }; + * deepMerge(obj1, obj2); // { a: { b: 2, d: 4 }, c: 3, e: 5 } + */ +export const deepMerge = (...a: (object)[]): Record => + a.reduce((r: { [key: string]: unknown }, o) => + (Object.entries(o).forEach(([k, v]) => { r[k] = r[k] && typeof r[k] === 'object' && typeof v === 'object' ? + deepMerge(r[k], v) : v }), r), {} as Record) + +/** + * Sort an array of objects by a specific key. + * + * @param {string} key - The key to sort by + * @returns {function} A comparison function for sorting + */ +export const sortBy = (key: keyof T): ((a: T, b: T) => number) => { + return (a: T, b: T) => (a[key] > b[key]) ? 1 : ((b[key] > a[key]) ? -1 : 0) +} \ No newline at end of file diff --git a/sites/derana.lk/derana.lk.config.js b/sites/derana.lk/derana.lk.config.js index 4d43be08..c047855c 100644 --- a/sites/derana.lk/derana.lk.config.js +++ b/sites/derana.lk/derana.lk.config.js @@ -3,7 +3,9 @@ const utc = require('dayjs/plugin/utc') const customParseFormat = require('dayjs/plugin/customParseFormat') const parseDuration = require('parse-duration').default const timezone = require('dayjs/plugin/timezone') -const _ = require('lodash') + +// importing custom function sortBy +const sortBy = require('../functions/functions') dayjs.extend(customParseFormat) dayjs.extend(utc) @@ -28,7 +30,7 @@ module.exports = { } }) - return _.sortBy(programs, p => p.start) + return programs.concat().sort(sortBy('start')) } } diff --git a/sites/dstv.com/dstv.com.config.js b/sites/dstv.com/dstv.com.config.js index 8e526ab5..eb04a766 100644 --- a/sites/dstv.com/dstv.com.config.js +++ b/sites/dstv.com/dstv.com.config.js @@ -3,6 +3,7 @@ const dayjs = require('dayjs') const utc = require('dayjs/plugin/utc') const timezone = require('dayjs/plugin/timezone') const customParseFormat = require('dayjs/plugin/customParseFormat') +const { uniqBy } = require('../../functions/functions') dayjs.extend(utc) dayjs.extend(timezone) @@ -45,8 +46,7 @@ module.exports = { return programs }, async channels({ country }) { - const _ = require('lodash') - + const countries = { ao: 'ago', bj: 'ben', @@ -114,7 +114,7 @@ module.exports = { }) }) - return _.uniqBy(channels, 'site_id') + return uniqBy(channels, 'site_id') } } diff --git a/sites/tv.blue.ch/__data__/content.json b/sites/tv.blue.ch/__data__/content.json new file mode 100644 index 00000000..682d337e --- /dev/null +++ b/sites/tv.blue.ch/__data__/content.json @@ -0,0 +1 @@ +{"Nodes":{"Count":1,"TotalItemCount":1,"Items":[{"Domain":"TV","Identifier":"1221","Kind":"Channel","Content":{"Description":{"Title":"blue Zoom D","Language":"de"},"Nodes":{"Count":29,"TotalItemCount":29,"Items":[{"Domain":"TV","Identifier":"t1221ddc59247d45","Kind":"Broadcast","Channel":"1221","Content":{"Description":{"Title":"Weekend on the Rocks","Summary":" - «R.E.S.P.E.C.T», lieber Charles Nguela. Der Comedian tourt fleissig durch die Schweiz, macht für uns aber einen Halt, um in der neuen Ausgabe von «Weekend on the Rocks» mit Moderatorin Vania Spescha über die Entertainment-News der Woche zu plaudern.","ShortSummary":"","Country":"CH","ReleaseDate":"2021-01-01T00:00:00Z","Source":"13","Language":"de","Duration":"00:30:00"},"Nodes":{"Count":1,"TotalItemCount":1,"Items":[{"Domain":"TV","Identifier":"t1221ddc59247d45_landscape","Kind":"Image","Role":"Landscape","ContentPath":"/tv/broadcast/1221/t1221ddc59247d45_landscape","Version":{"Date":"2022-01-04T08:55:22.567Z"}}]},"TechnicalAttributes":{"Stereo":true}},"Version":{"Hash":"60d3"},"Availabilities":[{"AvailabilityStart":"2022-01-16T23:30:00Z","AvailabilityEnd":"2022-01-17T00:00:00Z"}],"Relations":[{"Domain":"TV","Kind":"Reference","Role":"ChannelIdentifier","TargetIdentifier":"2b0898c7-3920-3200-7048-4ea5d9138921"},{"Domain":"TV","Kind":"Reference","Role":"OriginalAirSeries","TargetIdentifier":"false"},{"Domain":"TV","Kind":"Reference","Role":"ExternalBroadcastIdentifier","TargetIdentifier":"167324536-11"},{"Domain":"TV","Kind":"Reference","Role":"ProgramIdentifier","TargetIdentifier":"p12211351631155","Title":"Original"}]}]}}}]}} \ No newline at end of file diff --git a/sites/tv.blue.ch/__data__/content_invalid_siteid.json b/sites/tv.blue.ch/__data__/content_invalid_siteid.json new file mode 100644 index 00000000..974a9930 --- /dev/null +++ b/sites/tv.blue.ch/__data__/content_invalid_siteid.json @@ -0,0 +1 @@ +{"Status":{"Version":"7","Status":"OK","ProcessingTime":"00:00:00.0160674","ExecutionTime":"2022-01-17T13:47:30.584Z"},"Request":{"Domain":"TV","Resource":"Channels","Action":"List","Parameters":"(ids=12210;start=202201170000;end=202201180000;level=normal)","Identifiers":["12210"],"Start":"2022-01-17T00:00:00Z","End":"2022-01-18T00:00:00Z","DataLevel":"Normal"},"DataSource":{"Snapshot":"Tv_20220117114748","DbCreationTime":"2022-01-17T11:49:14.608Z","IncrementCreationTime":"0001-01-01T00:00:00Z"},"Nodes":{"Items":[]}} \ No newline at end of file diff --git a/sites/tv.blue.ch/__data__/content_without_image.json b/sites/tv.blue.ch/__data__/content_without_image.json new file mode 100644 index 00000000..8e698d46 --- /dev/null +++ b/sites/tv.blue.ch/__data__/content_without_image.json @@ -0,0 +1 @@ +{"Nodes":{"Count":1,"TotalItemCount":1,"Items":[{"Domain":"TV","Identifier":"1221","Kind":"Channel","Content":{"Description":{"Title":"blue Zoom D","Language":"de"},"Nodes":{"Count":29,"TotalItemCount":29,"Items":[{"Domain":"TV","Identifier":"t10014a78a8b0668","Kind":"Broadcast","Channel":"1001","Content":{"Description":{"Title":"Lorem ipsum","Language":"fr","Duration":"00:01:00"}},"Version":{"Hash":"440e"},"Availabilities":[{"AvailabilityStart":"2022-01-17T04:59:00Z","AvailabilityEnd":"2022-01-17T05:00:00Z"}],"Relations":[{"Domain":"TV","Kind":"Reference","Role":"ChannelIdentifier","TargetIdentifier":"3553a4f2-ff63-5200-7048-d8d59d805f81"},{"Domain":"TV","Kind":"Reference","Role":"Dummy","TargetIdentifier":"True"},{"Domain":"TV","Kind":"Reference","Role":"ProgramIdentifier","TargetIdentifier":"p1"}]}]}}}]}} \ No newline at end of file diff --git a/sites/tv.blue.ch/__data__/no_content.json b/sites/tv.blue.ch/__data__/no_content.json new file mode 100644 index 00000000..c4df608d --- /dev/null +++ b/sites/tv.blue.ch/__data__/no_content.json @@ -0,0 +1 @@ +{"Status":{"Version":"7","Status":"OK","ExecutionTime":"2022-01-17T15:30:37.97Z"},"Request":{"Domain":"TV","Resource":"Channels","Action":"List","Parameters":"(ids=1884;start=202201170000;end=202201180000;level=normal)","Identifiers":["1884"],"Start":"2022-01-17T00:00:00Z","End":"2022-01-18T00:00:00Z","DataLevel":"Normal"},"DataSource":{"Snapshot":"Tv_20220117144354","DbCreationTime":"2022-01-17T14:45:11.84Z","IncrementCreationTime":"0001-01-01T00:00:00Z"},"Nodes":{"Count":1,"TotalItemCount":1,"Items":[{"Domain":"TV","Identifier":"1884","Kind":"Channel","Content":{"Description":{"Title":"Fisu.tv 1","Language":"en"}}}]}} \ No newline at end of file diff --git a/sites/tv.blue.ch/tv.blue.ch.test.js b/sites/tv.blue.ch/tv.blue.ch.test.js index e01ec286..539b2485 100644 --- a/sites/tv.blue.ch/tv.blue.ch.test.js +++ b/sites/tv.blue.ch/tv.blue.ch.test.js @@ -1,6 +1,8 @@ const { parser, url } = require('./tv.blue.ch.config.js') const dayjs = require('dayjs') const utc = require('dayjs/plugin/utc') +const fs = require('fs') +const path = require('path') const customParseFormat = require('dayjs/plugin/customParseFormat') dayjs.extend(customParseFormat) dayjs.extend(utc) @@ -18,8 +20,7 @@ it('can generate valid url', () => { }) it('can parse response', () => { - const content = - '{"Nodes":{"Count":1,"TotalItemCount":1,"Items":[{"Domain":"TV","Identifier":"1221","Kind":"Channel","Content":{"Description":{"Title":"blue Zoom D","Language":"de"},"Nodes":{"Count":29,"TotalItemCount":29,"Items":[{"Domain":"TV","Identifier":"t1221ddc59247d45","Kind":"Broadcast","Channel":"1221","Content":{"Description":{"Title":"Weekend on the Rocks","Summary":" - «R.E.S.P.E.C.T», lieber Charles Nguela. Der Comedian tourt fleissig durch die Schweiz, macht für uns aber einen Halt, um in der neuen Ausgabe von «Weekend on the Rocks» mit Moderatorin Vania Spescha über die Entertainment-News der Woche zu plaudern.","ShortSummary":"","Country":"CH","ReleaseDate":"2021-01-01T00:00:00Z","Source":"13","Language":"de","Duration":"00:30:00"},"Nodes":{"Count":1,"TotalItemCount":1,"Items":[{"Domain":"TV","Identifier":"t1221ddc59247d45_landscape","Kind":"Image","Role":"Landscape","ContentPath":"/tv/broadcast/1221/t1221ddc59247d45_landscape","Version":{"Date":"2022-01-04T08:55:22.567Z"}}]},"TechnicalAttributes":{"Stereo":true}},"Version":{"Hash":"60d3"},"Availabilities":[{"AvailabilityStart":"2022-01-16T23:30:00Z","AvailabilityEnd":"2022-01-17T00:00:00Z"}],"Relations":[{"Domain":"TV","Kind":"Reference","Role":"ChannelIdentifier","TargetIdentifier":"2b0898c7-3920-3200-7048-4ea5d9138921"},{"Domain":"TV","Kind":"Reference","Role":"OriginalAirSeries","TargetIdentifier":"false"},{"Domain":"TV","Kind":"Reference","Role":"ExternalBroadcastIdentifier","TargetIdentifier":"167324536-11"},{"Domain":"TV","Kind":"Reference","Role":"ProgramIdentifier","TargetIdentifier":"p12211351631155","Title":"Original"}]}]}}}]}}' + const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json')) const result = parser({ content }).map(p => { p.start = p.start.toJSON() p.stop = p.stop.toJSON() @@ -40,8 +41,7 @@ it('can parse response', () => { }) it('can parse response without image', () => { - const content = - '{"Nodes":{"Count":1,"TotalItemCount":1,"Items":[{"Domain":"TV","Identifier":"1221","Kind":"Channel","Content":{"Description":{"Title":"blue Zoom D","Language":"de"},"Nodes":{"Count":29,"TotalItemCount":29,"Items":[{"Domain":"TV","Identifier":"t10014a78a8b0668","Kind":"Broadcast","Channel":"1001","Content":{"Description":{"Title":"Lorem ipsum","Language":"fr","Duration":"00:01:00"}},"Version":{"Hash":"440e"},"Availabilities":[{"AvailabilityStart":"2022-01-17T04:59:00Z","AvailabilityEnd":"2022-01-17T05:00:00Z"}],"Relations":[{"Domain":"TV","Kind":"Reference","Role":"ChannelIdentifier","TargetIdentifier":"3553a4f2-ff63-5200-7048-d8d59d805f81"},{"Domain":"TV","Kind":"Reference","Role":"Dummy","TargetIdentifier":"True"},{"Domain":"TV","Kind":"Reference","Role":"ProgramIdentifier","TargetIdentifier":"p1"}]}]}}}]}}' + const content = fs.readFileSync(path.resolve(__dirname, '__data__/content_without_image.json')) const result = parser({ content }).map(p => { p.start = p.start.toJSON() p.stop = p.stop.toJSON() @@ -59,16 +59,14 @@ it('can parse response without image', () => { it('can handle wrong site id', () => { const result = parser({ - content: - '{"Status":{"Version":"7","Status":"OK","ProcessingTime":"00:00:00.0160674","ExecutionTime":"2022-01-17T13:47:30.584Z"},"Request":{"Domain":"TV","Resource":"Channels","Action":"List","Parameters":"(ids=12210;start=202201170000;end=202201180000;level=normal)","Identifiers":["12210"],"Start":"2022-01-17T00:00:00Z","End":"2022-01-18T00:00:00Z","DataLevel":"Normal"},"DataSource":{"Snapshot":"Tv_20220117114748","DbCreationTime":"2022-01-17T11:49:14.608Z","IncrementCreationTime":"0001-01-01T00:00:00Z"},"Nodes":{"Items":[]}}' + content: fs.readFileSync(path.resolve(__dirname, '__data__/content_invalid_siteid.json')) }) expect(result).toMatchObject([]) }) it('can handle empty guide', () => { const result = parser({ - content: - '{"Status":{"Version":"7","Status":"OK","ExecutionTime":"2022-01-17T15:30:37.97Z"},"Request":{"Domain":"TV","Resource":"Channels","Action":"List","Parameters":"(ids=1884;start=202201170000;end=202201180000;level=normal)","Identifiers":["1884"],"Start":"2022-01-17T00:00:00Z","End":"2022-01-18T00:00:00Z","DataLevel":"Normal"},"DataSource":{"Snapshot":"Tv_20220117144354","DbCreationTime":"2022-01-17T14:45:11.84Z","IncrementCreationTime":"0001-01-01T00:00:00Z"},"Nodes":{"Count":1,"TotalItemCount":1,"Items":[{"Domain":"TV","Identifier":"1884","Kind":"Channel","Content":{"Description":{"Title":"Fisu.tv 1","Language":"en"}}}]}}' + content: fs.readFileSync(path.resolve(__dirname, '__data__/no_content.json')) }) expect(result).toMatchObject([]) }) diff --git a/sites/tv.lv/__data__/no_content.json b/sites/tv.lv/__data__/no_content.json new file mode 100644 index 00000000..60a75b51 --- /dev/null +++ b/sites/tv.lv/__data__/no_content.json @@ -0,0 +1 @@ +{"schedule":{"programme":[],"dayName":"Sestdiena","date":"30.11.2024"},"diff":368,"nextDate":"01-12-2024","previousDate":"29-11-2024","current_timestamp":1701194084} \ No newline at end of file diff --git a/sites/tv.lv/tv.lv.test.js b/sites/tv.lv/tv.lv.test.js index e406789a..2a53efd2 100644 --- a/sites/tv.lv/tv.lv.test.js +++ b/sites/tv.lv/tv.lv.test.js @@ -48,8 +48,7 @@ it('can parse response', () => { it('can handle empty guide', () => { const results = parser({ - content: - '{"schedule":{"programme":[],"dayName":"Sestdiena","date":"30.11.2024"},"diff":368,"nextDate":"01-12-2024","previousDate":"29-11-2024","current_timestamp":1701194084}' + content: fs.readFileSync(path.resolve(__dirname, '__data__/no_content.json')) }) expect(results).toMatchObject([]) }) diff --git a/sites/tv.magenta.at/tv.magenta.at.config.js b/sites/tv.magenta.at/tv.magenta.at.config.js index b56c02e9..aebd8c0d 100644 --- a/sites/tv.magenta.at/tv.magenta.at.config.js +++ b/sites/tv.magenta.at/tv.magenta.at.config.js @@ -7,8 +7,8 @@ const API_ENDPOINT = 'https://tv-at-prod.yo-digital.com/at-bifrost' const headers = { 'Device-Id': crypto.randomUUID(), app_key: 'CTnKA63ruKM0JM1doxAXwwyQLLmQiEiy', - app_version: '02.0.830', - 'X-User-Agent': 'web|web|Firefox-120|02.0.830|1', + app_version: '02.0.1260', + 'X-User-Agent': 'web|web|Firefox-120|02.0.1260|1', 'x-request-tracking-id': crypto.randomUUID() } diff --git a/sites/tv.mail.ru/tv.mail.ru.config.js b/sites/tv.mail.ru/tv.mail.ru.config.js index 56b12bd1..4fc3098c 100644 --- a/sites/tv.mail.ru/tv.mail.ru.config.js +++ b/sites/tv.mail.ru/tv.mail.ru.config.js @@ -3,18 +3,7 @@ const axios = require('axios') // Remove the big lodash dependency by implementing a simple uniqBy function // Complexity = O(n) -const uniqBy = (arr, predicate) => { - const cb = typeof predicate === 'function' ? predicate : (o) => o[predicate] - - return [...arr.reduce((map, item) => { - const key = (item === null || item === undefined) ? - item : cb(item) - - if (!map.has(key)) map.set(key, item) - - return map - }, new Map()).values()] -} +const uniqBy = (arr, fn) => [...new Map(arr.map(x => [fn(x), x])).values()] module.exports = { site: 'tv.mail.ru',