Merge pull request #1203 from iptv-org/create-status-md

Create STATUS.md
This commit is contained in:
Aleksandr Statciuk 2022-10-22 04:48:13 +03:00 committed by GitHub
commit bf94743137
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 259 additions and 81 deletions

View file

@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- run: echo "BRANCH_NAME=$(date +'bot/auto-update-%s')" >> $GITHUB_OUTPUT - run: echo "BRANCH_NAME=$(date +'bot/update-readme-%s')" >> $GITHUB_OUTPUT
id: create-branch-name id: create-branch-name
- run: git config user.name 'iptv-bot[bot]' - run: git config user.name 'iptv-bot[bot]'
- run: git config user.email '84861620+iptv-bot[bot]@users.noreply.github.com' - run: git config user.email '84861620+iptv-bot[bot]@users.noreply.github.com'

53
.github/workflows/_update-status.yml vendored Normal file
View file

@ -0,0 +1,53 @@
name: _update-status
on:
workflow_dispatch:
schedule:
- cron: '0 9 * * *'
jobs:
update:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: echo "BRANCH_NAME=$(date +'bot/update-status-%s')" >> $GITHUB_OUTPUT
id: create-branch-name
- run: git config user.name 'iptv-bot[bot]'
- run: git config user.email '84861620+iptv-bot[bot]@users.noreply.github.com'
- run: git checkout -b ${{ steps.create-branch-name.outputs.BRANCH_NAME }}
- uses: actions/setup-node@v3
if: ${{ !env.ACT }}
with:
node-version: 16
cache: 'npm'
- run: npm install
- run: npm run status:update
- name: Commit Changes
if: ${{ !env.ACT }}
run: |
git add STATUS.md
git commit -m "[Bot] Update STATUS.md"
git status
git push -u origin ${{ steps.create-branch-name.outputs.BRANCH_NAME }}
- uses: tibdex/github-app-token@v1
if: ${{ !env.ACT }}
id: create-app-token
with:
app_id: ${{ secrets.APP_ID }}
private_key: ${{ secrets.APP_PRIVATE_KEY }}
- uses: repo-sync/pull-request@v2
if: ${{ !env.ACT && github.ref == 'refs/heads/master' }}
id: pull-request
with:
github_token: ${{ steps.create-app-token.outputs.token }}
source_branch: ${{ steps.create-branch-name.outputs.BRANCH_NAME }}
destination_branch: 'master'
pr_title: '[Bot] Update STATUS.md'
pr_body: |
This pull request is created via [update-status][1] workflow.
[1]: https://github.com/iptv-org/epg/actions/runs/${{ github.run_id }}
- uses: juliangruber/merge-pull-request-action@v1
if: ${{ !env.ACT && github.ref == 'refs/heads/master' }}
with:
github-token: ${{ secrets.PAT }}
number: ${{ steps.pull-request.outputs.pr_number }}
method: squash

8
.readme/_sites.md Normal file
View file

@ -0,0 +1,8 @@
<table>
<thead>
<tr><th align="left">Site</th><th align="left">Status&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</th></tr>
</thead>
<tbody>
<tr><td valign="top" rowspan="1">example.com</td><td nowrap><a href="https://github.com/iptv-org/epg/actions/workflows/example.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/example.com.yml/badge.svg" alt="example.com" style="max-width: 100%;"></a></td></tr>
</tbody>
</table>

View file

@ -1,4 +0,0 @@
{
"build": "README.md",
"files": ["./.readme/template.md"]
}

4
.readme/readme.json Normal file
View file

@ -0,0 +1,4 @@
{
"build": "README.md",
"files": ["./.readme/readme.md"]
}

View file

