Merge pull request #3061 from iptv-org/be-2957-s1

04/2026 Part 1 : Dependencies & Cleanup
This commit is contained in:
Dum4G authored and GitHub committed 2026-04-11 00:12:34 +03:00
commit 390b6c26a7
34 files changed
+4913 -4543

No files matched your search

+1 -1
View File
@@ -44,7 +44,7 @@ jobs:
cache: 'npm'
- name: Install dependencies
if: steps.files.outputs.any_changed == 'true'
run: SKIP_POSTINSTALL=1 npm install
run: npm install --ignore-scripts
- name: Check changed js-files
if: steps.files.outputs.js_any_changed == 'true'
run: npx eslint ${{ steps.files.outputs.js_all_changed_files }}
+4560 -4253
View File
File diff suppressed because it is too large. Load diff
+41 -61
View File
@@ -17,15 +17,12 @@
"grab": "tsx scripts/commands/epg/grab.ts",
"lint": "npx eslint \"{scripts,tests,sites}/**/*.{ts,mts,js}\"",
"test": "cross-env TZ=Pacific/Nauru npx jest --runInBand",
"postinstall": "skip-postinstall || npm run api:load",
"postinstall": "npm run api:load",
"prepare": "husky"
},
"private": true,
"license": "UNLICENSED",
"jest": {
"setupFiles": [
"<rootDir>/node_modules/jest-offline"
],
"transform": {
"^.+\\.(ts|js)$": "@swc/jest"
},
@@ -39,23 +36,23 @@
]
},
"dependencies": {
"@alex_neo/jest-expect-message": "^1.0.5",
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "^9.32.0",
"@eslint/eslintrc": "^3.3.5",
"@eslint/js": "^9.39.4",
"@freearhey/chronos": "^0.0.1",
"@freearhey/core": "^0.13.2",
"@freearhey/core": "^0.15.2",
"@freearhey/search-js": "^0.2.1",
"@freearhey/storage-js": "^0.2.0",
"@iptv-org/sdk": "^1.1.4",
"@iptv-org/sdk": "^1.2.0",
"@ntlab/sfetch": "^1.2.0",
"@octokit/core": "^7.0.3",
"@octokit/plugin-paginate-rest": "^13.1.1",
"@octokit/plugin-rest-endpoint-methods": "^16.0.0",
"@stylistic/eslint-plugin": "^5.2.2",
"@swc/core": "^1.13.2",
"@octokit/core": "^7.0.6",
"@octokit/plugin-paginate-rest": "^14.0.0",
"@octokit/plugin-rest-endpoint-methods": "^17.0.0",
"@stylistic/eslint-plugin": "^5.10.0",
"@swc/core": "^1.15.24",
"@swc/jest": "^0.2.39",
"@types/cli-progress": "^3.11.6",
"@types/fs-extra": "^11.0.4",
"@types/inquirer": "^9.0.8",
"@types/inquirer": "^9.0.9",
"@types/jest": "^30.0.0",
"@types/langs": "^2.0.5",
"@types/lodash.merge": "^4.6.9",
@@ -63,68 +60,51 @@
"@types/lodash.sortby": "^4.7.9",
"@types/lodash.startcase": "^4.4.9",
"@types/lodash.uniqby": "^4.7.9",
"@types/node": "^24.1.0",
"@types/node-cleanup": "^2.1.5",
"@types/numeral": "^2.0.5",
"@types/node": "^25.5.2",
"@types/pako": "^2.0.4",
"@typescript-eslint/eslint-plugin": "^8.38.0",
"@typescript-eslint/parser": "^8.38.0",
"axios": "^1.11.0",
"axios-cookiejar-support": "^6.0.4",
"axios-mock-adapter": "^2.1.0",
"chalk": "^5.4.1",
"cheerio": "^1.1.2",
"@typescript-eslint/eslint-plugin": "^8.58.1",
"@typescript-eslint/parser": "^8.58.1",
"axios": "^1.15.0",
"chalk": "^5.6.2",
"cheerio": "^1.2.0",
"cli-progress": "^3.12.0",
"commander": "^14.0.0",
"commander": "^14.0.3",
"consola": "^3.4.2",
"cross-env": "^10.0.0",
"cross-env": "^10.1.0",
"csv-parser": "^3.2.0",
"curl-generator": "^0.5.0",
"cwait": "^1.1.2",
"dayjs": "^1.11.13",
"dayjs": "^1.11.20",
"epg-grabber": "^0.46.1",
"epg-parser": "^0.5.0",
"eslint": "^9.32.0",
"eslint": "^10.2.0",
"eslint-config-prettier": "^10.1.8",
"form-data": "^4.0.4",
"fs-extra": "^11.3.0",
"glob": "^11.0.3",
"globals": "^16.3.0",
"fs-extra": "^11.3.4",
"glob": "^13.0.6",
"globals": "^17.4.0",
"husky": "^9.1.7",
"iconv-lite": "^0.6.3",
"inquirer": "^12.8.2",
"jest": "^30.0.5",
"jest-offline": "^1.0.1",
"iconv-lite": "^0.7.2",
"inquirer": "^13.4.1",
"jest": "^30.3.0",
"langs": "^2.0.0",
"libxml2-wasm": "^0.5.0",
"libxml2-wasm": "^0.7.1",
"lodash.merge": "^4.6.2",
"lodash.orderby": "^4.6.0",
"lodash.orderby": "^4.18.0",
"lodash.sortby": "^4.7.0",
"lodash.startcase": "^4.4.0",
"lodash.uniqby": "^4.7.0",
"luxon": "^3.7.1",
"mockdate": "^3.0.5",
"nedb-promises": "^6.2.3",
"node-cleanup": "^2.1.2",
"numeral": "^2.0.6",
"p-limit": "^7.3.0",
"pako": "^2.1.0",
"parse-duration": "^2.1.4",
"pdf-parse": "^1.1.1",
"pm2": "^6.0.8",
"parse-duration": "^2.1.6",
"pm2": "^6.0.14",
"run-script-os": "^1.1.6",
"serve": "^14.2.4",
"signale": "^1.4.0",
"skip-postinstall": "^1.0.0",
"socks-proxy-agent": "^8.0.5",
"srcset": "^5.0.1",
"serve": "^14.2.6",
"socks-proxy-agent": "^10.0.0",
"srcset": "^5.0.3",
"table2array": "^0.0.2",
"tabletojson": "^4.1.6",
"tough-cookie": "^5.1.2",
"transliteration": "^2.3.5",
"tsx": "^4.20.3",
"typescript": "^5.8.3",
"unzipit": "^1.4.3",
"uuid": "^11.1.0",
"wildcard-match": "^5.1.4"
"tabletojson": "^4.1.9",
"tough-cookie": "^6.0.1",
"tsx": "^4.21.0",
"typescript": "^6.0.2",
"uuid": "^13.0.0"
}
}
+1 -2
View File
@@ -5,7 +5,6 @@ import { select, input } from '@inquirer/prompts'
import { generateChannelsXML } from '../../core'
import { Storage } from '@freearhey/storage-js'
import { Channel } from '../../models'
import nodeCleanup from 'node-cleanup'
import * as sdk from '@iptv-org/sdk'
import { Command } from 'commander'
@@ -30,7 +29,7 @@ const storage = new Storage()
let channelsFromXML = new Collection<Channel>()
main(filepath)
nodeCleanup(() => {
process.on('SIGINT', () => {
save(filepath, channelsFromXML)
if (process.platform === 'win32') process.kill(0)
})
+9 -11
View File
@@ -5,7 +5,7 @@ import { CurlBody } from 'curl-generator/dist/bodies/body'
import { Channel, Guide, Program } from '../../models'
import { SocksProxyAgent } from 'socks-proxy-agent'
import defaultConfig from '../../default.config'
import { PromisyClass, TaskQueue } from 'cwait'
import pLimit from 'p-limit'
import { Storage } from '@freearhey/storage-js'
import { CurlGenerator } from 'curl-generator'
import { QueueItem } from '../../types/queue'
@@ -213,8 +213,7 @@ async function main() {
}
const maxConnections = globalConfig.maxConnections || defaultConfig.maxConnections
const taskQueue = new TaskQueue(Promise as PromisyClass, maxConnections)
const limit = pLimit(maxConnections)
const channels = new Collection<Channel>()
const programs = new Collection<Program>()
@@ -222,8 +221,12 @@ async function main() {
let i = 1
const total = queue.count()
const requests = queue.map(
taskQueue.wrap(async (queueItem: QueueItem) => {
logger.info('run:')
const timer = new Timer()
timer.start()
const requests = queue.all().map((queueItem: QueueItem) =>
limit(async () => {
const { channel, config, date } = queueItem
if (!channel.logo) {
@@ -262,12 +265,7 @@ async function main() {
})
)
logger.info('run:')
const timer = new Timer()
timer.start()
await Promise.all(requests.all())
await Promise.all(requests)
const output = globalConfig.output || defaultConfig.output
@@ -1,5 +1,9 @@
const cheerio = require('cheerio')
const { DateTime } = require('luxon')
const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc')
const timezone = require('dayjs/plugin/timezone')
dayjs.extend(utc)
dayjs.extend(timezone)
module.exports = {
site: 'andorradifusio.ad',
@@ -15,12 +19,12 @@ module.exports = {
let start = parseStart(item, date)
if (prev) {
if (start < prev.start) {
start = start.plus({ days: 1 })
start = start.add(1, 'd')
date = date.add(1, 'd')
}
prev.stop = start
}
const stop = start.plus({ hours: 1 })
const stop = start.add(1, 'h')
programs.push({
title: item.title,
start,
@@ -35,12 +39,12 @@ module.exports = {
function parseStart(item, date) {
const dateString = `${date.format('MM/DD/YYYY')} ${item.time}`
return DateTime.fromFormat(dateString, 'MM/dd/yyyy HH:mm', { zone: 'Europe/Madrid' }).toUTC()
return dayjs.tz(dateString, 'MM/DD/YYYY HH:mm', 'Europe/Madrid').utc()
}
function parseItems(content, date) {
const $ = cheerio.load(content)
const day = DateTime.fromMillis(date.valueOf()).setLocale('ca').toFormat('dd LLLL').toLowerCase()
const day = dayjs(date.valueOf()).locale('ca').format('DD MMMM').toLowerCase()
const column = $('.programacio-dia > h3 > .dia')
.filter((i, el) => $(el).text() === day.slice(0, 6) + '.')
.first()
@@ -1,5 +1,9 @@
const cheerio = require('cheerio')
const { DateTime } = require('luxon')
const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc')
const timezone = require('dayjs/plugin/timezone')
dayjs.extend(utc)
dayjs.extend(timezone)
module.exports = {
site: 'arianatelevision.com',
@@ -13,12 +17,12 @@ module.exports = {
let start = parseStart(item, date)
if (prev) {
if (start < prev.start) {
start = start.plus({ days: 1 })
date = date.add(1, 'd')
start = start.add(1, 'day')
date = date.add(1, 'day')
}
prev.stop = start
}
const stop = start.plus({ minutes: 30 })
const stop = start.add(30, 'minute')
programs.push({
title: item.title,
start,
@@ -33,7 +37,7 @@ module.exports = {
function parseStart(item, date) {
const time = `${date.format('YYYY-MM-DD')} ${item.start}`
return DateTime.fromFormat(time, 'yyyy-MM-dd H:mm', { zone: 'Asia/Kabul' }).toUTC()
return dayjs.tz(time, 'YYYY-MM-DD H:mm', 'Asia/Kabul').utc()
}
function parseItems(content, date) {
+7 -3
View File
@@ -1,6 +1,10 @@
const cheerio = require('cheerio')
const axios = require('axios')
const { DateTime } = require('luxon')
const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc')
const timezone = require('dayjs/plugin/timezone')
dayjs.extend(utc)
dayjs.extend(timezone)
module.exports = {
site: 'awilime.com',
@@ -25,7 +29,7 @@ module.exports = {
if (prev) {
prev.stop = start
}
const stop = start.plus({ minute: 30 })
const stop = start.add(30, 'minute')
programs.push({
title: parseTitle($item),
@@ -87,7 +91,7 @@ function parseStart($item, date) {
if (!time || !/^\d/.test(time)) return null
time = `${date.format('YYYY-MM-DD')} ${time}`
return DateTime.fromFormat(time, 'yyyy-MM-dd HH:mm', { zone: 'Europe/Budapest' }).toUTC()
return dayjs.tz(time, 'YYYY-MM-DD HH:mm', 'Europe/Budapest').utc()
}
function parseItems(content) {
+10 -7
View File
@@ -1,7 +1,10 @@
const axios = require('axios')
const dayjs = require('dayjs')
const cheerio = require('cheerio')
const { DateTime } = require('luxon')
const utc = require('dayjs/plugin/utc')
const timezone = require('dayjs/plugin/timezone')
dayjs.extend(utc)
dayjs.extend(timezone)
module.exports = {
site: 'bein.com',
@@ -24,7 +27,7 @@ module.exports = {
parser: function ({ content, channel, date }) {
let programs = []
const items = parseItems(content, channel)
date = DateTime.fromMillis(date.valueOf()).minus({ days: 1 })
date = dayjs(date.valueOf()).subtract(1, 'day')
items.forEach(item => {
const $item = cheerio.load(item)
const title = parseTitle($item)
@@ -34,14 +37,14 @@ module.exports = {
let start = parseTime($item, date)
if (prev) {
if (start < prev.start) {
start = start.plus({ days: 1 })
date = date.plus({ days: 1 })
start = start.add(1, 'day')
date = date.add(1, 'day')
}
prev.stop = start
}
let stop = parseTime($item, start)
if (stop < start) {
stop = stop.plus({ days: 1 })
stop = stop.add(1, 'day')
}
programs.push({
title,
@@ -97,9 +100,9 @@ function parseTime($item, date) {
.text()
.match(/^(\d{2}:\d{2})/) || [null, null]
if (!time) return null
time = `${date.toFormat('yyyy-MM-dd')} ${time}`
time = `${date.format('YYYY-MM-DD')} ${time}`
return DateTime.fromFormat(time, 'yyyy-MM-dd HH:mm', { zone: 'Asia/Qatar' }).toUTC()
return dayjs.tz(time, 'YYYY-MM-DD HH:mm', 'Asia/Qatar').utc()
}
function parseItems(content, channel) {
@@ -1,6 +1,10 @@
const cheerio = require('cheerio')
const axios = require('axios')
const { DateTime } = require('luxon')
const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc')
const timezone = require('dayjs/plugin/timezone')
dayjs.extend(utc)
dayjs.extend(timezone)
module.exports = {
site: 'clickthecity.com',
@@ -17,7 +21,7 @@ module.exports = {
const params = new URLSearchParams()
params.append(
'optDate',
DateTime.fromMillis(date.valueOf()).setZone('Asia/Manila').toFormat('yyyy-MM-dd')
dayjs(date.valueOf()).tz('Asia/Manila').format('YYYY-MM-DD')
)
params.append('optTime', '00:00:00')
@@ -33,7 +37,7 @@ module.exports = {
let stop = parseStop($item, date)
if (!start || !stop) return
if (start > stop) {
stop = stop.plus({ days: 1 })
stop = stop.add(1, 'day')
}
programs.push({
@@ -77,7 +81,7 @@ function parseStart($item, date) {
if (!time) return null
time = `${date.format('YYYY-MM-DD')} ${time.replace('%3A', ':').replace('+', ' ')}`
return DateTime.fromFormat(time, 'yyyy-MM-dd h:mm a', { zone: 'Asia/Manila' }).toUTC()
return dayjs.tz(time, 'YYYY-MM-DD h:mm A', 'Asia/Manila').utc()
}
function parseStop($item, date) {
@@ -86,7 +90,7 @@ function parseStop($item, date) {
if (!time) return null
time = `${date.format('YYYY-MM-DD')} ${time.replace('%3A', ':').replace('+', ' ')}`
return DateTime.fromFormat(time, 'yyyy-MM-dd h:mm a', { zone: 'Asia/Manila' }).toUTC()
return dayjs.tz(time, 'YYYY-MM-DD h:mm A', 'Asia/Manila').utc()
}
function parseItems(content) {
@@ -1,6 +1,10 @@
const axios = require('axios')
const cheerio = require('cheerio')
const { DateTime } = require('luxon')
const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc')
const timezone = require('dayjs/plugin/timezone')
dayjs.extend(utc)
dayjs.extend(timezone)
module.exports = {
site: 'entertainment.ie',
@@ -19,10 +23,10 @@ module.exports = {
let start = parseStart($item, date)
if (!start) return
if (prev && start < prev.start) {
start = start.plus({ days: 1 })
start = start.add(1, 'day')
}
const duration = parseDuration($item)
const stop = start.plus({ minutes: duration })
const stop = start.add(duration, 'minute')
programs.push({
title: parseTitle($item),
description: parseDescription($item),
@@ -77,9 +81,7 @@ function parseStart($item, date) {
let [, time] = d ? d.split(', ') : [null, null]
return time
? DateTime.fromFormat(`${date.format('YYYY-MM-DD')} ${time}`, 'yyyy-MM-dd HH:mm', {
zone: 'UTC'
}).toUTC()
? dayjs.tz(`${date.format('YYYY-MM-DD')} ${time}`, 'YYYY-MM-DD HH:mm', 'UTC').utc()
: null
}
+13 -13
View File
@@ -2,7 +2,11 @@ const axios = require('axios')
const cheerio = require('cheerio')
const url = require('url')
const path = require('path')
const { DateTime } = require('luxon')
const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc')
const timezone = require('dayjs/plugin/timezone')
dayjs.extend(utc)
dayjs.extend(timezone)
module.exports = {
site: 'gatotv.com',
@@ -13,18 +17,18 @@ module.exports = {
parser({ content, date }) {
let programs = []
const items = parseItems(content)
date = date.subtract(1, 'd')
date = date.subtract(1, 'day')
items.forEach((item, i) => {
const $item = cheerio.load(item)
let start = parseStart($item, date)
if (i === 0 && start.hour >= 5) {
start = start.plus({ days: 1 })
date = date.add(1, 'd')
if (i === 0 && start.hour() >= 5) {
start = start.add(1, 'day')
date = date.add(1, 'day')
}
let stop = parseStop($item, date)
if (stop < start) {
stop = stop.plus({ days: 1 })
date = date.add(1, 'd')
stop = stop.add(1, 'day')
date = date.add(1, 'day')
}
programs.push({
@@ -78,17 +82,13 @@ function parseImage($item) {
function parseStart($item, date) {
const time = $item('td:nth-child(1) > div > time').attr('datetime')
return DateTime.fromFormat(`${date.format('YYYY-MM-DD')} ${time}`, 'yyyy-MM-dd HH:mm', {
zone: 'EST'
}).toUTC()
return dayjs.tz(`${date.format('YYYY-MM-DD')} ${time}`, 'YYYY-MM-DD HH:mm', 'EST').utc()
}
function parseStop($item, date) {
const time = $item('td:nth-child(2) > div > time').attr('datetime')
return DateTime.fromFormat(`${date.format('YYYY-MM-DD')} ${time}`, 'yyyy-MM-dd HH:mm', {
zone: 'EST'
}).toUTC()
return dayjs.tz(`${date.format('YYYY-MM-DD')} ${time}`, 'YYYY-MM-DD HH:mm', 'EST').utc()
}
function parseItems(content) {
+1 -1
View File
@@ -29,7 +29,7 @@ it('can parse response', () => {
start: '2023-06-23T07:14:32.000Z',
stop: '2023-06-23T09:09:36.000Z',
title: 'Killers Within',
date: '20180101',
date: '2018-01-01T00:00:00.000Z',
description:
'With her son being held captive by a criminal gang, police officer Amanda Doyle, together with her ex-husband and three unlikely allies, takes part in a desperate plot to hold a wealthy banker and his family to ransom. But this is no ordinary family.',
icon: 'https://provider-static.plex.tv/epg/images/thumbnails/darkmatter-tv-fallback.jpg',
+3 -1
View File
@@ -3,11 +3,13 @@ const fs = require('fs')
const path = require('path')
const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc')
const timezone = require('dayjs/plugin/timezone')
const customParseFormat = require('dayjs/plugin/customParseFormat')
dayjs.extend(customParseFormat)
dayjs.extend(utc)
dayjs.extend(timezone)
jest.useFakeTimers().setSystemTime(new Date('2025-01-15'))
jest.useFakeTimers().setSystemTime(dayjs.utc('2025-01-15').valueOf())
const date = dayjs.utc('2025-01-17', 'YYYY-MM-DD').startOf('d')
const channel = {
+9 -5
View File
@@ -1,6 +1,10 @@
const cheerio = require('cheerio')
const iconv = require('iconv-lite')
const { DateTime } = require('luxon')
const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc')
const timezone = require('dayjs/plugin/timezone')
dayjs.extend(utc)
dayjs.extend(timezone)
module.exports = {
site: 'm.tv.sms.cz',
@@ -19,12 +23,12 @@ module.exports = {
let start = parseStart($item, date)
if (prev) {
if (start < prev.start) {
start = start.plus({ days: 1 })
date = date.add(1, 'd')
start = start.add(1, 'day')
date = date.add(1, 'day')
}
prev.stop = start
}
const stop = start.plus({ hours: 1 })
const stop = start.add(1, 'hour')
programs.push({
title: parseTitle($item),
description: parseDescription($item),
@@ -65,7 +69,7 @@ function parseStart($item, date) {
const timeString = $item('div > span').text().trim()
const dateString = `${date.format('MM/DD/YYYY')} ${timeString}`
return DateTime.fromFormat(dateString, 'MM/dd/yyyy HH.mm', { zone: 'Europe/Prague' }).toUTC()
return dayjs.tz(dateString, 'MM/DD/YYYY HH.mm', 'Europe/Prague').utc()
}
function parseDescription($item) {
+7 -3
View File
@@ -1,4 +1,8 @@
const { DateTime } = require('luxon')
const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc')
const timezone = require('dayjs/plugin/timezone')
dayjs.extend(utc)
dayjs.extend(timezone)
module.exports = {
site: 'meo.pt',
@@ -31,8 +35,8 @@ module.exports = {
if (!items.length) return programs
for (const item of items) {
const start = DateTime.fromISO(item.StartDate, { zone: 'Europe/Lisbon' }).toUTC()
const stop = DateTime.fromISO(item.EndDate, { zone: 'Europe/Lisbon' }).toUTC()
const start = dayjs.tz(item.StartDate, 'Europe/Lisbon').utc()
const stop = dayjs.tz(item.EndDate, 'Europe/Lisbon').utc()
const prog = {
title: item.Title || 'Sem título',
+1 -1
View File
@@ -69,7 +69,7 @@ it('can parse response', () => {
description: 'Le journal de vivre ici.',
category: 'Info',
image:
'https://experience-cache.proximustv.be/posterserver/poster/EPG/w-166_h-110/250_250_4B990CC58066A7B2A660AFA0BDDE5C41.jpg'
'https://experience-cache.cdi.streaming.proximustv.be/posterserver/poster/EPG/250_250_4B990CC58066A7B2A660AFA0BDDE5C41.jpg'
})
})
@@ -1,12 +1,16 @@
const cheerio = require('cheerio')
const { DateTime } = require('luxon')
const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc')
const timezone = require('dayjs/plugin/timezone')
dayjs.extend(utc)
dayjs.extend(timezone)
module.exports = {
delay: 5000,
site: 'programtv.onet.pl',
days: 2,
url: function ({ date, channel }) {
const currDate = DateTime.now().toUTC().startOf('day')
const currDate = dayjs().utc().startOf('day')
const day = date.diff(currDate, 'd')
return `https://programtv.onet.pl/program-tv/${channel.site_id}?dzien=${day}`
@@ -20,12 +24,12 @@ module.exports = {
let start = parseStart($item, date)
if (prev) {
if (start < prev.start) {
start = start.plus({ days: 1 })
date = date.add(1, 'd')
start = start.add(1, 'day')
date = date.add(1, 'day')
}
prev.stop = start
}
const stop = start.plus({ hours: 1 })
const stop = start.add(1, 'hour')
programs.push({
title: parseTitle($item),
description: parseDescription($item),
@@ -67,7 +71,7 @@ function parseStart($item, date) {
const timeString = $item('.hours > .hour').text()
const dateString = `${date.format('MM/DD/YYYY')} ${timeString}`
return DateTime.fromFormat(dateString, 'MM/dd/yyyy HH:mm', { zone: 'Europe/Warsaw' }).toUTC()
return dayjs.tz(dateString, 'MM/DD/YYYY HH:mm', 'Europe/Warsaw').utc()
}
function parseCategory($item) {
@@ -1,4 +1,3 @@
const MockDate = require('mockdate')
const { parser, url } = require('./programtv.onet.pl.config.js')
const fs = require('fs')
const path = require('path')
@@ -14,20 +13,26 @@ const channel = {
xmltv_id: '13thStreet.de'
}
beforeEach(() => {
jest.useFakeTimers()
})
afterEach(() => {
jest.useRealTimers()
})
it('can generate valid url', () => {
MockDate.set(dayjs.utc('2021-11-24', 'YYYY-MM-DD').startOf('d'))
jest.setSystemTime(dayjs.utc('2021-11-24', 'YYYY-MM-DD').startOf('d').valueOf())
expect(url({ channel, date })).toBe(
'https://programtv.onet.pl/program-tv/13th-street-250?dzien=0'
)
MockDate.reset()
})
it('can generate valid url for next day', () => {
MockDate.set(dayjs.utc('2021-11-23', 'YYYY-MM-DD').startOf('d'))
jest.setSystemTime(dayjs.utc('2021-11-23', 'YYYY-MM-DD').startOf('d').valueOf())
expect(url({ channel, date })).toBe(
'https://programtv.onet.pl/program-tv/13th-street-250?dzien=1'
)
MockDate.reset()
})
it('can parse response', () => {
@@ -1,12 +1,16 @@
const cheerio = require('cheerio')
const axios = require('axios')
const { DateTime } = require('luxon')
const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc')
const timezone = require('dayjs/plugin/timezone')
dayjs.extend(utc)
dayjs.extend(timezone)
module.exports = {
site: 'superguidatv.it',
days: 3,
url({ channel, date }) {
let diff = date.diff(DateTime.now().toUTC().startOf('day'), 'd')
let diff = date.diff(dayjs().utc().startOf('day'), 'd')
let day = {
0: 'oggi',
1: 'domani',
@@ -24,12 +28,12 @@ module.exports = {
let start = parseStart($item, date)
if (prev) {
if (start < prev.start) {
start = start.plus({ days: 1 })
date = date.add(1, 'd')
start = start.add(1, 'day')
date = date.add(1, 'day')
}
prev.stop = start
}
const stop = start.plus({ minutes: 30 })
const stop = start.add(30, 'minute')
programs.push({
title: parseTitle($item),
category: parseCategory($item),
@@ -92,9 +96,7 @@ function parseStart($item, date) {
.text()
.trim()
return DateTime.fromFormat(`${date.format('YYYY-MM-DD')} ${hours}`, 'yyyy-MM-dd HH:mm', {
zone: 'Europe/Rome'
}).toUTC()
return dayjs.tz(`${date.format('YYYY-MM-DD')} ${hours}`, 'YYYY-MM-DD HH:mm', 'Europe/Rome').utc()
}
function parseTitle($item) {
+10 -8
View File
@@ -1,5 +1,9 @@
const cheerio = require('cheerio')
const { DateTime } = require('luxon')
const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc')
const timezone = require('dayjs/plugin/timezone')
dayjs.extend(utc)
dayjs.extend(timezone)
module.exports = {
site: 'tivu.tv',
@@ -10,7 +14,7 @@ module.exports = {
}
},
url({ date }) {
const diff = date.diff(DateTime.now().toUTC().startOf('day'), 'd')
const diff = date.diff(dayjs().utc().startOf('day'), 'd')
return `https://www.tivu.tv/epg_ajax_sat.aspx?d=${diff}`
},
@@ -24,12 +28,12 @@ module.exports = {
if (!start) return
if (prev) {
if (start < prev.start) {
start = start.plus({ days: 1 })
date = date.add(1, 'd')
start = start.add(1, 'day')
date = date.add(1, 'day')
}
prev.stop = start
}
const stop = start.plus({ minutes: 30 })
const stop = start.add(30, 'minute')
programs.push({
title: parseTitle($item),
start,
@@ -76,9 +80,7 @@ function parseStart($item, date) {
const [, , time] = $item('a').html().split('<br>')
if (!time) return null
return DateTime.fromFormat(`${date.format('YYYY-MM-DD')} ${time}`, 'yyyy-MM-dd HH:mm', {
zone: 'Europe/Rome'
}).toUTC()
return dayjs.tz(`${date.format('YYYY-MM-DD')} ${time}`, 'YYYY-MM-DD HH:mm', 'Europe/Rome').utc()
}
function parseItems(content, channel) {
@@ -1,4 +1,8 @@
const { DateTime } = require('luxon')
const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc')
const timezone = require('dayjs/plugin/timezone')
dayjs.extend(utc)
dayjs.extend(timezone)
module.exports = {
site: 'turksatkablo.com.tr',
@@ -21,13 +25,13 @@ module.exports = {
const prev = programs[programs.length - 1]
let start = parseStart(item, date)
if (prev && start < prev.start) {
start = start.plus({ days: 1 })
date = date.add(1, 'd')
start = start.add(1, 'day')
date = date.add(1, 'day')
}
let stop = parseStop(item, date)
if (prev && stop < start) {
stop = stop.plus({ days: 1 })
date = date.add(1, 'd')
stop = stop.add(1, 'day')
date = date.add(1, 'day')
}
programs.push({
title: item.b,
@@ -65,13 +69,13 @@ module.exports = {
function parseStart(item, date) {
const time = `${date.format('YYYY-MM-DD')} ${item.c}`
return DateTime.fromFormat(time, 'yyyy-MM-dd HH:mm', { zone: 'Europe/Istanbul' }).toUTC()
return dayjs.tz(time, 'YYYY-MM-DD HH:mm', 'Europe/Istanbul').utc()
}
function parseStop(item, date) {
const time = `${date.format('YYYY-MM-DD')} ${item.d}`
return DateTime.fromFormat(time, 'yyyy-MM-dd HH:mm', { zone: 'Europe/Istanbul' }).toUTC()
return dayjs.tz(time, 'YYYY-MM-DD HH:mm', 'Europe/Istanbul').utc()
}
function parseItems(content, channel) {
@@ -1,6 +1,10 @@
const cheerio = require('cheerio')
const axios = require('axios')
const { DateTime } = require('luxon')
const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc')
const timezone = require('dayjs/plugin/timezone')
dayjs.extend(utc)
dayjs.extend(timezone)
const API_ENDPOINT = 'https://tv-programme.telecablesat.fr/chaine'
const headers = {
@@ -41,12 +45,12 @@ module.exports = {
let start = parseStart($item, date)
if (prev) {
if (start < prev.start) {
start = start.plus({ days: 1 })
date = date.add(1, 'd')
start = start.add(1, 'day')
date = date.add(1, 'day')
}
prev.stop = start
}
const stop = start.plus({ hours: 1 })
const stop = start.add(1, 'hour')
programs.push({
title: parseTitle($item),
description: parseDescription($item),
@@ -87,9 +91,7 @@ function parseStart($item, date) {
const timeString = $item('.schedule-hour').text()
if (!timeString) return null
return DateTime.fromFormat(`${date.format('YYYY-MM-DD')} ${timeString}`, 'yyyy-MM-dd HH:mm', {
zone: 'Europe/Paris'
}).toUTC()
return dayjs.tz(`${date.format('YYYY-MM-DD')} ${timeString}`, 'YYYY-MM-DD HH:mm', 'Europe/Paris').utc()
}
function parseImage($item) {
+9 -5
View File
@@ -1,6 +1,10 @@
const { DateTime } = require('luxon')
const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc')
const timezone = require('dayjs/plugin/timezone')
const axios = require('axios')
const uniqBy = require('lodash.uniqby')
dayjs.extend(utc)
dayjs.extend(timezone)
module.exports = {
site: 'tv.mail.ru',
@@ -19,12 +23,12 @@ module.exports = {
let start = parseStart(item, date)
if (prev) {
if (start < prev.start) {
start = start.plus({ days: 1 })
date = date.add(1, 'd')
start = start.add(1, 'day')
date = date.add(1, 'day')
}
prev.stop = start
}
const stop = start.plus({ hours: 1 })
const stop = start.add(1, 'hour')
programs.push({
title: item.name,
category: parseCategory(item),
@@ -84,7 +88,7 @@ async function getTotalPageCount(region) {
function parseStart(item, date) {
const dateString = `${date.format('YYYY-MM-DD')} ${item.start}`
return DateTime.fromFormat(dateString, 'yyyy-MM-dd HH:mm', { zone: 'Europe/Moscow' }).toUTC()
return dayjs.tz(dateString, 'YYYY-MM-DD HH:mm', 'Europe/Moscow').utc()
}
function parseCategory(item) {
+10 -8
View File
@@ -1,13 +1,17 @@
const cheerio = require('cheerio')
const axios = require('axios')
const { DateTime } = require('luxon')
const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc')
const timezone = require('dayjs/plugin/timezone')
dayjs.extend(utc)
dayjs.extend(timezone)
module.exports = {
site: 'tvgids.nl',
days: 2,
url: function ({ date, channel }) {
const path =
DateTime.utc().day === DateTime.fromMillis(date.valueOf()).day
dayjs().utc().day() === dayjs(date).utc().day()
? ''
: `${date.format('DD-MM-YYYY')}/`
@@ -23,12 +27,12 @@ module.exports = {
let start = parseStart($item, date)
if (prev) {
if (start < prev.start) {
start = start.plus({ days: 1 })
date = date.add(1, 'd')
start = start.add(1, 'day')
date = date.add(1, 'day')
}
prev.stop = start
}
const stop = start.plus({ minutes: 30 })
const stop = start.add(30, 'minute')
programs.push({
title: parseTitle($item),
description: parseDescription($item),
@@ -75,9 +79,7 @@ function parseImage($item) {
function parseStart($item, date) {
const time = $item('.program__starttime').clone().children().remove().end().text().trim()
return DateTime.fromFormat(`${date.format('YYYY-MM-DD')} ${time}`, 'yyyy-MM-dd HH:mm', {
zone: 'Europe/Amsterdam'
}).toUTC()
return dayjs.tz(`${date.format('YYYY-MM-DD')} ${time}`, 'YYYY-MM-DD HH:mm', 'Europe/Amsterdam').utc()
}
function parseItems(content) {
+1 -1
View File
@@ -14,7 +14,7 @@ const channel = {
}
it('can generate valid url', () => {
jest.useFakeTimers().setSystemTime(new Date('2025-01-17'))
jest.useFakeTimers().setSystemTime(dayjs('2025-01-17').valueOf())
expect(url({ date, channel })).toBe('https://www.tvgids.nl/gids/19-01-2025/npo1')
})
+8 -6
View File
@@ -1,7 +1,11 @@
const cheerio = require('cheerio')
const axios = require('axios')
const { DateTime } = require('luxon')
const uniqBy = require('lodash.uniqby')
const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc')
const timezone = require('dayjs/plugin/timezone')
dayjs.extend(utc)
dayjs.extend(timezone)
module.exports = {
site: 'tvhebdo.com',
@@ -20,11 +24,11 @@ module.exports = {
let start = parseStart($item, date)
if (prev) {
if (start < prev.start) {
start = start.plus({ days: 1 })
start = start.add(1, 'day')
}
prev.stop = start
}
let stop = start.plus({ minutes: 30 })
let stop = start.add(30, 'minute')
programs.push({
title: parseTitle($item),
start,
@@ -83,9 +87,7 @@ function parseTitle($item) {
function parseStart($item, date) {
const time = $item('.heure').text()
return DateTime.fromFormat(`${date.format('YYYY-MM-DD')} ${time}`, 'yyyy-MM-dd HH:mm', {
zone: 'America/Toronto'
}).toUTC()
return dayjs.tz(`${date.format('YYYY-MM-DD')} ${time}`, 'YYYY-MM-DD HH:mm', 'America/Toronto').utc()
}
function parseItems(content) {
+7 -3
View File
@@ -1,6 +1,10 @@
const cheerio = require('cheerio')
const axios = require('axios')
const { DateTime } = require('luxon')
const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc')
const timezone = require('dayjs/plugin/timezone')
dayjs.extend(utc)
dayjs.extend(timezone)
module.exports = {
site: 'tvinsider.com',
@@ -20,7 +24,7 @@ module.exports = {
if (prev) {
prev.stop = start
}
const stop = start.plus({ minute: 30 })
const stop = start.add(30, 'minute')
programs.push({
title: parseTitle($item),
@@ -114,7 +118,7 @@ function parseStart($item, date) {
let time = $item('time').text().trim()
time = `${date.format('YYYY-MM-DD')} ${time}`
return DateTime.fromFormat(time, 'yyyy-MM-dd t', { zone: 'America/New_York' }).toUTC()
return dayjs.tz(time, 'YYYY-MM-DD h:mm A', 'EST').utc()
}
function parseItems(content, date) {
+9 -7
View File
@@ -1,6 +1,10 @@
const cheerio = require('cheerio')
const axios = require('axios')
const { DateTime } = require('luxon')
const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc')
const timezone = require('dayjs/plugin/timezone')
dayjs.extend(utc)
dayjs.extend(timezone)
module.exports = {
site: 'tvmustra.hu',
@@ -18,12 +22,12 @@ module.exports = {
if (!start) return
if (prev) {
if (start < prev.start) {
start = start.plus({ days: 1 })
date = date.add(1, 'd')
start = start.add(1, 'day')
date = date.add(1, 'day')
}
prev.stop = start
}
const stop = start.plus({ minute: 30 })
const stop = start.add(30, 'minute')
const title = parseTitle($item)
programs.push({
@@ -67,9 +71,7 @@ function parseTitle($item) {
function parseStart($item, date) {
const time = $item('div[class^="musor_lista_idopont"]').first().text().trim()
return DateTime.fromFormat(`${date.format('YYYY-MM-DD')} ${time}`, 'yyyy-MM-dd HH:mm', {
zone: 'Europe/Budapest'
}).toUTC()
return dayjs.tz(`${date.format('YYYY-MM-DD')} ${time}`, 'YYYY-MM-DD HH:mm', 'Europe/Budapest').utc()
}
function parseItems(content) {
+18 -14
View File
@@ -1,5 +1,9 @@
const { DateTime } = require('luxon')
const axios = require('axios')
const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc')
const timezone = require('dayjs/plugin/timezone')
dayjs.extend(utc)
dayjs.extend(timezone)
const API_ENDPOINT = 'https://cdn.pt.vtv.vodafone.com/epg'
@@ -18,25 +22,25 @@ module.exports = {
headers
},
url: function ({ channel, date }) {
const datetime = DateTime.fromJSDate(date.toDate()).setZone('Europe/Lisbon')
const formattedMonth = datetime.month < 10 ? `0${datetime.month}` : datetime.month
const formattedDay = datetime.day < 10 ? `0${datetime.day}` : datetime.day
return `${API_ENDPOINT}/${channel.site_id}/${date.year()}/${formattedMonth}/${formattedDay}/00-06`
const datetime = dayjs(date.toDate()).tz('Europe/Lisbon')
const formattedMonth = datetime.month() + 1 < 10 ? `0${datetime.month() + 1}` : datetime.month() + 1
const formattedDay = datetime.date() < 10 ? `0${datetime.date()}` : datetime.date()
return `${API_ENDPOINT}/${channel.site_id}/${datetime.year()}/${formattedMonth}/${formattedDay}/00-06`
},
async parser({ content, date, channel }) {
let programs = []
let items = parseItems(content)
if (items.length === 0) return programs
const datetime = DateTime.fromJSDate(date.toDate()).setZone('Europe/Lisbon')
const formattedMonth = datetime.month < 10 ? `0${datetime.month}` : datetime.month
const formattedDay = datetime.day < 10 ? `0${datetime.day}` : datetime.day
const datetime = dayjs(date.toDate()).tz('Europe/Lisbon')
const formattedMonth = datetime.month() + 1 < 10 ? `0${datetime.month() + 1}` : datetime.month() + 1
const formattedDay = datetime.date() < 10 ? `0${datetime.date()}` : datetime.date()
// Fetch the remaining 3 periods to get a full day schedule
const promises = [
axios.get(`${API_ENDPOINT}/${channel.site_id}/${date.year()}/${formattedMonth}/${formattedDay}/06-12`, { headers }),
axios.get(`${API_ENDPOINT}/${channel.site_id}/${date.year()}/${formattedMonth}/${formattedDay}/12-18`, { headers }),
axios.get(`${API_ENDPOINT}/${channel.site_id}/${date.year()}/${formattedMonth}/${formattedDay}/18-00`, { headers })
axios.get(`${API_ENDPOINT}/${channel.site_id}/${datetime.year()}/${formattedMonth}/${formattedDay}/06-12`, { headers }),
axios.get(`${API_ENDPOINT}/${channel.site_id}/${datetime.year()}/${formattedMonth}/${formattedDay}/12-18`, { headers }),
axios.get(`${API_ENDPOINT}/${channel.site_id}/${datetime.year()}/${formattedMonth}/${formattedDay}/18-00`, { headers })
]
await Promise.allSettled(promises).then(results => {
@@ -49,10 +53,10 @@ module.exports = {
for (let item of items) {
if (!item.startDate || !item.endDate) continue
let start = DateTime.fromSeconds(item.startDate, { zone: 'UTC' }).toUTC()
let stop = DateTime.fromSeconds(item.endDate, { zone: 'UTC' }).toUTC()
let start = dayjs.unix(item.startDate).utc()
let stop = dayjs.unix(item.endDate).utc()
if (stop < start) {
stop = stop.plus({ days: 1 })
stop = stop.add(1, 'day')
}
const prog = {
title: item.name || 'Sem título',
+7 -3
View File
@@ -1,5 +1,9 @@
const axios = require('axios')
const { DateTime } = require('luxon')
const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc')
const timezone = require('dayjs/plugin/timezone')
dayjs.extend(utc)
dayjs.extend(timezone)
module.exports = {
site: 'wavve.com',
@@ -47,11 +51,11 @@ module.exports = {
}
function parseStart(item) {
return DateTime.fromFormat(item.starttime, 'yyyy-MM-dd HH:mm', { zone: 'Asia/Seoul' }).toUTC()
return dayjs.tz(item.starttime, 'YYYY-MM-DD HH:mm', 'Asia/Seoul').utc()
}
function parseStop(item) {
return DateTime.fromFormat(item.endtime, 'yyyy-MM-dd HH:mm', { zone: 'Asia/Seoul' }).toUTC()
return dayjs.tz(item.endtime, 'YYYY-MM-DD HH:mm', 'Asia/Seoul').utc()
}
function parseItems(content) {
+7 -3
View File
@@ -1,5 +1,9 @@
const { DateTime } = require('luxon')
const axios = require('axios')
const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc')
const timezone = require('dayjs/plugin/timezone')
dayjs.extend(utc)
dayjs.extend(timezone)
module.exports = {
site: 'zap.co.ao',
@@ -18,8 +22,8 @@ module.exports = {
title: item.programName,
description: item.programDescription,
category: item.categoryName,
start: DateTime.fromSeconds(item.utcBeginDate).toUTC(),
stop: DateTime.fromSeconds(item.utcEndDate).toUTC()
start: dayjs.unix(item.utcBeginDate).utc(),
stop: dayjs.unix(item.utcEndDate).utc()
})
})
+47 -41
View File
@@ -1,41 +1,47 @@
import { execSync } from 'child_process'
import fs from 'fs-extra'
import { pathToFileURL } from 'node:url'
const ENV_VAR = 'cross-env SITES_DIR=tests/__data__/output/sites'
beforeEach(() => {
fs.emptyDirSync('tests/__data__/output')
fs.mkdirSync('tests/__data__/output/sites')
})
it('can create new site config from template', () => {
const cmd = `${ENV_VAR} npm run sites:init --- example.com`
const stdout = execSync(cmd, { encoding: 'utf8' })
if (process.env.DEBUG === 'true') console.log(cmd, stdout)
expect(exists('tests/__data__/output/sites/example.com')).toBe(true)
expect(exists('tests/__data__/output/sites/example.com/example.com.test.js')).toBe(true)
expect(exists('tests/__data__/output/sites/example.com/example.com.config.js')).toBe(true)
expect(exists('tests/__data__/output/sites/example.com/readme.md')).toBe(true)
expect(content('tests/__data__/output/sites/example.com/example.com.test.js')).toEqual(
content('tests/__data__/expected/sites_init/example.com.test.js')
)
expect(content('tests/__data__/output/sites/example.com/example.com.config.js')).toEqual(
content('tests/__data__/expected/sites_init/example.com.config.js')
)
expect(content('tests/__data__/output/sites/example.com/readme.md')).toEqual(
content('tests/__data__/expected/sites_init/readme.md')
)
})
function content(filepath: string) {
return fs.readFileSync(pathToFileURL(filepath), {
encoding: 'utf8'
})
}
function exists(filepath: string) {
return fs.existsSync(pathToFileURL(filepath))
}
import { execSync } from 'child_process'
import fs from 'fs-extra'
import { pathToFileURL } from 'node:url'
const ENV_VAR = 'cross-env SITES_DIR=tests/__data__/output/sites'
beforeEach(() => {
fs.emptyDirSync('tests/__data__/output')
fs.mkdirSync('tests/__data__/output/sites')
})
it('can create new site config from template', () => {
const cmd = `${ENV_VAR} npm run sites:init --- example.com`
const stdout = execSync(cmd, { encoding: 'utf8' })
if (process.env.DEBUG === 'true') console.log(cmd, stdout)
expect(exists('tests/__data__/output/sites/example.com')).toBe(true)
expect(exists('tests/__data__/output/sites/example.com/example.com.test.js')).toBe(true)
expect(exists('tests/__data__/output/sites/example.com/example.com.config.js')).toBe(true)
expect(exists('tests/__data__/output/sites/example.com/readme.md')).toBe(true)
expect(content('tests/__data__/output/sites/example.com/example.com.test.js')).toEqual(
content('tests/__data__/expected/sites_init/example.com.test.js')
)
expect(content('tests/__data__/output/sites/example.com/example.com.config.js')).toEqual(
content('tests/__data__/expected/sites_init/example.com.config.js')
)
expect(content('tests/__data__/output/sites/example.com/readme.md')).toEqual(
content('tests/__data__/expected/sites_init/readme.md')
)
})
function content(filepath: string) {
return normalizeLineEndings(
fs.readFileSync(pathToFileURL(filepath), {
encoding: 'utf8'
})
)
}
function normalizeLineEndings(data: string) {
return data.replace(/\r\n/g, '\n').replace(/\r/g, '\n')
}
function exists(filepath: string) {
return fs.existsSync(pathToFileURL(filepath))
}
+32 -28
View File
@@ -1,28 +1,32 @@
import { execSync } from 'child_process'
import fs from 'fs-extra'
import { pathToFileURL } from 'node:url'
const ENV_VAR = 'cross-env SITES_DIR=tests/__data__/input/sites_update/sites ROOT_DIR=tests/__data__/output'
beforeEach(() => {
fs.emptyDirSync('tests/__data__/output')
})
it('can update SITES.md', () => {
const cmd = `${ENV_VAR} npm run sites:update`
const stdout = execSync(cmd, { encoding: 'utf8' })
if (process.env.DEBUG === 'true') console.log(cmd, stdout)
expect(content('tests/__data__/output/SITES.md')).toEqual(
content('tests/__data__/expected/sites_update/SITES.md')
)
})
function content(filepath: string) {
const data = fs.readFileSync(pathToFileURL(filepath), {
encoding: 'utf8'
})
return JSON.stringify(data)
}
import { execSync } from 'child_process'
import fs from 'fs-extra'
import { pathToFileURL } from 'node:url'
const ENV_VAR = 'cross-env SITES_DIR=tests/__data__/input/sites_update/sites ROOT_DIR=tests/__data__/output'
beforeEach(() => {
fs.emptyDirSync('tests/__data__/output')
})
it('can update SITES.md', () => {
const cmd = `${ENV_VAR} npm run sites:update`
const stdout = execSync(cmd, { encoding: 'utf8' })
if (process.env.DEBUG === 'true') console.log(cmd, stdout)
expect(content('tests/__data__/output/SITES.md')).toEqual(
content('tests/__data__/expected/sites_update/SITES.md')
)
})
function content(filepath: string) {
const data = fs.readFileSync(pathToFileURL(filepath), {
encoding: 'utf8'
})
return JSON.stringify(normalizeLineEndings(data))
}
function normalizeLineEndings(data: string) {
return data.replace(/\r\n/g, '\n').replace(/\r/g, '\n')
}