diff --git a/sites/s.mxtv.jp/README.md b/sites/s.mxtv.jp/README.md new file mode 100644 index 000000000..ffe7c67b7 --- /dev/null +++ b/sites/s.mxtv.jp/README.md @@ -0,0 +1,28 @@ +# s.mxtv.jp + + + +## Index + +- [Index](#index) +- [Download the guide](#download-the-guide) +- [Update channel list](#update-channel-list) +- [Test](#test) + +## Download the guide + +```sh +npm run grab -- --site=s.mxtv.jp +``` + +## Update channel list + +```sh +npm run channels:parse -- --config=./sites/s.mxtv.jp/s.mxtv.jp.config.js --output=./sites/s.mxtv.jp/s.mxtv.jp.channels.xml +``` + +## Test + +```sh +npm test -- s.mxtv.jp +``` diff --git a/sites/s.mxtv.jp/s.mxtv.jp.channels.xml b/sites/s.mxtv.jp/s.mxtv.jp.channels.xml new file mode 100644 index 000000000..36a0de801 --- /dev/null +++ b/sites/s.mxtv.jp/s.mxtv.jp.channels.xml @@ -0,0 +1,5 @@ + + + Tokyo MX1 + Tokyo MX2 + diff --git a/sites/s.mxtv.jp/s.mxtv.jp.config.js b/sites/s.mxtv.jp/s.mxtv.jp.config.js new file mode 100644 index 000000000..1992ec459 --- /dev/null +++ b/sites/s.mxtv.jp/s.mxtv.jp.config.js @@ -0,0 +1,81 @@ +const axios = require('axios') +const dayjs = require('dayjs') +const duration = require("dayjs/plugin/duration") +const utc = require('dayjs/plugin/utc') +const timezone = require('dayjs/plugin/timezone') +const customParseFormat = require('dayjs/plugin/customParseFormat') + +dayjs.extend(utc) +dayjs.extend(timezone) +dayjs.extend(customParseFormat) +dayjs.extend(duration) + +module.exports = { + site: 's.mxtv.jp', + days: 1, + url: function ({ date, channel }) { + const id = `SV${channel.site_id}EPG${date.format('YYYYMMDD')}` + return `https://s.mxtv.jp/bangumi_file/json01/${id}.json` + }, + parser: function ({ content, channel, date }) { + let programs = [] + const items = parseItems(content, channel, date) + items.forEach(item => { + programs.push({ + title: item.Event_name, + description: item.Event_text, + category: parseCategory(item), + image: parseImage(item), + start: parseStart(item), + stop: parseStop(item) + }) + }) + return programs + }, + async channels() { + return [ + { + lang: 'ja', + site_id: '1', + name: 'Tokyo MX1', + xmltv_id: 'TokyoMX1.jp' + }, + { + lang: 'ja', + site_id: '2', + name: 'Tokyo MX2', + xmltv_id: 'TokyoMX2.jp' + } + ] + } +} + +function parseImage(item) { + // Should return a string if we can output an image URL + // Might be done with `https://s.mxtv.jp/bangumi/link/weblinkU.csv?1722421896752` ? + return null +} + +function parseCategory(item) { + // Should return a string if we can determine the category + // Might be done with `https://s.mxtv.jp/index_set/csv/ranking_bangumi_allU.csv` ? + return null +} + +function parseStart(item) { + return dayjs.tz(item.Start_time.toString(), 'YYYY年MM月DD日HH時mm分ss秒', 'Asia/Tokyo') +} + +function parseStop(item) { + // Add the duration to the start time + const durationDate = dayjs(item.Duration, 'HH:mm:ss'); + return parseStart(item).add(dayjs.duration({ + hours: durationDate.hour(), + minutes: durationDate.minute(), + seconds: durationDate.second() + })) +} + +function parseItems(content) { + return JSON.parse(content) || [] +} diff --git a/sites/s.mxtv.jp/s.mxtv.jp.test.js b/sites/s.mxtv.jp/s.mxtv.jp.test.js new file mode 100644 index 000000000..f2219d314 --- /dev/null +++ b/sites/s.mxtv.jp/s.mxtv.jp.test.js @@ -0,0 +1,47 @@ +const { parser, url } = require('./s.mxtv.jp.config.js') +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-08-01', 'YYYY-MM-DD').startOf('d') +const channel = { + site_id: '2', + name: 'Tokyo MX2', + xmltv_id: 'TokyoMX2.jp' +} +const content = `[{ "Event_id": "0x6a57", "Start_time": "2024年07月27日05時00分00秒", "Duration": "01:00:00", "Event_name": "ヒーリングタイム&ヘッドラインニュース", "Event_text": "ねこの足跡", "Component": "480i 16:9 パンベクトルなし", "Sound": "ステレオ", "Event_detail": ""}]` + +it('can generate valid url', () => { + const result = url({ date, channel }) + expect(result).toBe('https://s.mxtv.jp/bangumi_file/json01/SV2EPG20240801.json') +}) + +it('can parse response', () => { + const result = parser({ date, channel, content }).map(p => { + p.start = p.start.toJSON() + p.stop = p.stop.toJSON() + return p + }) + + expect(result).toMatchObject([ + { + start: '2024-07-26T20:00:00.000Z', // UTC time + stop: '2024-07-26T21:00:00.000Z', // UTC + title: 'ヒーリングタイム&ヘッドラインニュース', + description: 'ねこの足跡', + image: null, + category: null + } + ]) +}) + +it('can handle empty guide', () => { + const result = parser({ + date, + channel, + content: '[]' + }) + expect(result).toMatchObject([]) +})