@ -1,16 +1,16 @@
# EPG # EPG
EPG (Electronic Program Guide) for thousands of TV channels collected from different sources. EPG (Electronic Program Guide) for thousands of TV channels collected from different sources.
## Usage ## Usage
To load a program guide, all you need to do is copy the link to one or more of the guides from the list below and paste it into your favorite player. To load a program guide, all you need to do is copy the link to one or more of the guides from the list below and paste it into your favorite player.
<!-- prettier-ignore --> <!-- prettier-ignore -->
#include "./.readme/_countries.md" #include "./.readme/_countries.md"
All guides also have a compressed and JSON version. To download them, simply change the extension from `.xml` to `.xml.gz` or `.json` respectively. All guides also have a compressed and JSON version. To download them, simply change the extension from `.xml` to `.xml.gz` or `.json` respectively.
## Contribution ## Contribution
If you find a bug or want to contribute to the code or documentation, you can help by submitting an [issue](https://github.com/iptv-org/epg/issues) or a [pull request](https://github.com/iptv-org/epg/pulls). If you find a bug or want to contribute to the code or documentation, you can help by submitting an [issue](https://github.com/iptv-org/epg/issues) or a [pull request](https://github.com/iptv-org/epg/pulls).

4
.readme/status.json Normal file
View file

@ -0,0 +1,4 @@
{
"build": "STATUS.md",
"files": ["./.readme/status.md"]
}

4
.readme/status.md Normal file
View file

@ -0,0 +1,4 @@
# Status
<!-- prettier-ignore -->
#include "./.readme/_sites.md"

View file

@ -12,6 +12,7 @@
"api:update": "node scripts/commands/api/update.js", "api:update": "node scripts/commands/api/update.js",
"api:load": "mkdir -p scripts/data && curl -L -o scripts/data/channels.json https://iptv-org.github.io/api/channels.json && curl -L -o scripts/data/countries.json https://iptv-org.github.io/api/countries.json", "api:load": "mkdir -p scripts/data && curl -L -o scripts/data/channels.json https://iptv-org.github.io/api/channels.json && curl -L -o scripts/data/countries.json https://iptv-org.github.io/api/countries.json",
"readme:update": "node scripts/commands/readme/update.js", "readme:update": "node scripts/commands/readme/update.js",
"status:update": "node scripts/commands/status/update.js",
"lint": "npx eslint ./scripts/**/*.js", "lint": "npx eslint ./scripts/**/*.js",
"test": "TZ=Pacific/Nauru npx jest --runInBand", "test": "TZ=Pacific/Nauru npx jest --runInBand",
"test:commands": "npx jest --runInBand -- commands", "test:commands": "npx jest --runInBand -- commands",
@ -19,6 +20,7 @@
"act:test": "act workflow_dispatch -W .github/workflows/_test.yml", "act:test": "act workflow_dispatch -W .github/workflows/_test.yml",
"act:check": "act workflow_dispatch -W .github/workflows/_check.yml", "act:check": "act workflow_dispatch -W .github/workflows/_check.yml",
"act:update-readme": "act workflow_dispatch -W .github/workflows/_update-readme.yml", "act:update-readme": "act workflow_dispatch -W .github/workflows/_update-readme.yml",
"act:update-status": "act workflow_dispatch -W .github/workflows/_update-status.yml",
"act:update-api": "act workflow_dispatch -W .github/workflows/_update-api.yml" "act:update-api": "act workflow_dispatch -W .github/workflows/_update-api.yml"
}, },
"private": true, "private": true,

View file

