Skip to content

Commit

Permalink
Fixes parsing of ISO date substrings
Browse files Browse the repository at this point in the history
  • Loading branch information
evoactivity committed Aug 18, 2023
1 parent 24c6db3 commit 53395c5
Show file tree
Hide file tree
Showing 5 changed files with 209 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .changeset/neat-horses-lick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@iptv/xmltv': patch
---

Fixes parsing of ISO date substrings (YYYY, YYYYMM, YYYYMMDD) which are valid for the programme <date> tag
13 changes: 12 additions & 1 deletion src/parser.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { XmltvDom } from './types';
import { dateToXmltvUtcTimestamp, xmltvTimestampToUtcDate } from './utils.js';

/**
* The MIT License (MIT)
Expand Down Expand Up @@ -73,7 +74,7 @@ export function parser(xmltvString: string): XmltvDom {
'\nColumn: ' +
(parsedText[parsedText.length - 1].length + 1) +
'\nChar: ' +
xmltvString[pos],
xmltvString[pos]
);
}

Expand Down Expand Up @@ -209,6 +210,16 @@ export function parser(xmltvString: string): XmltvDom {
pos++;
}

if (tagName === 'date') {
return {
tagName,
attributes,
children: (children as string[]).map((child: string) =>
dateToXmltvUtcTimestamp(xmltvTimestampToUtcDate(child))
),
};
}

return {
tagName,
attributes,
Expand Down
152 changes: 151 additions & 1 deletion tests/__snapshots__/main.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,57 @@ exports[`Parses a XMLTV file > Fully parses to a JS object 1`] = `
},
],
},
{
"channel": "channel_one",
"date": 2022-04-02T00:00:00.000Z,
"desc": [
{
"_value": "This programme entry showcases a programme with no credits",
"lang": "en",
},
],
"start": 2022-03-31T18:00:00.000Z,
"stop": 2022-03-31T19:00:00.000Z,
"title": [
{
"_value": "Programme Three: YYYYMMDD",
},
],
},
{
"channel": "channel_one",
"date": 2022-04-01T00:00:00.000Z,
"desc": [
{
"_value": "This programme entry showcases a programme with no credits",
"lang": "en",
},
],
"start": 2022-03-31T18:00:00.000Z,
"stop": 2022-03-31T19:00:00.000Z,
"title": [
{
"_value": "Programme Four: YYYYMM",
},
],
},
{
"channel": "channel_one",
"date": 2022-01-01T00:00:00.000Z,
"desc": [
{
"_value": "This programme entry showcases a programme with no credits",
"lang": "en",
},
],
"start": 2022-03-31T18:00:00.000Z,
"stop": 2022-03-31T19:00:00.000Z,
"title": [
{
"_value": "Programme Five: YYYY",
},
],
},
],
"sourceDataUrl": "example.com/a",
"sourceInfoName": "example",
Expand Down Expand Up @@ -888,10 +939,109 @@ exports[`Parses a XMLTV file > it can convert an object into the dom structure 1
],
"tagName": "programme",
},
{
"attributes": {
"channel": "channel_one",
"start": "20220331180000 +0000",
"stop": "20220331190000 +0000",
},
"children": [
{
"attributes": {},
"children": [
"Programme Three: YYYYMMDD",
],
"tagName": "title",
},
{
"attributes": {
"lang": "en",
},
"children": [
"This programme entry showcases a programme with no credits",
],
"tagName": "desc",
},
{
"attributes": {},
"children": [
"20220402000000 +0000",
],
"tagName": "date",
},
],
"tagName": "programme",
},
{
"attributes": {
"channel": "channel_one",
"start": "20220331180000 +0000",
"stop": "20220331190000 +0000",
},
"children": [
{
"attributes": {},
"children": [
"Programme Four: YYYYMM",
],
"tagName": "title",
},
{
"attributes": {
"lang": "en",
},
"children": [
"This programme entry showcases a programme with no credits",
],
"tagName": "desc",
},
{
"attributes": {},
"children": [
"20220401000000 +0000",
],
"tagName": "date",
},
],
"tagName": "programme",
},
{
"attributes": {
"channel": "channel_one",
"start": "20220331180000 +0000",
"stop": "20220331190000 +0000",
},
"children": [
{
"attributes": {},
"children": [
"Programme Five: YYYY",
],
"tagName": "title",
},
{
"attributes": {
"lang": "en",
},
"children": [
"This programme entry showcases a programme with no credits",
],
"tagName": "desc",
},
{
"attributes": {},
"children": [
"20220101000000 +0000",
],
"tagName": "date",
},
],
"tagName": "programme",
},
],
"tagName": "tv",
},
]
`;

exports[`Parses a XMLTV file > it can convert back to xml 1`] = `"<?xml version=\\"1.0\\" encoding=\\"UTF-8\\"?><!DOCTYPE tv SYSTEM \\"xmltv.dtd\\"><tv date=\\"20220401000000 +0000\\" source-info-name=\\"example\\" source-info-url=\\"example.com\\" source-data-url=\\"example.com/a\\" generator-info-name=\\"Example Generator\\" generator-info-url=\\"example generator url\\"><channel id=\\"channel_one\\"><display-name lang=\\"en\\">Channel One</display-name><icon src=\\"https://example.com/channel_one_icon.jpg\\" width=\\"100\\" height=\\"100\\"/><url system=\\"example\\">https://example.com/channel_one</url><url system=\\"other_system\\">https://example.com/channel_one_alternate</url></channel><channel id=\\"channel_two\\"><display-name>Channel Two: Minimum valid channel</display-name></channel><programme start=\\"20220331180000 +0000\\" stop=\\"20220331190000 +0000\\" channel=\\"channel_one\\" pdc-start=\\"20220331180000 +0000\\" vps-start=\\"20220331180000 +0000\\" showview=\\"12345\\" videoplus=\\"67890\\" clumpidx=\\"0/1\\"><title lang=\\"en\\">Programme One</title><sub-title lang=\\"en\\">Pilot</sub-title><desc lang=\\"en\\">This programme entry showcases all possible features of the DTD</desc><credits><director>Samuel Jones</director><actor role=\\"Walter Johnson\\">David Thompson</actor><actor role=\\"Karl James\\" guest=\\"yes\\">Ryan Lee<image type=\\"person\\">https://www.example.com/xxx.jpg</image><url system=\\"moviedb\\">https://www.example.com/person/204</url></actor><writer>Samuel Jones</writer><adapter>William Brown</adapter><producer>Emily Davis</producer><composer>Max Wright</composer><editor>Nora Peterson</editor><presenter>Amanda Johnson</presenter><commentator>James Wilson</commentator><guest>Lucas Martin</guest><guest>Emily Parker</guest><guest>Oliver Nelson</guest></credits><date>20220401000000 +0000</date><category lang=\\"en\\">Crime</category><category lang=\\"en\\">Drama</category><keyword lang=\\"en\\">methamphetamine</keyword><keyword lang=\\"en\\">cancer</keyword><language>English</language><orig-language lang=\\"en\\">French</orig-language><length units=\\"minutes\\">60</length><icon src=\\"https://example.com/programme_one_icon.jpg\\" width=\\"100\\" height=\\"100\\"/><url system=\\"tvdb\\">https://example.com/programme_one</url><url>https://example.com/programme_one_2</url><country>US</country><episode-num system=\\"onscreen\\">S01E01</episode-num><episode-num system=\\"xmltv_ns\\">1.1.</episode-num><video><present>yes</present><colour>no</colour><aspect>16:9</aspect><quality>HDTV</quality></video><audio><present>yes</present><stereo>Dolby Digital</stereo></audio><previously-shown start=\\"20220331180000 +0000\\" channel=\\"channel_two\\"/><premiere>First time on British TV</premiere><last-chance lang=\\"en\\">Last time on this channel</last-chance><new/><subtitles type=\\"teletext\\"><language>English</language></subtitles><subtitles type=\\"onscreen\\"><language lang=\\"en\\">Spanish</language></subtitles><rating system=\\"BBFC\\"><value>15</value><icon src=\\"15_symbol.png\\"/></rating><star-rating system=\\"TV Guide\\"><value>4/5</value><icon src=\\"stars.png\\"/></star-rating><review type=\\"text\\" source=\\"Rotten Tomatoes\\" reviewer=\\"Joe Bloggs\\" lang=\\"en\\">This is a fantastic show!</review><review type=\\"url\\" source=\\"Rotten Tomatoes\\" reviewer=\\"Joe Bloggs\\" lang=\\"en\\">https://example.com/programme_one_review</review><image type=\\"poster\\" size=\\"1\\" orient=\\"P\\" system=\\"tvdb\\">https://tvdb.com/programme_one_poster_1.jpg</image><image type=\\"backdrop\\" size=\\"3\\" orient=\\"L\\" system=\\"tvdb\\">https://tvdb.com/programme_one_backdrop_3.jpg</image></programme><programme start=\\"20220331180000 +0000\\" channel=\\"channel_one\\"><title>Programme Two: The minimum valid programme</title></programme></tv>"`;
exports[`Parses a XMLTV file > it can convert back to xml 1`] = `"<?xml version=\\"1.0\\" encoding=\\"UTF-8\\"?><!DOCTYPE tv SYSTEM \\"xmltv.dtd\\"><tv date=\\"20220401000000 +0000\\" source-info-name=\\"example\\" source-info-url=\\"example.com\\" source-data-url=\\"example.com/a\\" generator-info-name=\\"Example Generator\\" generator-info-url=\\"example generator url\\"><channel id=\\"channel_one\\"><display-name lang=\\"en\\">Channel One</display-name><icon src=\\"https://example.com/channel_one_icon.jpg\\" width=\\"100\\" height=\\"100\\"/><url system=\\"example\\">https://example.com/channel_one</url><url system=\\"other_system\\">https://example.com/channel_one_alternate</url></channel><channel id=\\"channel_two\\"><display-name>Channel Two: Minimum valid channel</display-name></channel><programme start=\\"20220331180000 +0000\\" stop=\\"20220331190000 +0000\\" channel=\\"channel_one\\" pdc-start=\\"20220331180000 +0000\\" vps-start=\\"20220331180000 +0000\\" showview=\\"12345\\" videoplus=\\"67890\\" clumpidx=\\"0/1\\"><title lang=\\"en\\">Programme One</title><sub-title lang=\\"en\\">Pilot</sub-title><desc lang=\\"en\\">This programme entry showcases all possible features of the DTD</desc><credits><director>Samuel Jones</director><actor role=\\"Walter Johnson\\">David Thompson</actor><actor role=\\"Karl James\\" guest=\\"yes\\">Ryan Lee<image type=\\"person\\">https://www.example.com/xxx.jpg</image><url system=\\"moviedb\\">https://www.example.com/person/204</url></actor><writer>Samuel Jones</writer><adapter>William Brown</adapter><producer>Emily Davis</producer><composer>Max Wright</composer><editor>Nora Peterson</editor><presenter>Amanda Johnson</presenter><commentator>James Wilson</commentator><guest>Lucas Martin</guest><guest>Emily Parker</guest><guest>Oliver Nelson</guest></credits><date>20220401000000 +0000</date><category lang=\\"en\\">Crime</category><category lang=\\"en\\">Drama</category><keyword lang=\\"en\\">methamphetamine</keyword><keyword lang=\\"en\\">cancer</keyword><language>English</language><orig-language lang=\\"en\\">French</orig-language><length units=\\"minutes\\">60</length><icon src=\\"https://example.com/programme_one_icon.jpg\\" width=\\"100\\" height=\\"100\\"/><url system=\\"tvdb\\">https://example.com/programme_one</url><url>https://example.com/programme_one_2</url><country>US</country><episode-num system=\\"onscreen\\">S01E01</episode-num><episode-num system=\\"xmltv_ns\\">1.1.</episode-num><video><present>yes</present><colour>no</colour><aspect>16:9</aspect><quality>HDTV</quality></video><audio><present>yes</present><stereo>Dolby Digital</stereo></audio><previously-shown start=\\"20220331180000 +0000\\" channel=\\"channel_two\\"/><premiere>First time on British TV</premiere><last-chance lang=\\"en\\">Last time on this channel</last-chance><new/><subtitles type=\\"teletext\\"><language>English</language></subtitles><subtitles type=\\"onscreen\\"><language lang=\\"en\\">Spanish</language></subtitles><rating system=\\"BBFC\\"><value>15</value><icon src=\\"15_symbol.png\\"/></rating><star-rating system=\\"TV Guide\\"><value>4/5</value><icon src=\\"stars.png\\"/></star-rating><review type=\\"text\\" source=\\"Rotten Tomatoes\\" reviewer=\\"Joe Bloggs\\" lang=\\"en\\">This is a fantastic show!</review><review type=\\"url\\" source=\\"Rotten Tomatoes\\" reviewer=\\"Joe Bloggs\\" lang=\\"en\\">https://example.com/programme_one_review</review><image type=\\"poster\\" size=\\"1\\" orient=\\"P\\" system=\\"tvdb\\">https://tvdb.com/programme_one_poster_1.jpg</image><image type=\\"backdrop\\" size=\\"3\\" orient=\\"L\\" system=\\"tvdb\\">https://tvdb.com/programme_one_backdrop_3.jpg</image></programme><programme start=\\"20220331180000 +0000\\" channel=\\"channel_one\\"><title>Programme Two: The minimum valid programme</title></programme><programme start=\\"20220331180000 +0000\\" stop=\\"20220331190000 +0000\\" channel=\\"channel_one\\"><title>Programme Three: YYYYMMDD</title><desc lang=\\"en\\">This programme entry showcases a programme with no credits</desc><date>20220402000000 +0000</date></programme><programme start=\\"20220331180000 +0000\\" stop=\\"20220331190000 +0000\\" channel=\\"channel_one\\"><title>Programme Four: YYYYMM</title><desc lang=\\"en\\">This programme entry showcases a programme with no credits</desc><date>20220401000000 +0000</date></programme><programme start=\\"20220331180000 +0000\\" stop=\\"20220331190000 +0000\\" channel=\\"channel_one\\"><title>Programme Five: YYYY</title><desc lang=\\"en\\">This programme entry showcases a programme with no credits</desc><date>20220101000000 +0000</date></programme></tv>"`;
15 changes: 15 additions & 0 deletions tests/fixtures/example.xml
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,19 @@
channel="channel_one">
<title>Programme Two: The minimum valid programme</title>
</programme>
<programme start="20220331180000 +0000" stop="20220331190000 +0000" channel="channel_one">
<title>Programme Three: YYYYMMDD</title>
<desc lang="en">This programme entry showcases a programme with no credits</desc>
<date>20220402</date>
</programme>
<programme start="20220331180000 +0000" stop="20220331190000 +0000" channel="channel_one">
<title>Programme Four: YYYYMM</title>
<desc lang="en">This programme entry showcases a programme with no credits</desc>
<date>202204</date>
</programme>
<programme start="20220331180000 +0000" stop="20220331190000 +0000" channel="channel_one">
<title>Programme Five: YYYY</title>
<desc lang="en">This programme entry showcases a programme with no credits</desc>
<date>2022</date>
</programme>
</tv>
26 changes: 26 additions & 0 deletions tests/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import fs from 'node:fs';
import { describe, expect, test } from 'vitest';
import { objectToDom, parseXmltv, writeXmltv } from '../src/main';


const fileAsString = fs.readFileSync(`./tests/fixtures/example.xml`, {
encoding: 'utf-8',
});
Expand Down Expand Up @@ -58,3 +59,28 @@ Char: >`);
});
});
});

describe('Correctly parses ISO date substrings', () => {
test('Parses a date string with no timezone', () => {
const parsed = parseXmltv(fileAsString);
expect(parsed.programmes?.[0].start).toEqual(new Date('2022-03-31T18:00:00.000Z'));
});

test('Parses a date string with a timezone', () => {
const parsed = parseXmltv(fileAsString);
expect(parsed.programmes?.[0].stop).toEqual(new Date('2022-03-31T19:00:00.000Z'));
});

test('Parses a date string with just year, month, day YYYYMMDD', () => {
const parsed = parseXmltv(fileAsString);
expect(parsed.programmes?.[2].date).toEqual(new Date('2022-04-02T00:00:00.000Z'));
});
test('Parses a date string with just year and month YYYYMM', () => {
const parsed = parseXmltv(fileAsString);
expect(parsed.programmes?.[3].date).toEqual(new Date('2022-04-01T00:00:00.000Z'));
});
test('Parses a date string with just year YYYY', () => {
const parsed = parseXmltv(fileAsString);
expect(parsed.programmes?.[4].date).toEqual(new Date('2022-01-01T00:00:00.000Z'));
});
});

0 comments on commit 53395c5

Please sign in to comment.