@ -5,7 +5,7 @@ const _ = require('lodash')
const CHANNELS_PATH = process.env.CHANNELS_PATH || 'sites/**/*.channels.xml' const CHANNELS_PATH = process.env.CHANNELS_PATH || 'sites/**/*.channels.xml'
const options = program const options = program
.option('-c, --config <config>', 'Set path to config file', '.readme/config.json') .option('-c, --config <config>', 'Set path to config file', '.readme/readme.json')
.parse(process.argv) .parse(process.argv)
.opts() .opts()
@ -46,28 +46,30 @@ main()
async function generateCountriesTable(items = []) { async function generateCountriesTable(items = []) {
logger.info('generating countries table...') logger.info('generating countries table...')
let rows = [] let data = []
for (const item of items) { for (const item of items) {
const country = api.countries.find({ code: item.code.toUpperCase() }) const country = api.countries.find({ code: item.code.toUpperCase() })
if (!country) continue if (!country) continue
rows.push({ data.push([
flag: country.flag, country.name,
name: country.name, `${country.flag}&nbsp;${country.name}`,
channels: item.count, item.count,
epg: `<code>https://iptv-org.github.io/epg/guides/${item.group}.epg.xml</code>`, `<code>https://iptv-org.github.io/epg/guides/${item.group}.epg.xml</code>`
status: `<a href="https://github.com/iptv-org/epg/actions/workflows/${item.site}.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/${item.site}.yml/badge.svg" alt="${item.site}" style="max-width: 100%;"></a>` ])
})
} }
rows = _.orderBy(rows, ['name', 'channels'], ['asc', 'desc']) data = _.orderBy(data, [item => item[0], item => item[2]], ['asc', 'desc'])
rows = _.groupBy(rows, 'name') data = data.map(i => {
i.shift()
return i
})
data = Object.values(_.groupBy(data, item => item[0]))
const output = table.create(rows, [ const output = table.create(data, [
'Country&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;', 'Country&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;',
'Channels', 'Channels',
'EPG', 'EPG'
'Status&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'
]) ])
await file.create('./.readme/_countries.md', output) await file.create('./.readme/_countries.md', output)

View file

@ -0,0 +1,52 @@
const { file, markdown, logger, table } = require('../../core')
const { program } = require('commander')
const _ = require('lodash')
const CONFIGS_PATH = process.env.CONFIGS_PATH || 'sites/**/*.config.js'
const options = program
.option('-c, --config <config>', 'Set path to config file', '.readme/status.json')
.parse(process.argv)
.opts()
async function main() {
let data = []
const files = await file.list(CONFIGS_PATH).catch(console.error)
for (const filepath of files) {
try {
const { site, ignore } = require(file.resolve(filepath))
if (ignore) continue
data.push([
site,
`<a href="https://github.com/iptv-org/epg/actions/workflows/${site}.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/${site}.yml/badge.svg" alt="${site}" style="max-width: 100%;"></a>`
])
} catch (err) {
console.error(err)
continue
}
}
data = Object.values(_.groupBy(data, item => item[0]))
const output = table.create(data, [
'Site',
'Status&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'
])
await file.create('./.readme/_sites.md', output)
await updateMarkdown()
}
main()
async function updateMarkdown() {
logger.info('updating status.md...')
const config = require(file.resolve(options.config))
await file.createDir(file.dirname(config.build))
await markdown.compile(options.config)
}

View file

@ -1,35 +1,47 @@
const table = {} const table = {}
table.create = function (data, cols) { table.create = function (data, cols) {
let output = '<table>\n' let output = '<table>\r\n'
output += ' <thead>\n <tr>' output += ' <thead>\r\n <tr>'
for (let column of cols) { for (let column of cols) {
output += `<th>${column}</th>` output += `<th align="left">${column}</th>`
} }
output += '</tr>\n </thead>\n' output += '</tr>\r\n </thead>\r\n'
output += ' <tbody>\n' output += ' <tbody>\r\n'
for (let groupId in data) { output += getHTMLRows(data)
const group = data[groupId] output += ' </tbody>\r\n'
for (let [i, item] of group.entries()) {
const rowspan = group.length > 1 ? ` rowspan="${group.length}"` : ''
output += ' <tr>'
if (i === 0) {
const name = item.flag ? `${item.flag}&nbsp;${item.name}` : item.name
output += `<td valign="top"${rowspan}>${name}</td>`
}
output += `<td align="right">${item.channels}</td>`
output += `<td nowrap>${item.epg}</td>`
output += `<td>${item.status}</td>`
output += '</tr>\n'
}
}
output += ' </tbody>\n'
output += '</table>' output += '</table>'
return output return output
} }
function getHTMLRows(data) {
let output = ''
for (let group of data) {
let rowspan = group.length
for (let [j, row] of group.entries()) {
output += ' <tr>'
for (let [i, value] of row.entries()) {
if (i === 0 && j === 0) {
output += `<td valign="top" rowspan="${rowspan}">${value}</td>`
} else if (i > 0) {
if (typeof value === 'number') {
output += `<td align="right" nowrap>${value}</td>`
} else {
output += `<td nowrap>${value}</td>`
}
}
}
output += '</tr>\r\n'
}
}
return output
}
function getSpan() {}
module.exports = table module.exports = table

View file

@ -1,24 +1,24 @@
# EPG # EPG
EPG (Electronic Program Guide) for thousands of TV channels collected from different sources. EPG (Electronic Program Guide) for thousands of TV channels collected from different sources.
## Usage ## Usage
To load a program guide, all you need to do is copy the link to one or more of the guides from the list below and paste it into your favorite player. To load a program guide, all you need to do is copy the link to one or more of the guides from the list below and paste it into your favorite player.
<!-- prettier-ignore --> <!-- prettier-ignore -->
<table> <table>
<thead> <thead>
<tr><th>Country&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</th><th>Channels</th><th>EPG</th><th>Status&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</th></tr> <tr><th align="left">Country&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</th><th align="left">Channels</th><th align="left">EPG</th></tr>
</thead> </thead>
<tbody> <tbody>
<tr><td valign="top" rowspan="2">🇨🇦&nbsp;Canada</td><td align="right">2</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ca-en/example.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/example.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/example.com.yml/badge.svg" alt="example.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top" rowspan="2">🇨🇦&nbsp;Canada</td><td align="right" nowrap>2</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ca-en/example.com.epg.xml</code></td></tr>
<tr><td align="right">1</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ca-ru/example.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/example.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/example.com.yml/badge.svg" alt="example.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right" nowrap>1</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ca-ru/example.com.epg.xml</code></td></tr>
</tbody> </tbody>
</table> </table>
All guides also have a compressed and JSON version. To download them, simply change the extension from `.xml` to `.xml.gz` or `.json` respectively. All guides also have a compressed and JSON version. To download them, simply change the extension from `.xml` to `.xml.gz` or `.json` respectively.
## Contribution ## Contribution
If you find a bug or want to contribute to the code or documentation, you can help by submitting an [issue](https://github.com/iptv-org/epg/issues) or a [pull request](https://github.com/iptv-org/epg/pulls). If you find a bug or want to contribute to the code or documentation, you can help by submitting an [issue](https://github.com/iptv-org/epg/issues) or a [pull request](https://github.com/iptv-org/epg/pulls).

View file

@ -0,0 +1,11 @@
# Status
<!-- prettier-ignore -->
<table>
<thead>
<tr><th align="left">Site</th><th align="left">Status&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</th></tr>
</thead>
<tbody>
<tr><td valign="top" rowspan="1">example.com</td><td nowrap><a href="https://github.com/iptv-org/epg/actions/workflows/example.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/example.com.yml/badge.svg" alt="example.com" style="max-width: 100%;"></a></td></tr>
</tbody>
</table>

View file

@ -1,4 +1,4 @@
{ {
"build" : "tests/__data__/output/readme.md", "build" : "tests/__data__/output/readme.md",
"files" : ["./.readme/template.md"] "files" : ["./.readme/readme.md"]
} }

View file

@ -0,0 +1,4 @@
{
"build" : "tests/__data__/output/status.md",
"files" : ["./.readme/status.md"]
}

View file

@ -0,0 +1,26 @@
const { execSync } = require('child_process')
const fs = require('fs-extra')
const path = require('path')
beforeEach(() => {
fs.emptyDirSync('tests/__data__/output')
const stdout = execSync(
'CONFIGS_PATH=tests/__data__/input/sites/example.com.config.js DATA_DIR=tests/__data__/input/data npm run status:update -- --config=tests/__data__/input/status.json',
{ encoding: 'utf8' }
)
})
it('can update status.md', () => {
expect(content('tests/__data__/output/status.md')).toBe(
content('tests/__data__/expected/_status.md')
)
})
function content(filepath) {
const data = fs.readFileSync(path.resolve(filepath), {
encoding: 'utf8'
})
return JSON.stringify(data)
}