From e22594791889b7490a62c797949c97a3e93f2230 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Sat, 31 Jul 2021 21:55:17 +0300
Subject: [PATCH 01/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index 4ed5160ca..ce91e8e6e 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -46,7 +46,7 @@ jobs:
       - name: Install Dependencies
         run: npm install
       - name: Format Playlists
-        run: node scripts/format.js
+        run: node scripts/format.js --resolution
       - name: Upload Artifact
         uses: actions/upload-artifact@v2
         with:

From 101072c76c8982b2dfc1f96db69ad3eafac9ef62 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Sun, 1 Aug 2021 03:58:47 +0300
Subject: [PATCH 02/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 172 +++++++++++++++++++++++++++++-
 1 file changed, 170 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index ce91e8e6e..fe9476f4f 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -38,20 +38,188 @@ jobs:
   format:
     runs-on: ubuntu-latest
     needs: filter
+    continue-on-error: true
+    strategy:
+      fail-fast: false
+      matrix:
+        country:
+          [
+            ad,
+            ae,
+            af,
+            al,
+            am,
+            ao,
+            ar,
+            at,
+            au,
+            aw,
+            az,
+            ba,
+            bb,
+            bd,
+            be,
+            bf,
+            bg,
+            bh,
+            bn,
+            bo,
+            br,
+            bs,
+            by,
+            ca,
+            cd,
+            cg,
+            ch,
+            ci,
+            cl,
+            cm,
+            cn,
+            co,
+            cr,
+            cu,
+            cw,
+            cy,
+            cz,
+            de,
+            dk,
+            do,
+            dz,
+            ec,
+            ee,
+            eg,
+            es,
+            et,
+            fi,
+            fj,
+            fo,
+            fr,
+            ge,
+            gh,
+            gm,
+            gn,
+            gp,
+            gq,
+            gr,
+            gt,
+            hk,
+            hn,
+            hr,
+            ht,
+            hu,
+            id,
+            ie,
+            il,
+            in,
+            iq,
+            ir,
+            is,
+            it,
+            jm,
+            jo,
+            jp,
+            ke,
+            kg,
+            kh,
+            kp,
+            kr,
+            kw,
+            kz,
+            la,
+            lb,
+            li,
+            lk,
+            lt,
+            lu,
+            lv,
+            ly,
+            ma,
+            mc,
+            md,
+            me,
+            mk,
+            ml,
+            mm,
+            mn,
+            mo,
+            mt,
+            mx,
+            my,
+            mz,
+            ne,
+            ng,
+            nl,
+            no,
+            np,
+            nz,
+            om,
+            pa,
+            pe,
+            ph,
+            pk,
+            pl,
+            pr,
+            ps,
+            pt,
+            py,
+            qa,
+            ro,
+            rs,
+            ru,
+            rw,
+            sa,
+            sd,
+            se,
+            sg,
+            si,
+            sk,
+            sl,
+            sm,
+            sn,
+            so,
+            sv,
+            sy,
+            th,
+            tj,
+            tm,
+            tn,
+            tr,
+            tt,
+            tw,
+            tz,
+            ua,
+            ug,
+            uk,
+            us,
+            uy,
+            uz,
+            va,
+            ve,
+            vi,
+            vn,
+            xk,
+            ye,
+            zm
+          ]
     steps:
       - name: Checkout
         uses: actions/checkout@v2
       - name: Download Artifacts
         uses: actions/download-artifact@v2
+      - name: Enable Cache
+        uses: actions/setup-node@v2
+        with:
+          node-version: '14'
+          cache: 'npm'
       - name: Install Dependencies
         run: npm install
       - name: Format Playlists
-        run: node scripts/format.js --resolution
+        run: node scripts/format.js --country=${{ matrix.country }}
       - name: Upload Artifact
         uses: actions/upload-artifact@v2
         with:
           name: channels
-          path: channels/
+          path: channels/${{ matrix.country }}.m3u
   generate:
     runs-on: ubuntu-latest
     needs: format

From e96f13414b3e886f6dc886e3984dd2db73347b36 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Sun, 1 Aug 2021 04:10:01 +0300
Subject: [PATCH 03/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index fe9476f4f..505c3dcf8 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -206,11 +206,6 @@ jobs:
         uses: actions/checkout@v2
       - name: Download Artifacts
         uses: actions/download-artifact@v2
-      - name: Enable Cache
-        uses: actions/setup-node@v2
-        with:
-          node-version: '14'
-          cache: 'npm'
       - name: Install Dependencies
         run: npm install
       - name: Format Playlists

From 641286090e2770a9cb72354a595c64fb052f439c Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Sun, 1 Aug 2021 04:14:52 +0300
Subject: [PATCH 04/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index 505c3dcf8..04e0fc9a8 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -47,8 +47,10 @@ jobs:
             ad,
             ae,
             af,
+            ag,
             al,
             am,
+            an,
             ao,
             ar,
             at,
@@ -143,11 +145,13 @@ jobs:
             mn,
             mo,
             mt,
+            mv,
             mx,
             my,
             mz,
             ne,
             ng,
+            ni,
             nl,
             no,
             np,
@@ -209,7 +213,7 @@ jobs:
       - name: Install Dependencies
         run: npm install
       - name: Format Playlists
-        run: node scripts/format.js --country=${{ matrix.country }}
+        run: node scripts/format.js --country=${{ matrix.country }} --resolution
       - name: Upload Artifact
         uses: actions/upload-artifact@v2
         with:

From 70e214f9298eefad76be37bec6294828b6388b1b Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Sun, 1 Aug 2021 06:24:51 +0300
Subject: [PATCH 05/95] Update utils.js

---
 scripts/utils.js | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/scripts/utils.js b/scripts/utils.js
index 0c290ba5f..085848b55 100644
--- a/scripts/utils.js
+++ b/scripts/utils.js
@@ -185,4 +185,8 @@ utils.removeProtocol = function (string) {
   return string.replace(/(^\w+:|^)\/\//, '')
 }
 
+utils.log = function (string) {
+  process.stdout.write(string)
+}
+
 module.exports = utils

From c94589664f3a101d482acdd19fa8d7d2786435b5 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Sun, 1 Aug 2021 06:25:04 +0300
Subject: [PATCH 06/95] Update format.js

---
 scripts/format.js | 100 ++++------------------------------------------
 1 file changed, 7 insertions(+), 93 deletions(-)

diff --git a/scripts/format.js b/scripts/format.js
index 38673dd6b..45a8303a1 100644
--- a/scripts/format.js
+++ b/scripts/format.js
@@ -1,152 +1,66 @@
 const { program } = require('commander')
 const parser = require('./parser')
 const utils = require('./utils')
-const axios = require('axios')
-const ProgressBar = require('progress')
-const https = require('https')
 
 program
   .usage('[OPTIONS]...')
   .option('-d, --debug', 'Debug mode')
-  .option('-r --resolution', 'Parse stream resolution')
   .option('-c, --country <country>', 'Comma-separated list of country codes', '')
   .option('-e, --exclude <exclude>', 'Comma-separated list of country codes to be excluded', '')
-  .option('--delay <delay>', 'Delay between parser requests', 1000)
-  .option('--timeout <timeout>', 'Set timeout for each request', 5000)
   .parse(process.argv)
 
 const config = program.opts()
 
-const instance = axios.create({
-  timeout: config.timeout,
-  maxContentLength: 200000,
-  httpsAgent: new https.Agent({
-    rejectUnauthorized: false
-  })
-})
-
 async function main() {
-  console.info('Starting...')
-  console.time('Done in')
+  utils.log('Starting...\n')
+  console.time('\nDone in')
 
   const playlists = parseIndex()
 
   for (const playlist of playlists) {
-    await loadPlaylist(playlist.url)
-      .then(sortChannels)
-      .then(detectResolution)
-      .then(savePlaylist)
-      .then(done)
+    await loadPlaylist(playlist.url).then(sortChannels).then(savePlaylist)
   }
 
   finish()
 }
 
 function parseIndex() {
-  console.info(`\nParsing 'index.m3u'...`)
+  utils.log(`Parsing 'index.m3u'...`)
   let playlists = parser.parseIndex()
   playlists = utils
     .filterPlaylists(playlists, config.country, config.exclude)
     .filter(i => i.url !== 'channels/unsorted.m3u')
-  console.info(`Found ${playlists.length} playlist(s)\n`)
 
   return playlists
 }
 
 async function loadPlaylist(url) {
-  console.info(`Processing '${url}'...`)
+  utils.log(`\nProcessing '${url}'...`)
   return parser.parsePlaylist(url)
 }
 
 async function sortChannels(playlist) {
-  console.info(`  Sorting channels...`)
   playlist.channels = utils.sortBy(playlist.channels, ['name', 'url'])
 
   return playlist
 }
 
-async function detectResolution(playlist) {
-  if (!config.resolution) return playlist
-  console.log('  Detecting resolution...')
-  const bar = new ProgressBar('    Progress: [:bar] :current/:total (:percent) ', {
-    total: playlist.channels.length
-  })
-  const results = []
-  for (const channel of playlist.channels) {
-    bar.tick()
-    if (!channel.resolution.height) {
-      const CancelToken = axios.CancelToken
-      const source = CancelToken.source()
-      const timeout = setTimeout(() => {
-        source.cancel()
-      }, config.timeout)
-
-      const response = await instance
-        .get(channel.url, { cancelToken: source.token })
-        .then(res => {
-          clearTimeout(timeout)
-
-          return res
-        })
-        .then(utils.sleep(config.delay))
-        .catch(err => {
-          clearTimeout(timeout)
-        })
-
-      if (response && response.status === 200) {
-        if (/^#EXTM3U/.test(response.data)) {
-          const resolution = parseResolution(response.data)
-          if (resolution) {
-            channel.resolution = resolution
-          }
-        }
-      }
-    }
-
-    results.push(channel)
-  }
-
-  playlist.channels = results
-
-  return playlist
-}
-
-function parseResolution(string) {
-  const regex = /RESOLUTION=(\d+)x(\d+)/gm
-  const match = string.matchAll(regex)
-  const arr = Array.from(match).map(m => ({
-    width: parseInt(m[1]),
-    height: parseInt(m[2])
-  }))
-
-  return arr.length
-    ? arr.reduce(function (prev, current) {
-        return prev.height > current.height ? prev : current
-      })
-    : undefined
-}
-
 async function savePlaylist(playlist) {
   const original = utils.readFile(playlist.url)
   const output = playlist.toString()
 
   if (original === output) {
-    console.info(`No changes have been made.`)
     return false
   } else {
     utils.createFile(playlist.url, output)
-    console.info(`Playlist has been updated.`)
+    utils.log(`updated`)
   }
 
   return true
 }
 
-async function done() {
-  console.info(` `)
-}
-
 function finish() {
-  console.timeEnd('Done in')
+  console.timeEnd('\nDone in')
 }
 
 main()

From 33f00154cd3c7686f9a730728db0eb5eaa4e7c90 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Sun, 1 Aug 2021 06:51:16 +0300
Subject: [PATCH 07/95] Update format.js

---
 scripts/format.js | 1 -
 1 file changed, 1 deletion(-)

diff --git a/scripts/format.js b/scripts/format.js
index 45a8303a1..d683cfb08 100644
--- a/scripts/format.js
+++ b/scripts/format.js
@@ -16,7 +16,6 @@ async function main() {
   console.time('\nDone in')
 
   const playlists = parseIndex()
-
   for (const playlist of playlists) {
     await loadPlaylist(playlist.url).then(sortChannels).then(savePlaylist)
   }

From e96f1406edff76d71da759f619185303a424fcb9 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Sun, 1 Aug 2021 06:52:03 +0300
Subject: [PATCH 08/95] Create detect-resolution.js

---
 scripts/detect-resolution.js | 130 +++++++++++++++++++++++++++++++++++
 1 file changed, 130 insertions(+)
 create mode 100644 scripts/detect-resolution.js

diff --git a/scripts/detect-resolution.js b/scripts/detect-resolution.js
new file mode 100644
index 000000000..19c2a7d90
--- /dev/null
+++ b/scripts/detect-resolution.js
@@ -0,0 +1,130 @@
+const { program } = require('commander')
+const parser = require('./parser')
+const utils = require('./utils')
+const axios = require('axios')
+const ProgressBar = require('progress')
+const https = require('https')
+
+program
+  .usage('[OPTIONS]...')
+  .option('-d, --debug', 'Debug mode')
+  .option('-c, --country <country>', 'Comma-separated list of country codes', '')
+  .option('-e, --exclude <exclude>', 'Comma-separated list of country codes to be excluded', '')
+  .option('--delay <delay>', 'Delay between parser requests', 1000)
+  .option('--timeout <timeout>', 'Set timeout for each request', 5000)
+  .parse(process.argv)
+
+const config = program.opts()
+
+const instance = axios.create({
+  timeout: config.timeout,
+  maxContentLength: 200000,
+  httpsAgent: new https.Agent({
+    rejectUnauthorized: false
+  })
+})
+
+async function main() {
+  utils.log('Starting...\n')
+  console.time('Done in')
+
+  const playlists = parseIndex()
+  for (const playlist of playlists) {
+    await loadPlaylist(playlist.url).then(detectResolution).then(savePlaylist)
+  }
+
+  finish()
+}
+
+function parseIndex() {
+  utils.log(`Parsing 'index.m3u'...\n`)
+  let playlists = parser.parseIndex()
+  playlists = utils
+    .filterPlaylists(playlists, config.country, config.exclude)
+    .filter(i => i.url !== 'channels/unsorted.m3u')
+
+  return playlists
+}
+
+async function loadPlaylist(url) {
+  return parser.parsePlaylist(url)
+}
+
+async function detectResolution(playlist) {
+  const bar = new ProgressBar(`Processing '${playlist.url}': [:bar] :current/:total (:percent) `, {
+    total: playlist.channels.length
+  })
+  const results = []
+  for (const channel of playlist.channels) {
+    bar.tick()
+    if (!channel.resolution.height) {
+      const CancelToken = axios.CancelToken
+      const source = CancelToken.source()
+      const timeout = setTimeout(() => {
+        source.cancel()
+      }, config.timeout)
+
+      const response = await instance
+        .get(channel.url, { cancelToken: source.token })
+        .then(res => {
+          clearTimeout(timeout)
+
+          return res
+        })
+        .then(utils.sleep(config.delay))
+        .catch(err => {
+          clearTimeout(timeout)
+        })
+
+      if (response && response.status === 200) {
+        if (/^#EXTM3U/.test(response.data)) {
+          const resolution = parseResolution(response.data)
+          if (resolution) {
+            channel.resolution = resolution
+          }
+        }
+      }
+    }
+
+    results.push(channel)
+  }
+
+  playlist.channels = results
+
+  return playlist
+}
+
+function parseResolution(string) {
+  const regex = /RESOLUTION=(\d+)x(\d+)/gm
+  const match = string.matchAll(regex)
+  const arr = Array.from(match).map(m => ({
+    width: parseInt(m[1]),
+    height: parseInt(m[2])
+  }))
+
+  return arr.length
+    ? arr.reduce(function (prev, current) {
+        return prev.height > current.height ? prev : current
+      })
+    : undefined
+}
+
+async function savePlaylist(playlist) {
+  const original = utils.readFile(playlist.url)
+  const output = playlist.toString()
+
+  if (original === output) {
+    return false
+  } else {
+    utils.createFile(playlist.url, output)
+    utils.log(`Playlist '${playlist.url}' has been updated\n`)
+  }
+
+  return true
+}
+
+function finish() {
+  console.timeEnd('Done in')
+}
+
+main()

From 151396503b7d50df5d16b2752dea92f001c53a20 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Sun, 1 Aug 2021 07:13:18 +0300
Subject: [PATCH 09/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 23 ++++++++++++++++++++---
 1 file changed, 20 insertions(+), 3 deletions(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index 04e0fc9a8..4e70afcda 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -38,6 +38,23 @@ jobs:
   format:
     runs-on: ubuntu-latest
     needs: filter
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+      - name: Download Artifacts
+        uses: actions/download-artifact@v2
+      - name: Install Dependencies
+        run: npm install
+      - name: Format Playlists
+        run: node scripts/format.js
+      - name: Upload Artifact
+        uses: actions/upload-artifact@v2
+        with:
+          name: channels
+          path: channels/
+  detect-resolution:
+    runs-on: ubuntu-latest
+    needs: format
     continue-on-error: true
     strategy:
       fail-fast: false
@@ -212,8 +229,8 @@ jobs:
         uses: actions/download-artifact@v2
       - name: Install Dependencies
         run: npm install
-      - name: Format Playlists
-        run: node scripts/format.js --country=${{ matrix.country }} --resolution
+      - name: Detect Resolution
+        run: node scripts/detect-resolution.js --country=${{ matrix.country }}
       - name: Upload Artifact
         uses: actions/upload-artifact@v2
         with:
@@ -221,7 +238,7 @@ jobs:
           path: channels/${{ matrix.country }}.m3u
   generate:
     runs-on: ubuntu-latest
-    needs: format
+    needs: detect-resolution
     steps:
       - name: Checkout
         uses: actions/checkout@v2

From f5bb4299a99b6635b718d8de288a80187ad5b5f5 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Sun, 1 Aug 2021 07:21:31 +0300
Subject: [PATCH 10/95] Update remove-duplicates.js

---
 scripts/remove-duplicates.js | 35 ++++++++++-------------------------
 1 file changed, 10 insertions(+), 25 deletions(-)

diff --git a/scripts/remove-duplicates.js b/scripts/remove-duplicates.js
index f7fbd9432..7df01778b 100644
--- a/scripts/remove-duplicates.js
+++ b/scripts/remove-duplicates.js
@@ -4,37 +4,31 @@ const utils = require('./utils')
 let globalBuffer = []
 
 async function main() {
-  const playlists = parseIndex()
+  utils.log('Starting...\n')
+  console.time('\nDone in')
 
+  const playlists = parseIndex()
   for (const playlist of playlists) {
-    await loadPlaylist(playlist.url)
-      .then(addToBuffer)
-      .then(removeDuplicates)
-      .then(savePlaylist)
-      .then(done)
+    await loadPlaylist(playlist.url).then(addToBuffer).then(removeDuplicates).then(savePlaylist)
   }
 
   if (playlists.length) {
-    await loadPlaylist('channels/unsorted.m3u')
-      .then(removeUnsortedDuplicates)
-      .then(savePlaylist)
-      .then(done)
+    await loadPlaylist('channels/unsorted.m3u').then(removeUnsortedDuplicates).then(savePlaylist)
   }
 
   finish()
 }
 
 function parseIndex() {
-  console.info(`Parsing 'index.m3u'...`)
+  utils.log(`Parsing 'index.m3u'...`)
   let playlists = parser.parseIndex()
   playlists = playlists.filter(i => i.url !== 'channels/unsorted.m3u')
-  console.info(`Found ${playlists.length} playlist(s)\n`)
 
   return playlists
 }
 
 async function loadPlaylist(url) {
-  console.info(`Processing '${url}'...`)
+  utils.log(`\nProcessing '${url}'...`)
   return parser.parsePlaylist(url)
 }
 
@@ -46,9 +40,8 @@ async function addToBuffer(playlist) {
 }
 
 async function removeDuplicates(playlist) {
-  console.info(`  Looking for duplicates...`)
   let buffer = {}
-  const channels = playlist.channels.filter(i => {
+  playlist.channels = playlist.channels.filter(i => {
     const url = utils.removeProtocol(i.url)
     const result = typeof buffer[url] === 'undefined'
     if (result) {
@@ -58,13 +51,10 @@ async function removeDuplicates(playlist) {
     return result
   })
 
-  playlist.channels = channels
-
   return playlist
 }
 
 async function removeUnsortedDuplicates(playlist) {
-  console.info(`  Looking for duplicates...`)
   // locally
   let buffer = {}
   let channels = playlist.channels.filter(i => {
@@ -89,22 +79,17 @@ async function savePlaylist(playlist) {
   const output = playlist.toString({ raw: true })
 
   if (original === output) {
-    console.info(`No changes have been made.`)
     return false
   } else {
     utils.createFile(playlist.url, output)
-    console.info(`Playlist has been updated.`)
+    utils.log(`updated`)
   }
 
   return true
 }
 
-async function done() {
-  console.info(` `)
-}
-
 function finish() {
-  console.info('Done.')
+  console.timeEnd('\nDone in')
 }
 
 main()

From 3e2bd0c36bce5b2389ec4d75d1b5c1bbafd1c7a4 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Sun, 1 Aug 2021 07:55:18 +0300
Subject: [PATCH 11/95] Update filter.js

---
 scripts/filter.js | 55 ++++++++++++-----------------------------------
 1 file changed, 14 insertions(+), 41 deletions(-)

diff --git a/scripts/filter.js b/scripts/filter.js
index ec90cab56..35017c4f7 100644
--- a/scripts/filter.js
+++ b/scripts/filter.js
@@ -1,32 +1,23 @@
+const blacklist = require('./blacklist.json')
 const parser = require('./parser')
 const utils = require('./utils')
-const blacklist = require('./blacklist.json')
+const log = require('./log')
 
 async function main() {
-  const playlists = parseIndex()
+  log.start()
+
+  log.print(`Parsing 'index.m3u'...`)
+  const playlists = parser.parseIndex()
   for (const playlist of playlists) {
-    await loadPlaylist(playlist.url).then(removeBlacklisted).then(savePlaylist).then(done)
+    log.print(`\nProcessing '${playlist.url}'...`)
+    await parser.parsePlaylist(playlist.url).then(removeBlacklisted).then(utils.savePlaylist)
   }
 
-  finish()
-}
-
-function parseIndex() {
-  console.info(`Parsing 'index.m3u'...`)
-  let playlists = parser.parseIndex()
-  console.info(`Found ${playlists.length} playlist(s)\n`)
-
-  return playlists
-}
-
-async function loadPlaylist(url) {
-  console.info(`Processing '${url}'...`)
-  return parser.parsePlaylist(url)
+  log.finish()
 }
 
 async function removeBlacklisted(playlist) {
-  console.info(`  Looking for blacklisted channels...`)
-  playlist.channels = playlist.channels.filter(channel => {
+  const channels = playlist.channels.filter(channel => {
     return !blacklist.find(i => {
       const channelName = channel.name.toLowerCase()
       return (
@@ -37,31 +28,13 @@ async function removeBlacklisted(playlist) {
     })
   })
 
-  return playlist
-}
-
-async function savePlaylist(playlist) {
-  console.info(`  Saving playlist...`)
-  const original = utils.readFile(playlist.url)
-  const output = playlist.toString({ raw: true })
-
-  if (original === output) {
-    console.info(`No changes have been made.`)
-    return false
-  } else {
-    utils.createFile(playlist.url, output)
-    console.info(`Playlist has been updated.`)
+  if (playlist.channels.length !== channels.length) {
+    log.print(`updated`)
   }
 
-  return true
-}
-
-async function done() {
-  console.info(` `)
-}
+  playlist.channels = channels
 
-function finish() {
-  console.info('Done.')
+  return playlist
 }
 
 main()

From 4380a4fac4bc16a9549d9087ba8cbb7d4e012ba0 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Sun, 1 Aug 2021 08:04:03 +0300
Subject: [PATCH 12/95] Update remove-duplicates.js

---
 scripts/remove-duplicates.js | 67 +++++++++++++++---------------------
 1 file changed, 27 insertions(+), 40 deletions(-)

diff --git a/scripts/remove-duplicates.js b/scripts/remove-duplicates.js
index 7df01778b..8b21c748d 100644
--- a/scripts/remove-duplicates.js
+++ b/scripts/remove-duplicates.js
@@ -1,35 +1,32 @@
 const parser = require('./parser')
 const utils = require('./utils')
+const log = require('./log')
 
 let globalBuffer = []
 
 async function main() {
-  utils.log('Starting...\n')
-  console.time('\nDone in')
+  log.start()
 
-  const playlists = parseIndex()
+  log.print(`Parsing 'index.m3u'...`)
+  const playlists = parser.parseIndex().filter(i => i.url !== 'channels/unsorted.m3u')
   for (const playlist of playlists) {
-    await loadPlaylist(playlist.url).then(addToBuffer).then(removeDuplicates).then(savePlaylist)
+    log.print(`\nProcessing '${playlist.url}'...`)
+    await parser
+      .parsePlaylist(playlist.url)
+      .then(addToBuffer)
+      .then(removeDuplicates)
+      .then(utils.savePlaylist)
   }
 
   if (playlists.length) {
-    await loadPlaylist('channels/unsorted.m3u').then(removeUnsortedDuplicates).then(savePlaylist)
+    log.print(`\nProcessing 'channels/unsorted.m3u'...`)
+    await parser
+      .parsePlaylist('channels/unsorted.m3u')
+      .then(removeUnsortedDuplicates)
+      .then(utils.savePlaylist)
   }
 
-  finish()
-}
-
-function parseIndex() {
-  utils.log(`Parsing 'index.m3u'...`)
-  let playlists = parser.parseIndex()
-  playlists = playlists.filter(i => i.url !== 'channels/unsorted.m3u')
-
-  return playlists
-}
-
-async function loadPlaylist(url) {
-  utils.log(`\nProcessing '${url}'...`)
-  return parser.parsePlaylist(url)
+  log.finish()
 }
 
 async function addToBuffer(playlist) {
@@ -41,7 +38,7 @@ async function addToBuffer(playlist) {
 
 async function removeDuplicates(playlist) {
   let buffer = {}
-  playlist.channels = playlist.channels.filter(i => {
+  const channels = playlist.channels.filter(i => {
     const url = utils.removeProtocol(i.url)
     const result = typeof buffer[url] === 'undefined'
     if (result) {
@@ -51,6 +48,11 @@ async function removeDuplicates(playlist) {
     return result
   })
 
+  if (playlist.channels.length !== channels.length) {
+    log.print('updated')
+    playlist.channels = channels
+  }
+
   return playlist
 }
 
@@ -64,32 +66,17 @@ async function removeUnsortedDuplicates(playlist) {
 
     return result
   })
+
   // globally
   const urls = globalBuffer.map(i => utils.removeProtocol(i.url))
   channels = channels.filter(i => !urls.includes(utils.removeProtocol(i.url)))
-  if (channels.length === playlist.channels.length) return playlist
 
-  playlist.channels = channels
-
-  return playlist
-}
-
-async function savePlaylist(playlist) {
-  const original = utils.readFile(playlist.url)
-  const output = playlist.toString({ raw: true })
-
-  if (original === output) {
-    return false
-  } else {
-    utils.createFile(playlist.url, output)
-    utils.log(`updated`)
+  if (channels.length !== playlist.channels.length) {
+    log.print('updated')
+    playlist.channels = channels
   }
 
-  return true
-}
-
-function finish() {
-  console.timeEnd('\nDone in')
+  return playlist
 }
 
 main()

From a79e3567cf17b07abd0d3b203b70286c5b476b24 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Sun, 1 Aug 2021 08:25:39 +0300
Subject: [PATCH 13/95] Update format.js

---
 scripts/format.js | 60 ++++++++++-------------------------------------
 1 file changed, 12 insertions(+), 48 deletions(-)

diff --git a/scripts/format.js b/scripts/format.js
index d683cfb08..70c3decab 100644
--- a/scripts/format.js
+++ b/scripts/format.js
@@ -1,65 +1,29 @@
-const { program } = require('commander')
 const parser = require('./parser')
 const utils = require('./utils')
-
-program
-  .usage('[OPTIONS]...')
-  .option('-d, --debug', 'Debug mode')
-  .option('-c, --country <country>', 'Comma-separated list of country codes', '')
-  .option('-e, --exclude <exclude>', 'Comma-separated list of country codes to be excluded', '')
-  .parse(process.argv)
-
-const config = program.opts()
+const log = require('./log')
 
 async function main() {
-  utils.log('Starting...\n')
-  console.time('\nDone in')
+  log.start()
 
-  const playlists = parseIndex()
+  log.print(`Parsing 'index.m3u'...`)
+  let playlists = parser.parseIndex().filter(i => i.url !== 'channels/unsorted.m3u')
   for (const playlist of playlists) {
-    await loadPlaylist(playlist.url).then(sortChannels).then(savePlaylist)
+    log.print(`\nProcessing '${playlist.url}'...`)
+    await parser.parsePlaylist(playlist.url).then(sortChannels).then(utils.savePlaylist)
   }
 
-  finish()
-}
-
-function parseIndex() {
-  utils.log(`Parsing 'index.m3u'...`)
-  let playlists = parser.parseIndex()
-  playlists = utils
-    .filterPlaylists(playlists, config.country, config.exclude)
-    .filter(i => i.url !== 'channels/unsorted.m3u')
-
-  return playlists
-}
-
-async function loadPlaylist(url) {
-  utils.log(`\nProcessing '${url}'...`)
-  return parser.parsePlaylist(url)
+  log.finish()
 }
 
 async function sortChannels(playlist) {
-  playlist.channels = utils.sortBy(playlist.channels, ['name', 'url'])
+  const channels = [...playlist.channels]
+  utils.sortBy(channels, ['name', 'url'])
 
-  return playlist
-}
+  if (JSON.stringify(playlist.channels) !== JSON.stringify(channels)) log.print('updated')
 
-async function savePlaylist(playlist) {
-  const original = utils.readFile(playlist.url)
-  const output = playlist.toString()
-
-  if (original === output) {
-    return false
-  } else {
-    utils.createFile(playlist.url, output)
-    utils.log(`updated`)
-  }
+  playlist.channels = channels
 
-  return true
-}
-
-function finish() {
-  console.timeEnd('\nDone in')
+  return playlist
 }
 
 main()

From 417e9cb9971b440cb0e2bec8aa9072d0104104ab Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Sun, 1 Aug 2021 08:25:44 +0300
Subject: [PATCH 14/95] Create log.js

---
 scripts/log.js | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)
 create mode 100644 scripts/log.js

diff --git a/scripts/log.js b/scripts/log.js
new file mode 100644
index 000000000..a4b580cc8
--- /dev/null
+++ b/scripts/log.js
@@ -0,0 +1,16 @@
+const log = {}
+
+log.print = function (string) {
+  process.stdout.write(string)
+}
+
+log.start = function () {
+  this.print('Starting...\n')
+  console.time('\nDone in')
+}
+
+log.finish = function () {
+  console.timeEnd('\nDone in')
+}
+
+module.exports = log

From 3d1a5ed5c5acf46279ff0705854e4f15e37f8611 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Sun, 1 Aug 2021 08:25:48 +0300
Subject: [PATCH 15/95] Update parser.js

---
 scripts/parser.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/scripts/parser.js b/scripts/parser.js
index 2348c9145..0c86a0206 100644
--- a/scripts/parser.js
+++ b/scripts/parser.js
@@ -15,7 +15,7 @@ parser.parseIndex = function () {
   return result.items
 }
 
-parser.parsePlaylist = function (filename) {
+parser.parsePlaylist = async function (filename) {
   const content = utils.readFile(filename)
   const result = playlistParser.parse(content)
   const name = path.parse(filename).name

From f164c948edee225dbf6c1d2781153e0d8af99dcc Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Sun, 1 Aug 2021 08:26:01 +0300
Subject: [PATCH 16/95] Update utils.js

---
 scripts/utils.js | 48 +++++++++++++++++-------------------------------
 1 file changed, 17 insertions(+), 31 deletions(-)

diff --git a/scripts/utils.js b/scripts/utils.js
index 085848b55..08465b4bd 100644
--- a/scripts/utils.js
+++ b/scripts/utils.js
@@ -66,29 +66,12 @@ utils.sortBy = function (arr, fields) {
     for (let field of fields) {
       let propA = a[field] ? a[field].toLowerCase() : ''
       let propB = b[field] ? b[field].toLowerCase() : ''
-
-      if (propA === 'undefined') {
-        return 1
-      }
-
-      if (propB === 'undefined') {
-        return -1
-      }
-
-      if (propA === 'other') {
-        return 1
-      }
-
-      if (propB === 'other') {
-        return -1
-      }
-
-      if (propA < propB) {
-        return -1
-      }
-      if (propA > propB) {
-        return 1
-      }
+      if (propA === 'undefined') return 1
+      if (propB === 'undefined') return -1
+      if (propA === 'other') return 1
+      if (propB === 'other') return -1
+      if (propA < propB) return -1
+      if (propA > propB) return 1
     }
     return 0
   })
@@ -169,12 +152,6 @@ utils.createFile = function (filename, data = '') {
   fs.writeFileSync(path.resolve(__dirname) + '/../' + filename, data)
 }
 
-utils.writeToLog = function (country, msg, url) {
-  var now = new Date()
-  var line = `${country}: ${msg} '${url}'`
-  this.appendToFile('error.log', now.toISOString() + ' ' + line + '\n')
-}
-
 utils.sleep = function (ms) {
   return function (x) {
     return new Promise(resolve => setTimeout(() => resolve(x), ms))
@@ -185,8 +162,17 @@ utils.removeProtocol = function (string) {
   return string.replace(/(^\w+:|^)\/\//, '')
 }
 
-utils.log = function (string) {
-  process.stdout.write(string)
+utils.savePlaylist = async function (playlist) {
+  const original = utils.readFile(playlist.url)
+  const output = playlist.toString({ raw: true })
+
+  if (original === output) {
+    return false
+  } else {
+    utils.createFile(playlist.url, output)
+  }
+
+  return true
 }
 
 module.exports = utils

From 16f409856ef6f95791fe2f06f5d381d264ec4b13 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Sun, 1 Aug 2021 19:45:15 +0300
Subject: [PATCH 17/95] Create /helpers directory

---
 scripts/{ => helpers}/blacklist.json  |   0
 scripts/{ => helpers}/categories.json |   0
 scripts/{ => helpers}/db.js           |   0
 scripts/{ => helpers}/log.js          |   4 +-
 scripts/helpers/parser.js             |  24 +++
 scripts/{ => helpers}/regions.json    |   0
 scripts/{ => helpers}/utils.js        | 104 ++---------
 scripts/parser.js                     | 244 --------------------------
 8 files changed, 41 insertions(+), 335 deletions(-)
 rename scripts/{ => helpers}/blacklist.json (100%)
 rename scripts/{ => helpers}/categories.json (100%)
 rename scripts/{ => helpers}/db.js (100%)
 rename scripts/{ => helpers}/log.js (76%)
 create mode 100644 scripts/helpers/parser.js
 rename scripts/{ => helpers}/regions.json (100%)
 rename scripts/{ => helpers}/utils.js (58%)
 delete mode 100644 scripts/parser.js

diff --git a/scripts/blacklist.json b/scripts/helpers/blacklist.json
similarity index 100%
rename from scripts/blacklist.json
rename to scripts/helpers/blacklist.json
diff --git a/scripts/categories.json b/scripts/helpers/categories.json
similarity index 100%
rename from scripts/categories.json
rename to scripts/helpers/categories.json
diff --git a/scripts/db.js b/scripts/helpers/db.js
similarity index 100%
rename from scripts/db.js
rename to scripts/helpers/db.js
diff --git a/scripts/log.js b/scripts/helpers/log.js
similarity index 76%
rename from scripts/log.js
rename to scripts/helpers/log.js
index a4b580cc8..a2e8fe0a2 100644
--- a/scripts/log.js
+++ b/scripts/helpers/log.js
@@ -6,11 +6,11 @@ log.print = function (string) {
 
 log.start = function () {
   this.print('Starting...\n')
-  console.time('\nDone in')
+  console.time('Done in')
 }
 
 log.finish = function () {
-  console.timeEnd('\nDone in')
+  console.timeEnd('Done in')
 }
 
 module.exports = log
diff --git a/scripts/helpers/parser.js b/scripts/helpers/parser.js
new file mode 100644
index 000000000..815c819ec
--- /dev/null
+++ b/scripts/helpers/parser.js
@@ -0,0 +1,24 @@
+const playlistParser = require('iptv-playlist-parser')
+const Playlist = require('./Playlist')
+const utils = require('./utils')
+const file = require('./file')
+
+const parser = {}
+
+parser.parseIndex = function () {
+  const content = file.read('index.m3u')
+  const result = playlistParser.parse(content)
+
+  return result.items
+}
+
+parser.parsePlaylist = async function (url) {
+  const content = file.read(url)
+  const result = playlistParser.parse(content)
+  const name = file.getFilename(url)
+  const country = utils.code2name(name)
+
+  return new Playlist({ header: result.header, items: result.items, url, country, name })
+}
+
+module.exports = parser
diff --git a/scripts/regions.json b/scripts/helpers/regions.json
similarity index 100%
rename from scripts/regions.json
rename to scripts/helpers/regions.json
diff --git a/scripts/utils.js b/scripts/helpers/utils.js
similarity index 58%
rename from scripts/utils.js
rename to scripts/helpers/utils.js
index 08465b4bd..7a2bae063 100644
--- a/scripts/utils.js
+++ b/scripts/helpers/utils.js
@@ -1,21 +1,15 @@
-const fs = require('fs')
-const path = require('path')
-const axios = require('axios')
-const zlib = require('zlib')
-const urlParser = require('url')
 const escapeStringRegexp = require('escape-string-regexp')
-const markdownInclude = require('markdown-include')
-const iso6393 = require('@freearhey/iso-639-3')
 const transliteration = require('transliteration')
-const regions = require('./regions')
+const iso6393 = require('@freearhey/iso-639-3')
 const categories = require('./categories')
+const regions = require('./regions')
+
+const utils = {}
 const intlDisplayNames = new Intl.DisplayNames(['en'], {
   style: 'narrow',
   type: 'region'
 })
 
-const utils = {}
-
 utils.name2id = function (name) {
   return transliteration
     .transliterate(name)
@@ -77,81 +71,10 @@ utils.sortBy = function (arr, fields) {
   })
 }
 
-utils.getBasename = function (filename) {
-  return path.basename(filename, path.extname(filename))
-}
-
-utils.filterPlaylists = function (arr, include = '', exclude = '') {
-  if (include) {
-    const included = include.split(',').map(filename => `channels/${filename}.m3u`)
-
-    return arr.filter(i => included.indexOf(i.url) > -1)
-  }
-
-  if (exclude) {
-    const excluded = exclude.split(',').map(filename => `channels/${filename}.m3u`)
-
-    return arr.filter(i => excluded.indexOf(i.url) === -1)
-  }
-
-  return arr
-}
-
-utils.generateTable = function (data, options) {
-  let output = '<table>\n'
-
-  output += '\t<thead>\n\t\t<tr>'
-  for (let column of options.columns) {
-    output += `<th align="${column.align}">${column.name}</th>`
-  }
-  output += '</tr>\n\t</thead>\n'
-
-  output += '\t<tbody>\n'
-  for (let item of data) {
-    output += '\t\t<tr>'
-    let i = 0
-    for (let prop in item) {
-      const column = options.columns[i]
-      let nowrap = column.nowrap
-      let align = column.align
-      output += `<td align="${align}"${nowrap ? ' nowrap' : ''}>${item[prop]}</td>`
-      i++
-    }
-    output += '</tr>\n'
-  }
-  output += '\t</tbody>\n'
-
-  output += '</table>'
-
-  return output
-}
-
-utils.createDir = function (dir) {
-  if (!fs.existsSync(dir)) {
-    fs.mkdirSync(dir)
-  }
-}
-
-utils.readFile = function (filename) {
-  return fs.readFileSync(path.resolve(__dirname) + `/../${filename}`, { encoding: 'utf8' })
-}
-
-utils.appendToFile = function (filename, data) {
-  fs.appendFileSync(path.resolve(__dirname) + '/../' + filename, data)
-}
-
-utils.compileMarkdown = function (filepath) {
-  return markdownInclude.compileFiles(path.resolve(__dirname, filepath))
-}
-
 utils.escapeStringRegexp = function (scring) {
   return escapeStringRegexp(string)
 }
 
-utils.createFile = function (filename, data = '') {
-  fs.writeFileSync(path.resolve(__dirname) + '/../' + filename, data)
-}
-
 utils.sleep = function (ms) {
   return function (x) {
     return new Promise(resolve => setTimeout(() => resolve(x), ms))
@@ -162,17 +85,20 @@ utils.removeProtocol = function (string) {
   return string.replace(/(^\w+:|^)\/\//, '')
 }
 
-utils.savePlaylist = async function (playlist) {
-  const original = utils.readFile(playlist.url)
-  const output = playlist.toString({ raw: true })
+utils.filterPlaylists = function (arr, include = '', exclude = '') {
+  if (include) {
+    const included = include.split(',').map(filename => `channels/${filename}.m3u`)
+
+    return arr.filter(i => included.indexOf(i.url) > -1)
+  }
 
-  if (original === output) {
-    return false
-  } else {
-    utils.createFile(playlist.url, output)
+  if (exclude) {
+    const excluded = exclude.split(',').map(filename => `channels/${filename}.m3u`)
+
+    return arr.filter(i => excluded.indexOf(i.url) === -1)
   }
 
-  return true
+  return arr
 }
 
 module.exports = utils
diff --git a/scripts/parser.js b/scripts/parser.js
deleted file mode 100644
index 0c86a0206..000000000
--- a/scripts/parser.js
+++ /dev/null
@@ -1,244 +0,0 @@
-const playlistParser = require('iptv-playlist-parser')
-const utils = require('./utils')
-const categories = require('./categories')
-const path = require('path')
-
-const sfwCategories = categories.filter(c => !c.nsfw).map(c => c.name)
-const nsfwCategories = categories.filter(c => c.nsfw).map(c => c.name)
-
-const parser = {}
-
-parser.parseIndex = function () {
-  const content = utils.readFile('index.m3u')
-  const result = playlistParser.parse(content)
-
-  return result.items
-}
-
-parser.parsePlaylist = async function (filename) {
-  const content = utils.readFile(filename)
-  const result = playlistParser.parse(content)
-  const name = path.parse(filename).name
-  const country = utils.code2name(name)
-
-  return new Playlist({ header: result.header, items: result.items, url: filename, country, name })
-}
-
-class Playlist {
-  constructor({ header, items, url, name, country }) {
-    this.url = url
-    this.name = name
-    this.country = country
-    this.header = header
-    this.channels = items
-      .map(item => new Channel({ data: item, header, sourceUrl: url }))
-      .filter(channel => channel.url)
-  }
-
-  toString(options = {}) {
-    const config = { raw: false, ...options }
-    let parts = ['#EXTM3U']
-    for (let key in this.header.attrs) {
-      let value = this.header.attrs[key]
-      if (value) {
-        parts.push(`${key}="${value}"`)
-      }
-    }
-
-    let output = `${parts.join(' ')}\n`
-    for (let channel of this.channels) {
-      output += channel.toString(config.raw)
-    }
-
-    return output
-  }
-}
-
-class Channel {
-  constructor({ data, header, sourceUrl }) {
-    this.parseData(data)
-
-    this.filename = utils.getBasename(sourceUrl)
-    if (!this.countries.length) {
-      const countryName = utils.code2name(this.filename)
-      this.countries = countryName ? [{ code: this.filename, name: countryName }] : []
-      this.tvg.country = this.countries.map(c => c.code.toUpperCase()).join(';')
-    }
-  }
-
-  parseData(data) {
-    const title = this.parseTitle(data.name)
-
-    this.tvg = data.tvg
-    this.http = data.http
-    this.url = data.url
-    this.logo = data.tvg.logo
-    this.name = title.channelName
-    this.status = title.streamStatus
-    this.resolution = title.streamResolution
-    this.countries = this.parseCountries(data.tvg.country)
-    this.languages = this.parseLanguages(data.tvg.language)
-    this.category = this.parseCategory(data.group.title)
-    this.raw = data.raw
-  }
-
-  parseCountries(string) {
-    let arr = string
-      .split(';')
-      .reduce((acc, curr) => {
-        const codes = utils.region2codes(curr)
-        if (codes.length) {
-          for (let code of codes) {
-            if (!acc.includes(code)) {
-              acc.push(code)
-            }
-          }
-        } else {
-          acc.push(curr)
-        }
-
-        return acc
-      }, [])
-      .filter(code => code && utils.code2name(code))
-
-    return arr.map(code => {
-      return { code: code.toLowerCase(), name: utils.code2name(code) }
-    })
-  }
-
-  parseLanguages(string) {
-    return string
-      .split(';')
-      .map(name => {
-        const code = name ? utils.language2code(name) : null
-        if (!code) return null
-
-        return {
-          code,
-          name
-        }
-      })
-      .filter(l => l)
-  }
-
-  parseCategory(string) {
-    const category = categories.find(c => c.id === string.toLowerCase())
-
-    return category ? category.name : ''
-  }
-
-  parseTitle(title) {
-    const channelName = title
-      .trim()
-      .split(' ')
-      .map(s => s.trim())
-      .filter(s => {
-        return !/\[|\]/i.test(s) && !/\((\d+)P\)/i.test(s)
-      })
-      .join(' ')
-
-    const streamStatusMatch = title.match(/\[(.*)\]/i)
-    const streamStatus = streamStatusMatch ? streamStatusMatch[1] : null
-
-    const streamResolutionMatch = title.match(/\((\d+)P\)/i)
-    const streamResolutionHeight = streamResolutionMatch ? parseInt(streamResolutionMatch[1]) : null
-    const streamResolution = { width: null, height: streamResolutionHeight }
-
-    return { channelName, streamStatus, streamResolution }
-  }
-
-  get tvgCountry() {
-    return this.tvg.country
-      .split(';')
-      .map(code => utils.code2name(code))
-      .join(';')
-  }
-
-  get tvgLanguage() {
-    return this.tvg.language
-  }
-
-  get tvgUrl() {
-    return this.tvg.id && this.tvg.url ? this.tvg.url : ''
-  }
-
-  get tvgId() {
-    if (this.tvg.id) {
-      return this.tvg.id
-    } else if (this.filename !== 'unsorted') {
-      const id = utils.name2id(this.tvgName)
-
-      return id ? `${id}.${this.filename}` : ''
-    }
-
-    return ''
-  }
-
-  get tvgName() {
-    if (this.tvg.name) {
-      return this.tvg.name
-    } else if (this.filename !== 'unsorted') {
-      return this.name.replace(/\"/gi, '')
-    }
-
-    return ''
-  }
-
-  getInfo() {
-    this.tvg.country = this.tvg.country.toUpperCase()
-
-    let info = `-1 tvg-id="${this.tvgId}" tvg-name="${this.tvgName}" tvg-country="${this.tvg.country}" tvg-language="${this.tvg.language}" tvg-logo="${this.logo}"`
-
-    info += ` group-title="${this.category}",${this.name}`
-
-    if (this.resolution.height) {
-      info += ` (${this.resolution.height}p)`
-    }
-
-    if (this.status) {
-      info += ` [${this.status}]`
-    }
-
-    if (this.http['referrer']) {
-      info += `\n#EXTVLCOPT:http-referrer=${this.http['referrer']}`
-    }
-
-    if (this.http['user-agent']) {
-      info += `\n#EXTVLCOPT:http-user-agent=${this.http['user-agent']}`
-    }
-
-    return info
-  }
-
-  toString(raw = false) {
-    if (raw) return this.raw + '\n'
-
-    return '#EXTINF:' + this.getInfo() + '\n' + this.url + '\n'
-  }
-
-  toObject() {
-    return {
-      name: this.name,
-      logo: this.logo || null,
-      url: this.url,
-      category: this.category || null,
-      languages: this.languages,
-      countries: this.countries,
-      tvg: {
-        id: this.tvgId || null,
-        name: this.tvgName || null,
-        url: this.tvgUrl || null
-      }
-    }
-  }
-
-  isSFW() {
-    return sfwCategories.includes(this.category)
-  }
-
-  isNSFW() {
-    return nsfwCategories.includes(this.category)
-  }
-}
-
-module.exports = parser

From 1a5af21f3567c12866018f6c338b2df21292c03c Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Sun, 1 Aug 2021 19:45:20 +0300
Subject: [PATCH 18/95] Create Playlist.js

---
 scripts/helpers/Playlist.js | 44 +++++++++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)
 create mode 100644 scripts/helpers/Playlist.js

diff --git a/scripts/helpers/Playlist.js b/scripts/helpers/Playlist.js
new file mode 100644
index 000000000..4e8668edd
--- /dev/null
+++ b/scripts/helpers/Playlist.js
@@ -0,0 +1,44 @@
+const Channel = require('./Channel')
+const file = require('./file')
+
+module.exports = class Playlist {
+  constructor({ header, items, url, name, country }) {
+    this.url = url
+    this.name = name
+    this.country = country
+    this.header = header
+    this.channels = items
+      .map(item => new Channel({ data: item, header, sourceUrl: url }))
+      .filter(channel => channel.url)
+  }
+
+  toString(options = {}) {
+    const config = { raw: false, ...options }
+    let parts = ['#EXTM3U']
+    for (let key in this.header.attrs) {
+      let value = this.header.attrs[key]
+      if (value) {
+        parts.push(`${key}="${value}"`)
+      }
+    }
+
+    let output = `${parts.join(' ')}\n`
+    for (let channel of this.channels) {
+      output += channel.toString(config.raw)
+    }
+
+    return output
+  }
+
+  save() {
+    const original = file.read(this.url)
+    const output = this.toString({ raw: true })
+    if (original === output) {
+      return false
+    } else {
+      file.create(this.url, output)
+    }
+
+    return true
+  }
+}

From 6f21d0d1674e9f96948daaabd9d0520010c14a0c Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Sun, 1 Aug 2021 19:45:23 +0300
Subject: [PATCH 19/95] Create Channel.js

---
 scripts/helpers/Channel.js | 193 +++++++++++++++++++++++++++++++++++++
 1 file changed, 193 insertions(+)
 create mode 100644 scripts/helpers/Channel.js

diff --git a/scripts/helpers/Channel.js b/scripts/helpers/Channel.js
new file mode 100644
index 000000000..1dccf41ad
--- /dev/null
+++ b/scripts/helpers/Channel.js
@@ -0,0 +1,193 @@
+const categories = require('./categories')
+const utils = require('./utils')
+const file = require('./file')
+
+const sfwCategories = categories.filter(c => !c.nsfw).map(c => c.name)
+const nsfwCategories = categories.filter(c => c.nsfw).map(c => c.name)
+
+module.exports = class Channel {
+  constructor({ data, header, sourceUrl }) {
+    this.parseData(data)
+
+    this.filename = file.getBasename(sourceUrl)
+    if (!this.countries.length) {
+      const countryName = utils.code2name(this.filename)
+      this.countries = countryName ? [{ code: this.filename, name: countryName }] : []
+      this.tvg.country = this.countries.map(c => c.code.toUpperCase()).join(';')
+    }
+  }
+
+  parseData(data) {
+    const title = this.parseTitle(data.name)
+
+    this.tvg = data.tvg
+    this.http = data.http
+    this.url = data.url
+    this.logo = data.tvg.logo
+    this.name = title.channelName
+    this.status = title.streamStatus
+    this.resolution = title.streamResolution
+    this.countries = this.parseCountries(data.tvg.country)
+    this.languages = this.parseLanguages(data.tvg.language)
+    this.category = this.parseCategory(data.group.title)
+    this.raw = data.raw
+  }
+
+  parseCountries(string) {
+    let arr = string
+      .split(';')
+      .reduce((acc, curr) => {
+        const codes = utils.region2codes(curr)
+        if (codes.length) {
+          for (let code of codes) {
+            if (!acc.includes(code)) {
+              acc.push(code)
+            }
+          }
+        } else {
+          acc.push(curr)
+        }
+
+        return acc
+      }, [])
+      .filter(code => code && utils.code2name(code))
+
+    return arr.map(code => {
+      return { code: code.toLowerCase(), name: utils.code2name(code) }
+    })
+  }
+
+  parseLanguages(string) {
+    return string
+      .split(';')
+      .map(name => {
+        const code = name ? utils.language2code(name) : null
+        if (!code) return null
+
+        return {
+          code,
+          name
+        }
+      })
+      .filter(l => l)
+  }
+
+  parseCategory(string) {
+    const category = categories.find(c => c.id === string.toLowerCase())
+
+    return category ? category.name : ''
+  }
+
+  parseTitle(title) {
+    const channelName = title
+      .trim()
+      .split(' ')
+      .map(s => s.trim())
+      .filter(s => {
+        return !/\[|\]/i.test(s) && !/\((\d+)P\)/i.test(s)
+      })
+      .join(' ')
+
+    const streamStatusMatch = title.match(/\[(.*)\]/i)
+    const streamStatus = streamStatusMatch ? streamStatusMatch[1] : null
+
+    const streamResolutionMatch = title.match(/\((\d+)P\)/i)
+    const streamResolutionHeight = streamResolutionMatch ? parseInt(streamResolutionMatch[1]) : null
+    const streamResolution = { width: null, height: streamResolutionHeight }
+
+    return { channelName, streamStatus, streamResolution }
+  }
+
+  get tvgCountry() {
+    return this.tvg.country
+      .split(';')
+      .map(code => utils.code2name(code))
+      .join(';')
+  }
+
+  get tvgLanguage() {
+    return this.tvg.language
+  }
+
+  get tvgUrl() {
+    return this.tvg.id && this.tvg.url ? this.tvg.url : ''
+  }
+
+  get tvgId() {
+    if (this.tvg.id) {
+      return this.tvg.id
+    } else if (this.filename !== 'unsorted') {
+      const id = utils.name2id(this.tvgName)
+
+      return id ? `${id}.${this.filename}` : ''
+    }
+
+    return ''
+  }
+
+  get tvgName() {
+    if (this.tvg.name) {
+      return this.tvg.name
+    } else if (this.filename !== 'unsorted') {
+      return this.name.replace(/\"/gi, '')
+    }
+
+    return ''
+  }
+
+  getInfo() {
+    this.tvg.country = this.tvg.country.toUpperCase()
+
+    let info = `-1 tvg-id="${this.tvgId}" tvg-name="${this.tvgName}" tvg-country="${this.tvg.country}" tvg-language="${this.tvg.language}" tvg-logo="${this.logo}"`
+
+    info += ` group-title="${this.category}",${this.name}`
+
+    if (this.resolution.height) {
+      info += ` (${this.resolution.height}p)`
+    }
+
+    if (this.status) {
+      info += ` [${this.status}]`
+    }
+
+    if (this.http['referrer']) {
+      info += `\n#EXTVLCOPT:http-referrer=${this.http['referrer']}`
+    }
+
+    if (this.http['user-agent']) {
+      info += `\n#EXTVLCOPT:http-user-agent=${this.http['user-agent']}`
+    }
+
+    return info
+  }
+
+  toString(raw = false) {
+    if (raw) return this.raw + '\n'
+
+    return '#EXTINF:' + this.getInfo() + '\n' + this.url + '\n'
+  }
+
+  toObject() {
+    return {
+      name: this.name,
+      logo: this.logo || null,
+      url: this.url,
+      category: this.category || null,
+      languages: this.languages,
+      countries: this.countries,
+      tvg: {
+        id: this.tvgId || null,
+        name: this.tvgName || null,
+        url: this.tvgUrl || null
+      }
+    }
+  }
+
+  isSFW() {
+    return sfwCategories.includes(this.category)
+  }
+
+  isNSFW() {
+    return nsfwCategories.includes(this.category)
+  }
+}

From f7ea225cacb1de72c9a781d7cca8c990fd783400 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Sun, 1 Aug 2021 19:45:26 +0300
Subject: [PATCH 20/95] Create file.js

---
 scripts/helpers/file.js | 37 +++++++++++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)
 create mode 100644 scripts/helpers/file.js

diff --git a/scripts/helpers/file.js b/scripts/helpers/file.js
new file mode 100644
index 000000000..8b1f96c5d
--- /dev/null
+++ b/scripts/helpers/file.js
@@ -0,0 +1,37 @@
+const fs = require('fs')
+const path = require('path')
+
+const rootPath = path.resolve(__dirname) + '/../../'
+const file = {}
+
+file.getBasename = function (filename) {
+  return path.basename(filename, path.extname(filename))
+}
+
+file.getFilename = function (filename) {
+  return path.parse(filename).name
+}
+
+file.createDir = function (dir) {
+  if (!fs.existsSync(dir)) {
+    fs.mkdirSync(dir)
+  }
+}
+
+file.read = function (filename) {
+  return fs.readFileSync(rootPath + filename, { encoding: 'utf8' })
+}
+
+file.append = function (filename, data) {
+  fs.appendFileSync(rootPath + filename, data)
+}
+
+file.create = function (filename, data = '') {
+  fs.writeFileSync(rootPath + filename, data)
+}
+
+file.compileMarkdown = function (filepath) {
+  return markdownInclude.compileFiles(path.resolve(__dirname, filepath))
+}
+
+module.exports = file

From e41bf2c72d8a8c5d0a96f3aff824efb8e9563173 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Sun, 1 Aug 2021 19:46:42 +0300
Subject: [PATCH 21/95] Update clean.js

---
 scripts/clean.js | 93 +++++++++++-------------------------------------
 1 file changed, 20 insertions(+), 73 deletions(-)

diff --git a/scripts/clean.js b/scripts/clean.js
index 9b4bd2095..34b98db66 100644
--- a/scripts/clean.js
+++ b/scripts/clean.js
@@ -1,14 +1,14 @@
 const { program } = require('commander')
-const parser = require('./parser')
-const utils = require('./utils')
-const axios = require('axios')
 const ProgressBar = require('progress')
+const axios = require('axios')
 const https = require('https')
 const chalk = require('chalk')
+const parser = require('./helpers/parser')
+const utils = require('./helpers/utils')
+const log = require('./helpers/log')
 
 program
   .usage('[OPTIONS]...')
-  .option('-d, --debug', 'Debug mode')
   .option('-c, --country <country>', 'Comma-separated list of country codes', '')
   .option('-e, --exclude <exclude>', 'Comma-separated list of country codes to be excluded', '')
   .option('--delay <delay>', 'Delay between parser requests', 1000)
@@ -16,8 +16,8 @@ program
   .parse(process.argv)
 
 const config = program.opts()
-
 const offlineStatusCodes = [404, 410, 500, 501]
+const ignore = ['Geo-blocked', 'Not 24/7']
 const instance = axios.create({
   timeout: config.timeout,
   maxContentLength: 200000,
@@ -29,60 +29,39 @@ const instance = axios.create({
   }
 })
 
-const ignore = ['Geo-blocked', 'Not 24/7']
-
-const stats = { broken: 0 }
+let broken = 0
 
 async function main() {
-  console.info(`\nStarting...`)
-  console.time('Done in')
-  if (config.debug) {
-    console.info(chalk.yellow(`INFO: Debug mode enabled\n`))
-  }
-  const playlists = parseIndex()
+  log.start()
 
-  for (const playlist of playlists) {
-    await loadPlaylist(playlist.url).then(checkStatus).then(savePlaylist).then(done)
-  }
-
-  finish()
-}
-
-function parseIndex() {
-  console.info(`Parsing 'index.m3u'...`)
+  log.print(`Parsing 'index.m3u'...`)
   let playlists = parser.parseIndex()
   playlists = utils.filterPlaylists(playlists, config.country, config.exclude)
-  console.info(`Found ${playlists.length} playlist(s)\n`)
-
-  return playlists
-}
+  for (const playlist of playlists) {
+    await parser
+      .parsePlaylist(playlist.url)
+      .then(checkStatus)
+      .then(p => p.save())
+  }
 
-async function loadPlaylist(url) {
-  console.info(`Processing '${url}'...`)
-  return parser.parsePlaylist(url)
+  log.finish()
 }
 
 async function checkStatus(playlist) {
-  let bar
-  if (!config.debug) {
-    bar = new ProgressBar('  Testing: [:bar] :current/:total (:percent) ', {
-      total: playlist.channels.length
-    })
-  }
+  let bar = new ProgressBar(`Checking '${playlist.url}': [:bar] :current/:total (:percent) `, {
+    total: playlist.channels.length
+  })
   const results = []
   const total = playlist.channels.length
   for (const [index, channel] of playlist.channels.entries()) {
     const current = index + 1
     const counter = chalk.gray(`[${current}/${total}]`)
-    if (bar) bar.tick()
+    bar.tick()
     if (
       (channel.status && ignore.map(i => i.toLowerCase()).includes(channel.status.toLowerCase())) ||
       (!channel.url.startsWith('http://') && !channel.url.startsWith('https://'))
     ) {
       results.push(channel)
-      if (config.debug) {
-        console.info(`  ${counter} ${chalk.green('online')} ${chalk.white(channel.url)}`)
-      }
     } else {
       const CancelToken = axios.CancelToken
       const source = CancelToken.source()
@@ -95,23 +74,14 @@ async function checkStatus(playlist) {
         .then(() => {
           clearTimeout(timeout)
           results.push(channel)
-          if (config.debug) {
-            console.info(`  ${counter} ${chalk.green('online')} ${chalk.white(channel.url)}`)
-          }
         })
         .then(utils.sleep(config.delay))
         .catch(err => {
           clearTimeout(timeout)
           if (err.response && offlineStatusCodes.includes(err.response.status)) {
-            if (config.debug) {
-              console.info(`  ${counter} ${chalk.red('offline')} ${chalk.white(channel.url)}`)
-            }
-            stats.broken++
+            broken++
           } else {
             results.push(channel)
-            if (config.debug) {
-              console.info(`  ${counter} ${chalk.green('online')} ${chalk.white(channel.url)}`)
-            }
           }
         })
     }
@@ -122,27 +92,4 @@ async function checkStatus(playlist) {
   return playlist
 }
 
-async function savePlaylist(playlist) {
-  const original = utils.readFile(playlist.url)
-  const output = playlist.toString({ raw: true })
-
-  if (original === output) {
-    console.info(`No changes have been made.`)
-    return false
-  } else {
-    utils.createFile(playlist.url, output)
-    console.info(`Playlist has been updated. Removed ${stats.broken} links.`)
-  }
-
-  return true
-}
-
-async function done() {
-  console.info(` `)
-}
-
-function finish() {
-  console.timeEnd('Done in')
-}
-
 main()

From bb62e295a4f142fd810e3312a793a175efce8bdb Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Sun, 1 Aug 2021 19:50:33 +0300
Subject: [PATCH 22/95] Update filter.js

---
 scripts/filter.js | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/scripts/filter.js b/scripts/filter.js
index 35017c4f7..c3be8b9d0 100644
--- a/scripts/filter.js
+++ b/scripts/filter.js
@@ -1,7 +1,7 @@
-const blacklist = require('./blacklist.json')
-const parser = require('./parser')
-const utils = require('./utils')
-const log = require('./log')
+const blacklist = require('./helpers/blacklist.json')
+const parser = require('./helpers/parser')
+const utils = require('./helpers/utils')
+const log = require('./helpers/log')
 
 async function main() {
   log.start()
@@ -13,6 +13,7 @@ async function main() {
     await parser.parsePlaylist(playlist.url).then(removeBlacklisted).then(utils.savePlaylist)
   }
 
+  log.print('\n')
   log.finish()
 }
 

From 2eddce0e8392c9c22112298c75dac7db92290bc2 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Sun, 1 Aug 2021 19:52:47 +0300
Subject: [PATCH 23/95] Update format.js

---
 scripts/format.js | 19 ++++++++++++-------
 1 file changed, 12 insertions(+), 7 deletions(-)

diff --git a/scripts/format.js b/scripts/format.js
index 70c3decab..b687eed2f 100644
--- a/scripts/format.js
+++ b/scripts/format.js
@@ -1,6 +1,6 @@
-const parser = require('./parser')
-const utils = require('./utils')
-const log = require('./log')
+const parser = require('./helpers/parser')
+const utils = require('./helpers/utils')
+const log = require('./helpers/log')
 
 async function main() {
   log.start()
@@ -9,9 +9,13 @@ async function main() {
   let playlists = parser.parseIndex().filter(i => i.url !== 'channels/unsorted.m3u')
   for (const playlist of playlists) {
     log.print(`\nProcessing '${playlist.url}'...`)
-    await parser.parsePlaylist(playlist.url).then(sortChannels).then(utils.savePlaylist)
+    await parser
+      .parsePlaylist(playlist.url)
+      .then(sortChannels)
+      .then(p => p.save())
   }
 
+  log.print('\n')
   log.finish()
 }
 
@@ -19,9 +23,10 @@ async function sortChannels(playlist) {
   const channels = [...playlist.channels]
   utils.sortBy(channels, ['name', 'url'])
 
-  if (JSON.stringify(playlist.channels) !== JSON.stringify(channels)) log.print('updated')
-
-  playlist.channels = channels
+  if (JSON.stringify(playlist.channels) !== JSON.stringify(channels)) {
+    log.print('updated')
+    playlist.channels = channels
+  }
 
   return playlist
 }

From d9dc411cfd7f93f883f839c641d5cf7f20f74d09 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Sun, 1 Aug 2021 19:56:02 +0300
Subject: [PATCH 24/95] Update remove-duplicates.js

---
 scripts/remove-duplicates.js | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/scripts/remove-duplicates.js b/scripts/remove-duplicates.js
index 8b21c748d..f2d62e919 100644
--- a/scripts/remove-duplicates.js
+++ b/scripts/remove-duplicates.js
@@ -1,6 +1,6 @@
-const parser = require('./parser')
-const utils = require('./utils')
-const log = require('./log')
+const parser = require('./helpers/parser')
+const utils = require('./helpers/utils')
+const log = require('./helpers/log')
 
 let globalBuffer = []
 
@@ -15,7 +15,7 @@ async function main() {
       .parsePlaylist(playlist.url)
       .then(addToBuffer)
       .then(removeDuplicates)
-      .then(utils.savePlaylist)
+      .then(p => p.save())
   }
 
   if (playlists.length) {
@@ -23,14 +23,14 @@ async function main() {
     await parser
       .parsePlaylist('channels/unsorted.m3u')
       .then(removeUnsortedDuplicates)
-      .then(utils.savePlaylist)
+      .then(p => p.save())
   }
 
+  log.print('\n')
   log.finish()
 }
 
 async function addToBuffer(playlist) {
-  if (playlist.url === 'channels/unsorted.m3u') return playlist
   globalBuffer = globalBuffer.concat(playlist.channels)
 
   return playlist

From 905b45f7fec0f85380df92c560c34c431f88f605 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Sun, 1 Aug 2021 20:11:37 +0300
Subject: [PATCH 25/95] Update Playlist.js

---
 scripts/helpers/Playlist.js | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/scripts/helpers/Playlist.js b/scripts/helpers/Playlist.js
index 4e8668edd..5b26b4ac2 100644
--- a/scripts/helpers/Playlist.js
+++ b/scripts/helpers/Playlist.js
@@ -32,13 +32,9 @@ module.exports = class Playlist {
 
   save() {
     const original = file.read(this.url)
-    const output = this.toString({ raw: true })
-    if (original === output) {
-      return false
-    } else {
+    const output = this.toString()
+    if (original !== output) {
       file.create(this.url, output)
     }
-
-    return true
   }
 }

From 95bf8ae6e9156d9eeaf9cfc90589affb3a39365e Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Sun, 1 Aug 2021 20:12:17 +0300
Subject: [PATCH 26/95] Update detect-resolution.js

---
 scripts/detect-resolution.js | 63 ++++++++++++------------------------
 1 file changed, 21 insertions(+), 42 deletions(-)

diff --git a/scripts/detect-resolution.js b/scripts/detect-resolution.js
index 19c2a7d90..57c33267c 100644
--- a/scripts/detect-resolution.js
+++ b/scripts/detect-resolution.js
@@ -1,13 +1,13 @@
 const { program } = require('commander')
-const parser = require('./parser')
-const utils = require('./utils')
-const axios = require('axios')
 const ProgressBar = require('progress')
+const axios = require('axios')
 const https = require('https')
+const parser = require('./helpers/parser')
+const utils = require('./helpers/utils')
+const log = require('./helpers/log')
 
 program
   .usage('[OPTIONS]...')
-  .option('-d, --debug', 'Debug mode')
   .option('-c, --country <country>', 'Comma-separated list of country codes', '')
   .option('-e, --exclude <exclude>', 'Comma-separated list of country codes to be excluded', '')
   .option('--delay <delay>', 'Delay between parser requests', 1000)
@@ -15,7 +15,6 @@ program
   .parse(process.argv)
 
 const config = program.opts()
-
 const instance = axios.create({
   timeout: config.timeout,
   maxContentLength: 200000,
@@ -25,36 +24,30 @@ const instance = axios.create({
 })
 
 async function main() {
-  utils.log('Starting...\n')
-  console.time('Done in')
-
-  const playlists = parseIndex()
-  for (const playlist of playlists) {
-    await loadPlaylist(playlist.url).then(detectResolution).then(savePlaylist)
-  }
-
-  finish()
-}
+  log.start()
 
-function parseIndex() {
-  utils.log(`Parsing 'index.m3u'...\n`)
+  log.print(`Parsing 'index.m3u'...\n`)
   let playlists = parser.parseIndex()
   playlists = utils
     .filterPlaylists(playlists, config.country, config.exclude)
     .filter(i => i.url !== 'channels/unsorted.m3u')
 
-  return playlists
-}
+  for (const playlist of playlists) {
+    await parser
+      .parsePlaylist(playlist.url)
+      .then(detectResolution)
+      .then(p => p.save())
+  }
 
-async function loadPlaylist(url) {
-  return parser.parsePlaylist(url)
+  log.finish()
 }
 
 async function detectResolution(playlist) {
+  const channels = []
   const bar = new ProgressBar(`Processing '${playlist.url}': [:bar] :current/:total (:percent) `, {
     total: playlist.channels.length
   })
-  const results = []
+  let updated = false
   for (const channel of playlist.channels) {
     bar.tick()
     if (!channel.resolution.height) {
@@ -81,15 +74,19 @@ async function detectResolution(playlist) {
           const resolution = parseResolution(response.data)
           if (resolution) {
             channel.resolution = resolution
+            updated = true
           }
         }
       }
     }
 
-    results.push(channel)
+    channels.push(channel)
   }
 
-  playlist.channels = results
+  if (updated) {
+    log.print(`File '${playlist.url}' has been updated\n`)
+    playlist.channels = channels
+  }
 
   return playlist
 }
@@ -109,22 +106,4 @@ function parseResolution(string) {
     : undefined
 }
 
-async function savePlaylist(playlist) {
-  const original = utils.readFile(playlist.url)
-  const output = playlist.toString()
-
-  if (original === output) {
-    return false
-  } else {
-    utils.createFile(playlist.url, output)
-    utils.log(`Playlist '${playlist.url}' has been updated\n`)
-  }
-
-  return true
-}
-
-function finish() {
-  console.timeEnd('Done in')
-}
-
 main()

From 61b2a014f7ab3a15c47fd01eba1f5638d1fe3652 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Sun, 1 Aug 2021 20:31:46 +0300
Subject: [PATCH 27/95] Update db.js

---
 scripts/helpers/db.js | 9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)

diff --git a/scripts/helpers/db.js b/scripts/helpers/db.js
index a0d67cb4d..417742f84 100644
--- a/scripts/helpers/db.js
+++ b/scripts/helpers/db.js
@@ -2,14 +2,12 @@ const categories = require('./categories')
 const parser = require('./parser')
 const utils = require('./utils')
 
-const sfwCategories = categories.filter(c => !c.nsfw).map(c => c.name)
-
 const db = {}
 
-db.load = function () {
+db.load = async function () {
   const items = parser.parseIndex()
   for (const item of items) {
-    const playlist = parser.parsePlaylist(item.url)
+    const playlist = await parser.parsePlaylist(item.url)
     db.playlists.add(playlist)
     for (const channel of playlist.channels) {
       db.channels.add(channel)
@@ -107,9 +105,6 @@ db.channels = {
   all() {
     return this.list
   },
-  sfw() {
-    return this.list.filter(i => sfwCategories.includes(i.category))
-  },
   forCountry(country) {
     this.filter = {
       field: 'countries',

From 911fe272d66284387edf42e310afcc680c564781 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Sun, 1 Aug 2021 20:31:49 +0300
Subject: [PATCH 28/95] Update generate.js

---
 scripts/generate.js | 87 ++++++++++++++++++++++++---------------------
 1 file changed, 46 insertions(+), 41 deletions(-)

diff --git a/scripts/generate.js b/scripts/generate.js
index f561c9e23..459497894 100644
--- a/scripts/generate.js
+++ b/scripts/generate.js
@@ -1,11 +1,11 @@
-const db = require('./db')
-const utils = require('./utils')
+const file = require('./helpers/file')
+const log = require('./helpers/log')
+const db = require('./helpers/db')
 
 const ROOT_DIR = './.gh-pages'
 
-db.load()
-
-function main() {
+async function main() {
+  await loadDatabase()
   createRootDirectory()
   createNoJekyllFile()
   generateIndex()
@@ -16,51 +16,56 @@ function main() {
   generateCountries()
   generateLanguages()
   generateChannelsJson()
-  finish()
+  showResults()
+}
+
+async function loadDatabase() {
+  log.print('Loading database...\n')
+  await db.load()
 }
 
 function createRootDirectory() {
-  console.log('Creating .gh-pages folder...')
-  utils.createDir(ROOT_DIR)
+  log.print('Creating .gh-pages folder...\n')
+  file.createDir(ROOT_DIR)
 }
 
 function createNoJekyllFile() {
-  console.log('Creating .nojekyll...')
-  utils.createFile(`${ROOT_DIR}/.nojekyll`)
+  log.print('Creating .nojekyll...\n')
+  file.create(`${ROOT_DIR}/.nojekyll`)
 }
 
 function generateIndex() {
-  console.log('Generating index.m3u...')
+  log.print('Generating index.m3u...\n')
   const filename = `${ROOT_DIR}/index.m3u`
-  utils.createFile(filename, '#EXTM3U\n')
+  file.create(filename, '#EXTM3U\n')
 
   const nsfwFilename = `${ROOT_DIR}/index.nsfw.m3u`
-  utils.createFile(nsfwFilename, '#EXTM3U\n')
+  file.create(nsfwFilename, '#EXTM3U\n')
 
   const channels = db.channels.sortBy(['name', 'url']).removeDuplicates().get()
   for (const channel of channels) {
     if (!channel.isNSFW()) {
-      utils.appendToFile(filename, channel.toString())
+      file.append(filename, channel.toString())
     }
-    utils.appendToFile(nsfwFilename, channel.toString())
+    file.append(nsfwFilename, channel.toString())
   }
 }
 
 function generateCategoryIndex() {
-  console.log('Generating index.category.m3u...')
+  log.print('Generating index.category.m3u...\n')
   const filename = `${ROOT_DIR}/index.category.m3u`
-  utils.createFile(filename, '#EXTM3U\n')
+  file.create(filename, '#EXTM3U\n')
 
   const channels = db.channels.sortBy(['category', 'name', 'url']).removeDuplicates().get()
   for (const channel of channels) {
-    utils.appendToFile(filename, channel.toString())
+    file.append(filename, channel.toString())
   }
 }
 
 function generateCountryIndex() {
-  console.log('Generating index.country.m3u...')
+  log.print('Generating index.country.m3u...\n')
   const filename = `${ROOT_DIR}/index.country.m3u`
-  utils.createFile(filename, '#EXTM3U\n')
+  file.create(filename, '#EXTM3U\n')
 
   for (const country of [{ code: 'undefined' }, ...db.countries.sortBy(['name']).all()]) {
     const channels = db.channels
@@ -73,7 +78,7 @@ function generateCountryIndex() {
       const nsfw = channel.isNSFW()
       channel.category = country.name || ''
       if (!nsfw) {
-        utils.appendToFile(filename, channel.toString())
+        file.append(filename, channel.toString())
       }
       channel.category = category
     }
@@ -81,9 +86,9 @@ function generateCountryIndex() {
 }
 
 function generateLanguageIndex() {
-  console.log('Generating index.language.m3u...')
+  log.print('Generating index.language.m3u...\n')
   const filename = `${ROOT_DIR}/index.language.m3u`
-  utils.createFile(filename, '#EXTM3U\n')
+  file.create(filename, '#EXTM3U\n')
 
   for (const language of [{ code: 'undefined' }, ...db.languages.sortBy(['name']).all()]) {
     const channels = db.channels
@@ -96,7 +101,7 @@ function generateLanguageIndex() {
       const nsfw = channel.isNSFW()
       channel.category = language.name || ''
       if (!nsfw) {
-        utils.appendToFile(filename, channel.toString())
+        file.append(filename, channel.toString())
       }
       channel.category = category
     }
@@ -104,13 +109,13 @@ function generateLanguageIndex() {
 }
 
 function generateCategories() {
-  console.log(`Generating /categories...`)
+  log.print(`Generating /categories...\n`)
   const outputDir = `${ROOT_DIR}/categories`
-  utils.createDir(outputDir)
+  file.createDir(outputDir)
 
   for (const category of [...db.categories.all(), { id: 'other' }]) {
     const filename = `${outputDir}/${category.id}.m3u`
-    utils.createFile(filename, '#EXTM3U\n')
+    file.create(filename, '#EXTM3U\n')
 
     const channels = db.channels
       .sortBy(['name', 'url'])
@@ -118,19 +123,19 @@ function generateCategories() {
       .removeDuplicates()
       .get()
     for (const channel of channels) {
-      utils.appendToFile(filename, channel.toString())
+      file.append(filename, channel.toString())
     }
   }
 }
 
 function generateCountries() {
-  console.log(`Generating /countries...`)
+  log.print(`Generating /countries...\n`)
   const outputDir = `${ROOT_DIR}/countries`
-  utils.createDir(outputDir)
+  file.createDir(outputDir)
 
   for (const country of [...db.countries.all(), { code: 'undefined' }]) {
     const filename = `${outputDir}/${country.code}.m3u`
-    utils.createFile(filename, '#EXTM3U\n')
+    file.create(filename, '#EXTM3U\n')
 
     const channels = db.channels
       .sortBy(['name', 'url'])
@@ -139,20 +144,20 @@ function generateCountries() {
       .get()
     for (const channel of channels) {
       if (!channel.isNSFW()) {
-        utils.appendToFile(filename, channel.toString())
+        file.append(filename, channel.toString())
       }
     }
   }
 }
 
 function generateLanguages() {
-  console.log(`Generating /languages...`)
+  log.print(`Generating /languages...\n`)
   const outputDir = `${ROOT_DIR}/languages`
-  utils.createDir(outputDir)
+  file.createDir(outputDir)
 
   for (const language of [...db.languages.all(), { code: 'undefined' }]) {
     const filename = `${outputDir}/${language.code}.m3u`
-    utils.createFile(filename, '#EXTM3U\n')
+    file.create(filename, '#EXTM3U\n')
 
     const channels = db.channels
       .sortBy(['name', 'url'])
@@ -161,25 +166,25 @@ function generateLanguages() {
       .get()
     for (const channel of channels) {
       if (!channel.isNSFW()) {
-        utils.appendToFile(filename, channel.toString())
+        file.append(filename, channel.toString())
       }
     }
   }
 }
 
 function generateChannelsJson() {
-  console.log('Generating channels.json...')
+  log.print('Generating channels.json...\n')
   const filename = `${ROOT_DIR}/channels.json`
   const channels = db.channels
     .sortBy(['name', 'url'])
     .get()
     .map(c => c.toObject())
-  utils.createFile(filename, JSON.stringify(channels))
+  file.create(filename, JSON.stringify(channels))
 }
 
-function finish() {
-  console.log(
-    `\nTotal: ${db.channels.count()} channels, ${db.countries.count()} countries, ${db.languages.count()} languages, ${db.categories.count()} categories.`
+function showResults() {
+  log.print(
+    `Total: ${db.channels.count()} channels, ${db.countries.count()} countries, ${db.languages.count()} languages, ${db.categories.count()} categories.\n`
   )
 }
 

From e3941ae31c313648858f17c3954f451068d4c52e Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Sun, 1 Aug 2021 20:42:04 +0300
Subject: [PATCH 29/95] Update file.js

---
 scripts/helpers/file.js | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/scripts/helpers/file.js b/scripts/helpers/file.js
index 8b1f96c5d..cb946c337 100644
--- a/scripts/helpers/file.js
+++ b/scripts/helpers/file.js
@@ -1,5 +1,6 @@
-const fs = require('fs')
+const markdownInclude = require('markdown-include')
 const path = require('path')
+const fs = require('fs')
 
 const rootPath = path.resolve(__dirname) + '/../../'
 const file = {}
@@ -30,8 +31,8 @@ file.create = function (filename, data = '') {
   fs.writeFileSync(rootPath + filename, data)
 }
 
-file.compileMarkdown = function (filepath) {
-  return markdownInclude.compileFiles(path.resolve(__dirname, filepath))
+file.compileMarkdown = function (filename) {
+  markdownInclude.compileFiles(rootPath + filename)
 }
 
 module.exports = file

From 35a1e96ca2e0e90a0225e0a7902bb776afc16946 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Sun, 1 Aug 2021 20:42:11 +0300
Subject: [PATCH 30/95] Update update-readme.js

---
 scripts/update-readme.js | 78 ++++++++++++++++++++++++++--------------
 1 file changed, 52 insertions(+), 26 deletions(-)

diff --git a/scripts/update-readme.js b/scripts/update-readme.js
index 3f20e0a73..e4a96489c 100644
--- a/scripts/update-readme.js
+++ b/scripts/update-readme.js
@@ -1,20 +1,25 @@
-const utils = require('./utils')
-const db = require('./db')
-const parser = require('./parser')
-
-db.load()
-
-function main() {
-  start()
+const utils = require('./helpers/utils')
+const file = require('./helpers/file')
+const log = require('./helpers/log')
+const db = require('./helpers/db')
+
+async function main() {
+  log.start()
+  await loadDatabase()
   generateCategoriesTable()
   generateCountriesTable()
   generateLanguagesTable()
   generateReadme()
-  finish()
+  log.finish()
+}
+
+async function loadDatabase() {
+  log.print('Loading database...\n')
+  await db.load()
 }
 
 function generateCategoriesTable() {
-  console.log(`Generating categories table...`)
+  log.print('Generating categories table...\n')
 
   const categories = []
   for (const category of [...db.categories.all(), { name: 'Other', id: 'other' }]) {
@@ -25,7 +30,7 @@ function generateCategoriesTable() {
     })
   }
 
-  const table = utils.generateTable(categories, {
+  const table = generateTable(categories, {
     columns: [
       { name: 'Category', align: 'left' },
       { name: 'Channels', align: 'right' },
@@ -33,11 +38,11 @@ function generateCategoriesTable() {
     ]
   })
 
-  utils.createFile('./.readme/_categories.md', table)
+  file.create('./.readme/_categories.md', table)
 }
 
 function generateCountriesTable() {
-  console.log(`Generating countries table...`)
+  log.print('Generating countries table...\n')
 
   const countries = []
   for (const country of [
@@ -53,7 +58,7 @@ function generateCountriesTable() {
     })
   }
 
-  const table = utils.generateTable(countries, {
+  const table = generateTable(countries, {
     columns: [
       { name: 'Country', align: 'left' },
       { name: 'Channels', align: 'right' },
@@ -61,11 +66,11 @@ function generateCountriesTable() {
     ]
   })
 
-  utils.createFile('./.readme/_countries.md', table)
+  file.create('./.readme/_countries.md', table)
 }
 
 function generateLanguagesTable() {
-  console.log(`Generating languages table...`)
+  log.print('Generating languages table...\n')
   const languages = []
 
   for (const language of [
@@ -79,7 +84,7 @@ function generateLanguagesTable() {
     })
   }
 
-  const table = utils.generateTable(languages, {
+  const table = generateTable(languages, {
     columns: [
       { name: 'Language', align: 'left' },
       { name: 'Channels', align: 'right' },
@@ -87,20 +92,41 @@ function generateLanguagesTable() {
     ]
   })
 
-  utils.createFile('./.readme/_languages.md', table)
+  file.create('./.readme/_languages.md', table)
 }
 
-function generateReadme() {
-  console.log(`Generating README.md...`)
-  utils.compileMarkdown('../.readme/config.json')
-}
+function generateTable(data, options) {
+  let output = '<table>\n'
 
-function start() {
-  console.log(`Starting...`)
+  output += '\t<thead>\n\t\t<tr>'
+  for (let column of options.columns) {
+    output += `<th align="${column.align}">${column.name}</th>`
+  }
+  output += '</tr>\n\t</thead>\n'
+
+  output += '\t<tbody>\n'
+  for (let item of data) {
+    output += '\t\t<tr>'
+    let i = 0
+    for (let prop in item) {
+      const column = options.columns[i]
+      let nowrap = column.nowrap
+      let align = column.align
+      output += `<td align="${align}"${nowrap ? ' nowrap' : ''}>${item[prop]}</td>`
+      i++
+    }
+    output += '</tr>\n'
+  }
+  output += '\t</tbody>\n'
+
+  output += '</table>'
+
+  return output
 }
 
-function finish() {
-  console.log(`Done.`)
+function generateReadme() {
+  log.print('Generating README.md...\n')
+  file.compileMarkdown('.readme/config.json')
 }
 
 main()

From 2a83eb675da8f3101babe0ab85adb8c436e1924d Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Sun, 1 Aug 2021 20:46:26 +0300
Subject: [PATCH 31/95] Update filter.js

---
 scripts/filter.js | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/scripts/filter.js b/scripts/filter.js
index c3be8b9d0..c36bcfb11 100644
--- a/scripts/filter.js
+++ b/scripts/filter.js
@@ -31,10 +31,9 @@ async function removeBlacklisted(playlist) {
 
   if (playlist.channels.length !== channels.length) {
     log.print(`updated`)
+    playlist.channels = channels
   }
 
-  playlist.channels = channels
-
   return playlist
 }
 

From e9f5494a299c72ba5174b180afc7aca634099a90 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Sun, 1 Aug 2021 20:49:00 +0300
Subject: [PATCH 32/95] Update clean.js

---
 scripts/clean.js | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/scripts/clean.js b/scripts/clean.js
index 34b98db66..cb5ecced2 100644
--- a/scripts/clean.js
+++ b/scripts/clean.js
@@ -51,7 +51,7 @@ async function checkStatus(playlist) {
   let bar = new ProgressBar(`Checking '${playlist.url}': [:bar] :current/:total (:percent) `, {
     total: playlist.channels.length
   })
-  const results = []
+  const channels = []
   const total = playlist.channels.length
   for (const [index, channel] of playlist.channels.entries()) {
     const current = index + 1
@@ -61,7 +61,7 @@ async function checkStatus(playlist) {
       (channel.status && ignore.map(i => i.toLowerCase()).includes(channel.status.toLowerCase())) ||
       (!channel.url.startsWith('http://') && !channel.url.startsWith('https://'))
     ) {
-      results.push(channel)
+      channels.push(channel)
     } else {
       const CancelToken = axios.CancelToken
       const source = CancelToken.source()
@@ -73,7 +73,7 @@ async function checkStatus(playlist) {
         .get(channel.url, { cancelToken: source.token })
         .then(() => {
           clearTimeout(timeout)
-          results.push(channel)
+          channels.push(channel)
         })
         .then(utils.sleep(config.delay))
         .catch(err => {
@@ -81,13 +81,16 @@ async function checkStatus(playlist) {
           if (err.response && offlineStatusCodes.includes(err.response.status)) {
             broken++
           } else {
-            results.push(channel)
+            channels.push(channel)
           }
         })
     }
   }
 
-  playlist.channels = results
+  if (playlist.channels.length !== channels.length) {
+    log.print(`File '${playlist.url}' has been updated\n`)
+    playlist.channels = channels
+  }
 
   return playlist
 }

From dce06bfa8a44056c920e7c22e87be1bca95da218 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Sun, 1 Aug 2021 20:56:00 +0300
Subject: [PATCH 33/95] Update auto-update.yml

for test only
---
 .github/workflows/auto-update.yml | 323 +++++++++++++++---------------
 1 file changed, 161 insertions(+), 162 deletions(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index 4e70afcda..6c11b2d02 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -59,168 +59,167 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        country:
-          [
-            ad,
-            ae,
-            af,
-            ag,
-            al,
-            am,
-            an,
-            ao,
-            ar,
-            at,
-            au,
-            aw,
-            az,
-            ba,
-            bb,
-            bd,
-            be,
-            bf,
-            bg,
-            bh,
-            bn,
-            bo,
-            br,
-            bs,
-            by,
-            ca,
-            cd,
-            cg,
-            ch,
-            ci,
-            cl,
-            cm,
-            cn,
-            co,
-            cr,
-            cu,
-            cw,
-            cy,
-            cz,
-            de,
-            dk,
-            do,
-            dz,
-            ec,
-            ee,
-            eg,
-            es,
-            et,
-            fi,
-            fj,
-            fo,
-            fr,
-            ge,
-            gh,
-            gm,
-            gn,
-            gp,
-            gq,
-            gr,
-            gt,
-            hk,
-            hn,
-            hr,
-            ht,
-            hu,
-            id,
-            ie,
-            il,
-            in,
-            iq,
-            ir,
-            is,
-            it,
-            jm,
-            jo,
-            jp,
-            ke,
-            kg,
-            kh,
-            kp,
-            kr,
-            kw,
-            kz,
-            la,
-            lb,
-            li,
-            lk,
-            lt,
-            lu,
-            lv,
-            ly,
-            ma,
-            mc,
-            md,
-            me,
-            mk,
-            ml,
-            mm,
-            mn,
-            mo,
-            mt,
-            mv,
-            mx,
-            my,
-            mz,
-            ne,
-            ng,
-            ni,
-            nl,
-            no,
-            np,
-            nz,
-            om,
-            pa,
-            pe,
-            ph,
-            pk,
-            pl,
-            pr,
-            ps,
-            pt,
-            py,
-            qa,
-            ro,
-            rs,
-            ru,
-            rw,
-            sa,
-            sd,
-            se,
-            sg,
-            si,
-            sk,
-            sl,
-            sm,
-            sn,
-            so,
-            sv,
-            sy,
-            th,
-            tj,
-            tm,
-            tn,
-            tr,
-            tt,
-            tw,
-            tz,
-            ua,
-            ug,
-            uk,
-            us,
-            uy,
-            uz,
-            va,
-            ve,
-            vi,
-            vn,
-            xk,
-            ye,
-            zm
+        country: [
+            # ad,
+            # ae,
+            # af,
+            # ag,
+            # al,
+            # am,
+            an
+            # ao,
+            # ar,
+            # at,
+            # au,
+            # aw,
+            # az,
+            # ba,
+            # bb,
+            # bd,
+            # be,
+            # bf,
+            # bg,
+            # bh,
+            # bn,
+            # bo,
+            # br,
+            # bs,
+            # by,
+            # ca,
+            # cd,
+            # cg,
+            # ch,
+            # ci,
+            # cl,
+            # cm,
+            # cn,
+            # co,
+            # cr,
+            # cu,
+            # cw,
+            # cy,
+            # cz,
+            # de,
+            # dk,
+            # do,
+            # dz,
+            # ec,
+            # ee,
+            # eg,
+            # es,
+            # et,
+            # fi,
+            # fj,
+            # fo,
+            # fr,
+            # ge,
+            # gh,
+            # gm,
+            # gn,
+            # gp,
+            # gq,
+            # gr,
+            # gt,
+            # hk,
+            # hn,
+            # hr,
+            # ht,
+            # hu,
+            # id,
+            # ie,
+            # il,
+            # in,
+            # iq,
+            # ir,
+            # is,
+            # it,
+            # jm,
+            # jo,
+            # jp,
+            # ke,
+            # kg,
+            # kh,
+            # kp,
+            # kr,
+            # kw,
+            # kz,
+            # la,
+            # lb,
+            # li,
+            # lk,
+            # lt,
+            # lu,
+            # lv,
+            # ly,
+            # ma,
+            # mc,
+            # md,
+            # me,
+            # mk,
+            # ml,
+            # mm,
+            # mn,
+            # mo,
+            # mt,
+            # mv,
+            # mx,
+            # my,
+            # mz,
+            # ne,
+            # ng,
+            # ni,
+            # nl,
+            # no,
+            # np,
+            # nz,
+            # om,
+            # pa,
+            # pe,
+            # ph,
+            # pk,
+            # pl,
+            # pr,
+            # ps,
+            # pt,
+            # py,
+            # qa,
+            # ro,
+            # rs,
+            # ru,
+            # rw,
+            # sa,
+            # sd,
+            # se,
+            # sg,
+            # si,
+            # sk,
+            # sl,
+            # sm,
+            # sn,
+            # so,
+            # sv,
+            # sy,
+            # th,
+            # tj,
+            # tm,
+            # tn,
+            # tr,
+            # tt,
+            # tw,
+            # tz,
+            # ua,
+            # ug,
+            # uk,
+            # us,
+            # uy,
+            # uz,
+            # va,
+            # ve,
+            # vi,
+            # vn,
+            # xk,
+            # ye,
+            # zm
           ]
     steps:
       - name: Checkout

From a79edce7b173fa322855c9d4ae5cc6ca161996fd Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Mon, 2 Aug 2021 04:13:25 +0300
Subject: [PATCH 34/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 77 +++++++++++++++++++------------
 1 file changed, 47 insertions(+), 30 deletions(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index 6c11b2d02..79f12fb6f 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -4,54 +4,64 @@ on:
   schedule:
     - cron: '0 0 * * *'
 jobs:
+  create-branch:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+      - name: Create Branch
+        run: git branch bot/auto-update && git push
   remove-duplicates:
     runs-on: ubuntu-latest
+    needs: create-branch
     steps:
       - name: Checkout
         uses: actions/checkout@v2
+        with:
+          ref: bot/auto-update
       - name: Install Dependencies
         run: npm install
       - name: Remove Duplicates
         run: node scripts/remove-duplicates.js
-      - name: Upload Artifact
-        uses: actions/upload-artifact@v2
+      - name: Commit Changes
+        uses: stefanzweifel/git-auto-commit-action@v4
         with:
-          name: channels
-          path: channels/
+          commit_message: '[Bot] Removed duplicates'
+          branch: bot/auto-update
   filter:
     runs-on: ubuntu-latest
     needs: remove-duplicates
     steps:
       - name: Checkout
         uses: actions/checkout@v2
-      - name: Download Artifacts
-        uses: actions/download-artifact@v2
+        with:
+          ref: bot/auto-update
       - name: Install Dependencies
         run: npm install
       - name: Filter Playlists
         run: node scripts/filter.js
-      - name: Upload Artifact
-        uses: actions/upload-artifact@v2
+      - name: Commit Changes
+        uses: stefanzweifel/git-auto-commit-action@v4
         with:
-          name: channels
-          path: channels/
+          commit_message: '[Bot] Filtered channels'
+          branch: bot/auto-update
   format:
     runs-on: ubuntu-latest
     needs: filter
     steps:
       - name: Checkout
         uses: actions/checkout@v2
-      - name: Download Artifacts
-        uses: actions/download-artifact@v2
+        with:
+          ref: bot/auto-update
       - name: Install Dependencies
         run: npm install
       - name: Format Playlists
         run: node scripts/format.js
-      - name: Upload Artifact
-        uses: actions/upload-artifact@v2
+      - name: Commit Changes
+        uses: stefanzweifel/git-auto-commit-action@v4
         with:
-          name: channels
-          path: channels/
+          commit_message: '[Bot] Formated playlists'
+          branch: bot/auto-update
   detect-resolution:
     runs-on: ubuntu-latest
     needs: format
@@ -224,8 +234,8 @@ jobs:
     steps:
       - name: Checkout
         uses: actions/checkout@v2
-      - name: Download Artifacts
-        uses: actions/download-artifact@v2
+        with:
+          ref: bot/auto-update
       - name: Install Dependencies
         run: npm install
       - name: Detect Resolution
@@ -235,14 +245,29 @@ jobs:
         with:
           name: channels
           path: channels/${{ matrix.country }}.m3u
-  generate:
+  commit-changes:
     runs-on: ubuntu-latest
     needs: detect-resolution
     steps:
       - name: Checkout
         uses: actions/checkout@v2
+        with:
+          ref: bot/auto-update
       - name: Download Artifacts
         uses: actions/download-artifact@v2
+      - name: Commit Changes
+        uses: stefanzweifel/git-auto-commit-action@v4
+        with:
+          commit_message: '[Bot] Detected resolution'
+          branch: bot/auto-update
+  generate:
+    runs-on: ubuntu-latest
+    needs: commit-changes
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+        with:
+          ref: bot/auto-update
       - name: Install Dependencies
         run: npm install
       - name: Generate Playlists
@@ -258,8 +283,8 @@ jobs:
     steps:
       - name: Checkout
         uses: actions/checkout@v2
-      - name: Download Artifacts
-        uses: actions/download-artifact@v2
+        with:
+          ref: bot/auto-update
       - name: Install Dependencies
         run: npm install
       - name: Update README.md
@@ -275,15 +300,8 @@ jobs:
     steps:
       - name: Checkout
         uses: actions/checkout@v2
-      - name: Download /channels
-        uses: actions/download-artifact@v2
         with:
-          name: channels
-          path: channels/
-      - name: Download README.md
-        uses: actions/download-artifact@v2
-        with:
-          name: README.md
+          ref: bot/auto-update
       - name: Generate Token
         uses: tibdex/github-app-token@v1
         id: generate-token
@@ -297,7 +315,6 @@ jobs:
           title: '[Bot] Update playlists'
           body: |
             This pull request is created automatically by `auto-update` action.
-          commit-message: '[Bot] Update playlists'
           committer: GitHub <noreply@github.com>
           branch: bot/auto-update
           delete-branch: true

From 0b2bd8720d51e742174166a1a034c8882503cb7d Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Mon, 2 Aug 2021 04:18:18 +0300
Subject: [PATCH 35/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index 79f12fb6f..6980b02f7 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -10,7 +10,11 @@ jobs:
       - name: Checkout
         uses: actions/checkout@v2
       - name: Create Branch
-        run: git branch bot/auto-update && git push
+        uses: peterjgrainger/action-create-branch@v2.0.1
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        with:
+          branch: 'bot/auto-update'
   remove-duplicates:
     runs-on: ubuntu-latest
     needs: create-branch

From ab62155b36d6fea62a406a79288b3ddc1ab2cb6c Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Mon, 2 Aug 2021 04:41:01 +0300
Subject: [PATCH 36/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 21 +++++++++++++--------
 1 file changed, 13 insertions(+), 8 deletions(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index 6980b02f7..1b2a506b4 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -30,8 +30,9 @@ jobs:
       - name: Commit Changes
         uses: stefanzweifel/git-auto-commit-action@v4
         with:
-          commit_message: '[Bot] Removed duplicates'
+          commit_message: '[Bot] Remove duplicates'
           branch: bot/auto-update
+          file_pattern: channels/*
   filter:
     runs-on: ubuntu-latest
     needs: remove-duplicates
@@ -47,8 +48,9 @@ jobs:
       - name: Commit Changes
         uses: stefanzweifel/git-auto-commit-action@v4
         with:
-          commit_message: '[Bot] Filtered channels'
+          commit_message: '[Bot] Filtere channels'
           branch: bot/auto-update
+          file_pattern: channels/*
   format:
     runs-on: ubuntu-latest
     needs: filter
@@ -64,8 +66,9 @@ jobs:
       - name: Commit Changes
         uses: stefanzweifel/git-auto-commit-action@v4
         with:
-          commit_message: '[Bot] Formated playlists'
+          commit_message: '[Bot] Formate playlists'
           branch: bot/auto-update
+          file_pattern: channels/*
   detect-resolution:
     runs-on: ubuntu-latest
     needs: format
@@ -262,8 +265,9 @@ jobs:
       - name: Commit Changes
         uses: stefanzweifel/git-auto-commit-action@v4
         with:
-          commit_message: '[Bot] Detected resolution'
+          commit_message: '[Bot] Detect resolution'
           branch: bot/auto-update
+          file_pattern: channels/*
   generate:
     runs-on: ubuntu-latest
     needs: commit-changes
@@ -293,11 +297,12 @@ jobs:
         run: npm install
       - name: Update README.md
         run: node scripts/update-readme.js
-      - name: Upload Artifact
-        uses: actions/upload-artifact@v2
+      - name: Commit Changes
+        uses: stefanzweifel/git-auto-commit-action@v4
         with:
-          name: README.md
-          path: README.md
+          commit_message: '[Bot] Update README.md'
+          branch: bot/auto-update
+          file_pattern: README.md
   pull-request:
     needs: update-readme
     runs-on: ubuntu-latest

From 69367ca57b9c7c6c7f9f76d1eaae866e611a1725 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Mon, 2 Aug 2021 04:52:32 +0300
Subject: [PATCH 37/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index 1b2a506b4..e335afd3f 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -31,6 +31,7 @@ jobs:
         uses: stefanzweifel/git-auto-commit-action@v4
         with:
           commit_message: '[Bot] Remove duplicates'
+          commit_author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
           branch: bot/auto-update
           file_pattern: channels/*
   filter:
@@ -49,6 +50,7 @@ jobs:
         uses: stefanzweifel/git-auto-commit-action@v4
         with:
           commit_message: '[Bot] Filtere channels'
+          commit_author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
           branch: bot/auto-update
           file_pattern: channels/*
   format:
@@ -67,6 +69,7 @@ jobs:
         uses: stefanzweifel/git-auto-commit-action@v4
         with:
           commit_message: '[Bot] Formate playlists'
+          commit_author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
           branch: bot/auto-update
           file_pattern: channels/*
   detect-resolution:
@@ -266,6 +269,7 @@ jobs:
         uses: stefanzweifel/git-auto-commit-action@v4
         with:
           commit_message: '[Bot] Detect resolution'
+          commit_author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
           branch: bot/auto-update
           file_pattern: channels/*
   generate:
@@ -301,6 +305,7 @@ jobs:
         uses: stefanzweifel/git-auto-commit-action@v4
         with:
           commit_message: '[Bot] Update README.md'
+          commit_author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
           branch: bot/auto-update
           file_pattern: README.md
   pull-request:
@@ -324,7 +329,7 @@ jobs:
           title: '[Bot] Update playlists'
           body: |
             This pull request is created automatically by `auto-update` action.
-          committer: GitHub <noreply@github.com>
+          committer: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
           branch: bot/auto-update
           delete-branch: true
           token: ${{ steps.generate-token.outputs.token }}

From 6e3f17480548b231dff948f9d28e34d8225697c5 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Mon, 2 Aug 2021 05:01:39 +0300
Subject: [PATCH 38/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index e335afd3f..f993b761f 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -31,6 +31,8 @@ jobs:
         uses: stefanzweifel/git-auto-commit-action@v4
         with:
           commit_message: '[Bot] Remove duplicates'
+          commit_user_name: iptv-bot
+          commit_user_email: 84861620+iptv-bot[bot]@users.noreply.github.com
           commit_author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
           branch: bot/auto-update
           file_pattern: channels/*
@@ -50,6 +52,8 @@ jobs:
         uses: stefanzweifel/git-auto-commit-action@v4
         with:
           commit_message: '[Bot] Filtere channels'
+          commit_user_name: iptv-bot
+          commit_user_email: 84861620+iptv-bot[bot]@users.noreply.github.com
           commit_author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
           branch: bot/auto-update
           file_pattern: channels/*
@@ -69,6 +73,8 @@ jobs:
         uses: stefanzweifel/git-auto-commit-action@v4
         with:
           commit_message: '[Bot] Formate playlists'
+          commit_user_name: iptv-bot
+          commit_user_email: 84861620+iptv-bot[bot]@users.noreply.github.com
           commit_author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
           branch: bot/auto-update
           file_pattern: channels/*
@@ -269,6 +275,8 @@ jobs:
         uses: stefanzweifel/git-auto-commit-action@v4
         with:
           commit_message: '[Bot] Detect resolution'
+          commit_user_name: iptv-bot
+          commit_user_email: 84861620+iptv-bot[bot]@users.noreply.github.com
           commit_author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
           branch: bot/auto-update
           file_pattern: channels/*
@@ -305,6 +313,8 @@ jobs:
         uses: stefanzweifel/git-auto-commit-action@v4
         with:
           commit_message: '[Bot] Update README.md'
+          commit_user_name: iptv-bot
+          commit_user_email: 84861620+iptv-bot[bot]@users.noreply.github.com
           commit_author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
           branch: bot/auto-update
           file_pattern: README.md

From 5563d7428936791d85bafdf316ab442a833b8368 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Mon, 2 Aug 2021 05:26:37 +0300
Subject: [PATCH 39/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index f993b761f..484172dc4 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -30,7 +30,7 @@ jobs:
       - name: Commit Changes
         uses: stefanzweifel/git-auto-commit-action@v4
         with:
-          commit_message: '[Bot] Remove duplicates'
+          commit_message: '[Bot] Remove duplicates\nhttps://github.com/iptv-org/iptv/runs/${{ github.run_id }}'
           commit_user_name: iptv-bot
           commit_user_email: 84861620+iptv-bot[bot]@users.noreply.github.com
           commit_author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'

From 2c12a5d087d9acb40bf21378dd3ce321b2435907 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Mon, 2 Aug 2021 05:33:35 +0300
Subject: [PATCH 40/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index 484172dc4..951fcb026 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -30,7 +30,7 @@ jobs:
       - name: Commit Changes
         uses: stefanzweifel/git-auto-commit-action@v4
         with:
-          commit_message: '[Bot] Remove duplicates\nhttps://github.com/iptv-org/iptv/runs/${{ github.run_id }}'
+          commit_message: '[Bot] Remove duplicates\nhttps://github.com/iptv-org/iptv/runs/${{ github.job }}'
           commit_user_name: iptv-bot
           commit_user_email: 84861620+iptv-bot[bot]@users.noreply.github.com
           commit_author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'

From e051c7eeb5b5b9aed98eaad069f86ea127ea0a04 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Mon, 2 Aug 2021 05:43:08 +0300
Subject: [PATCH 41/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index 951fcb026..f7e0f465c 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -30,7 +30,7 @@ jobs:
       - name: Commit Changes
         uses: stefanzweifel/git-auto-commit-action@v4
         with:
-          commit_message: '[Bot] Remove duplicates\nhttps://github.com/iptv-org/iptv/runs/${{ github.job }}'
+          commit_message: '[Bot] Remove duplicates'
           commit_user_name: iptv-bot
           commit_user_email: 84861620+iptv-bot[bot]@users.noreply.github.com
           commit_author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
@@ -339,6 +339,8 @@ jobs:
           title: '[Bot] Update playlists'
           body: |
             This pull request is created automatically by `auto-update` action.
+
+            https://github.com/iptv-org/iptv/actions/runs/${{ github.run_id }}
           committer: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
           branch: bot/auto-update
           delete-branch: true

From 0b80bb0cbc07c0a1a83fb983a56d026c6a19c68a Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Mon, 2 Aug 2021 06:00:14 +0300
Subject: [PATCH 42/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index f7e0f465c..e925c8a3c 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -9,6 +9,8 @@ jobs:
     steps:
       - name: Checkout
         uses: actions/checkout@v2
+        with:
+          ref: master
       - name: Create Branch
         uses: peterjgrainger/action-create-branch@v2.0.1
         env:

From 11d67c7a013cf427031389cee6b3b21c07f08617 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Mon, 2 Aug 2021 06:00:52 +0300
Subject: [PATCH 43/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 2 --
 1 file changed, 2 deletions(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index e925c8a3c..f7e0f465c 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -9,8 +9,6 @@ jobs:
     steps:
       - name: Checkout
         uses: actions/checkout@v2
-        with:
-          ref: master
       - name: Create Branch
         uses: peterjgrainger/action-create-branch@v2.0.1
         env:

From 430f129513d5c7a8e87ef20a5d5cc7c2e11e70d5 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Mon, 2 Aug 2021 06:06:40 +0300
Subject: [PATCH 44/95] Update CONTRIBUTING.md

---
 CONTRIBUTING.md | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 3933d33a4..c6c1e01e3 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -167,18 +167,14 @@ http://example.com/stream.m3u8
   - ...
   - `unsorted.m3u`: playlist with channels not yet sorted.
 - `scripts/`
-  - `blacklist.json`: list of channels banned for addition to the repository.
-  - `categories.json`: list of supported categories.
+  - `helpers/`: helper scripts used in GitHub Actions.
   - `clean.js`: used in GitHub Action to check all links and remove broken ones.
-  - `db.js`: contains functions for retrieving and managing the channel list.
+  - `detect-resolution.js`: used in GitHub Action to detect resolution of the streams.
   - `filter.js`: used within GitHub Action to remove blacklisted channels from playlists.
   - `format.js`: used within GitHub Action to format channel descriptions and sort playlists.
   - `generate.js`: used within GitHub Action to generate all additional playlists.
-  - `parser.js`: contains functions for parsing playlists.
-  - `regions.json`: list of supported region codes.
   - `remove-duplicates.js`: used in GitHub Action to remove duplicates from the playlist.
   - `update-readme.js`: used within GitHub Action to update the `README.md` file.
-  - `utils.js`: contains functions that are used in other scripts.
 - `CONTRIBUTING.md`: file you are currently reading.
 - `index.m3u`: main playlist that contains links to all playlists in the `channels/` folder.
 - `README.md`: project description generated from the contents of the `.readme/` folder.

From f65143e2743263484c5320e3be3a4c157b3e4ae1 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Mon, 2 Aug 2021 06:08:15 +0300
Subject: [PATCH 45/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 323 +++++++++++++++---------------
 1 file changed, 162 insertions(+), 161 deletions(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index f7e0f465c..83b5708ba 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -85,167 +85,168 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        country: [
-            # ad,
-            # ae,
-            # af,
-            # ag,
-            # al,
-            # am,
-            an
-            # ao,
-            # ar,
-            # at,
-            # au,
-            # aw,
-            # az,
-            # ba,
-            # bb,
-            # bd,
-            # be,
-            # bf,
-            # bg,
-            # bh,
-            # bn,
-            # bo,
-            # br,
-            # bs,
-            # by,
-            # ca,
-            # cd,
-            # cg,
-            # ch,
-            # ci,
-            # cl,
-            # cm,
-            # cn,
-            # co,
-            # cr,
-            # cu,
-            # cw,
-            # cy,
-            # cz,
-            # de,
-            # dk,
-            # do,
-            # dz,
-            # ec,
-            # ee,
-            # eg,
-            # es,
-            # et,
-            # fi,
-            # fj,
-            # fo,
-            # fr,
-            # ge,
-            # gh,
-            # gm,
-            # gn,
-            # gp,
-            # gq,
-            # gr,
-            # gt,
-            # hk,
-            # hn,
-            # hr,
-            # ht,
-            # hu,
-            # id,
-            # ie,
-            # il,
-            # in,
-            # iq,
-            # ir,
-            # is,
-            # it,
-            # jm,
-            # jo,
-            # jp,
-            # ke,
-            # kg,
-            # kh,
-            # kp,
-            # kr,
-            # kw,
-            # kz,
-            # la,
-            # lb,
-            # li,
-            # lk,
-            # lt,
-            # lu,
-            # lv,
-            # ly,
-            # ma,
-            # mc,
-            # md,
-            # me,
-            # mk,
-            # ml,
-            # mm,
-            # mn,
-            # mo,
-            # mt,
-            # mv,
-            # mx,
-            # my,
-            # mz,
-            # ne,
-            # ng,
-            # ni,
-            # nl,
-            # no,
-            # np,
-            # nz,
-            # om,
-            # pa,
-            # pe,
-            # ph,
-            # pk,
-            # pl,
-            # pr,
-            # ps,
-            # pt,
-            # py,
-            # qa,
-            # ro,
-            # rs,
-            # ru,
-            # rw,
-            # sa,
-            # sd,
-            # se,
-            # sg,
-            # si,
-            # sk,
-            # sl,
-            # sm,
-            # sn,
-            # so,
-            # sv,
-            # sy,
-            # th,
-            # tj,
-            # tm,
-            # tn,
-            # tr,
-            # tt,
-            # tw,
-            # tz,
-            # ua,
-            # ug,
-            # uk,
-            # us,
-            # uy,
-            # uz,
-            # va,
-            # ve,
-            # vi,
-            # vn,
-            # xk,
-            # ye,
-            # zm
+        country:
+          [
+            ad,
+            ae,
+            af,
+            ag,
+            al,
+            am,
+            an,
+            ao,
+            ar,
+            at,
+            au,
+            aw,
+            az,
+            ba,
+            bb,
+            bd,
+            be,
+            bf,
+            bg,
+            bh,
+            bn,
+            bo,
+            br,
+            bs,
+            by,
+            ca,
+            cd,
+            cg,
+            ch,
+            ci,
+            cl,
+            cm,
+            cn,
+            co,
+            cr,
+            cu,
+            cw,
+            cy,
+            cz,
+            de,
+            dk,
+            do,
+            dz,
+            ec,
+            ee,
+            eg,
+            es,
+            et,
+            fi,
+            fj,
+            fo,
+            fr,
+            ge,
+            gh,
+            gm,
+            gn,
+            gp,
+            gq,
+            gr,
+            gt,
+            hk,
+            hn,
+            hr,
+            ht,
+            hu,
+            id,
+            ie,
+            il,
+            in,
+            iq,
+            ir,
+            is,
+            it,
+            jm,
+            jo,
+            jp,
+            ke,
+            kg,
+            kh,
+            kp,
+            kr,
+            kw,
+            kz,
+            la,
+            lb,
+            li,
+            lk,
+            lt,
+            lu,
+            lv,
+            ly,
+            ma,
+            mc,
+            md,
+            me,
+            mk,
+            ml,
+            mm,
+            mn,
+            mo,
+            mt,
+            mv,
+            mx,
+            my,
+            mz,
+            ne,
+            ng,
+            ni,
+            nl,
+            no,
+            np,
+            nz,
+            om,
+            pa,
+            pe,
+            ph,
+            pk,
+            pl,
+            pr,
+            ps,
+            pt,
+            py,
+            qa,
+            ro,
+            rs,
+            ru,
+            rw,
+            sa,
+            sd,
+            se,
+            sg,
+            si,
+            sk,
+            sl,
+            sm,
+            sn,
+            so,
+            sv,
+            sy,
+            th,
+            tj,
+            tm,
+            tn,
+            tr,
+            tt,
+            tw,
+            tz,
+            ua,
+            ug,
+            uk,
+            us,
+            uy,
+            uz,
+            va,
+            ve,
+            vi,
+            vn,
+            xk,
+            ye,
+            zm
           ]
     steps:
       - name: Checkout

From 1c0554738988e4eaab41b09e8a05b1d4b389629a Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Mon, 2 Aug 2021 19:07:10 +0300
Subject: [PATCH 46/95] Updated Playlist.save() method

---
 scripts/clean.js             | 1 +
 scripts/detect-resolution.js | 1 +
 scripts/format.js            | 1 +
 scripts/helpers/Playlist.js  | 7 +++----
 scripts/remove-duplicates.js | 2 ++
 5 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/scripts/clean.js b/scripts/clean.js
index cb5ecced2..6b372cf2a 100644
--- a/scripts/clean.js
+++ b/scripts/clean.js
@@ -90,6 +90,7 @@ async function checkStatus(playlist) {
   if (playlist.channels.length !== channels.length) {
     log.print(`File '${playlist.url}' has been updated\n`)
     playlist.channels = channels
+    playlist.updated = true
   }
 
   return playlist
diff --git a/scripts/detect-resolution.js b/scripts/detect-resolution.js
index 57c33267c..5c01841f5 100644
--- a/scripts/detect-resolution.js
+++ b/scripts/detect-resolution.js
@@ -86,6 +86,7 @@ async function detectResolution(playlist) {
   if (updated) {
     log.print(`File '${playlist.url}' has been updated\n`)
     playlist.channels = channels
+    playlist.updated = true
   }
 
   return playlist
diff --git a/scripts/format.js b/scripts/format.js
index b687eed2f..0fa233f3f 100644
--- a/scripts/format.js
+++ b/scripts/format.js
@@ -26,6 +26,7 @@ async function sortChannels(playlist) {
   if (JSON.stringify(playlist.channels) !== JSON.stringify(channels)) {
     log.print('updated')
     playlist.channels = channels
+    playlist.updated = true
   }
 
   return playlist
diff --git a/scripts/helpers/Playlist.js b/scripts/helpers/Playlist.js
index 5b26b4ac2..aaaf676ff 100644
--- a/scripts/helpers/Playlist.js
+++ b/scripts/helpers/Playlist.js
@@ -10,6 +10,7 @@ module.exports = class Playlist {
     this.channels = items
       .map(item => new Channel({ data: item, header, sourceUrl: url }))
       .filter(channel => channel.url)
+    this.updated = false
   }
 
   toString(options = {}) {
@@ -31,10 +32,8 @@ module.exports = class Playlist {
   }
 
   save() {
-    const original = file.read(this.url)
-    const output = this.toString()
-    if (original !== output) {
-      file.create(this.url, output)
+    if (this.updated) {
+      file.create(this.url, this.toString())
     }
   }
 }
diff --git a/scripts/remove-duplicates.js b/scripts/remove-duplicates.js
index f2d62e919..650fbab47 100644
--- a/scripts/remove-duplicates.js
+++ b/scripts/remove-duplicates.js
@@ -51,6 +51,7 @@ async function removeDuplicates(playlist) {
   if (playlist.channels.length !== channels.length) {
     log.print('updated')
     playlist.channels = channels
+    playlist.updated = true
   }
 
   return playlist
@@ -74,6 +75,7 @@ async function removeUnsortedDuplicates(playlist) {
   if (channels.length !== playlist.channels.length) {
     log.print('updated')
     playlist.channels = channels
+    playlist.updated = true
   }
 
   return playlist

From 8dd15f08f5094f378b5ad88a4bc8dc1ab4f4e7c7 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Mon, 2 Aug 2021 20:23:49 +0300
Subject: [PATCH 47/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index 83b5708ba..42d9ee167 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -36,7 +36,7 @@ jobs:
           commit_author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
           branch: bot/auto-update
           file_pattern: channels/*
-  filter:
+  format:
     runs-on: ubuntu-latest
     needs: remove-duplicates
     steps:
@@ -46,20 +46,20 @@ jobs:
           ref: bot/auto-update
       - name: Install Dependencies
         run: npm install
-      - name: Filter Playlists
-        run: node scripts/filter.js
+      - name: Format Playlists
+        run: node scripts/format.js
       - name: Commit Changes
         uses: stefanzweifel/git-auto-commit-action@v4
         with:
-          commit_message: '[Bot] Filtere channels'
+          commit_message: '[Bot] Formate playlists'
           commit_user_name: iptv-bot
           commit_user_email: 84861620+iptv-bot[bot]@users.noreply.github.com
           commit_author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
           branch: bot/auto-update
           file_pattern: channels/*
-  format:
+  filter:
     runs-on: ubuntu-latest
-    needs: filter
+    needs: format
     steps:
       - name: Checkout
         uses: actions/checkout@v2
@@ -67,12 +67,12 @@ jobs:
           ref: bot/auto-update
       - name: Install Dependencies
         run: npm install
-      - name: Format Playlists
-        run: node scripts/format.js
+      - name: Filter Playlists
+        run: node scripts/filter.js
       - name: Commit Changes
         uses: stefanzweifel/git-auto-commit-action@v4
         with:
-          commit_message: '[Bot] Formate playlists'
+          commit_message: '[Bot] Filter channels'
           commit_user_name: iptv-bot
           commit_user_email: 84861620+iptv-bot[bot]@users.noreply.github.com
           commit_author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
@@ -80,7 +80,7 @@ jobs:
           file_pattern: channels/*
   detect-resolution:
     runs-on: ubuntu-latest
-    needs: format
+    needs: filter
     continue-on-error: true
     strategy:
       fail-fast: false

From 573174c79d732f9222549c80e6042676af70a15d Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Mon, 2 Aug 2021 21:12:27 +0300
Subject: [PATCH 48/95] Update format.js

---
 scripts/format.js | 55 +++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 48 insertions(+), 7 deletions(-)

diff --git a/scripts/format.js b/scripts/format.js
index 0fa233f3f..58f24b4f6 100644
--- a/scripts/format.js
+++ b/scripts/format.js
@@ -1,5 +1,7 @@
+const categories = require('./helpers/categories')
 const parser = require('./helpers/parser')
 const utils = require('./helpers/utils')
+const file = require('./helpers/file')
 const log = require('./helpers/log')
 
 async function main() {
@@ -11,23 +13,62 @@ async function main() {
     log.print(`\nProcessing '${playlist.url}'...`)
     await parser
       .parsePlaylist(playlist.url)
+      .then(removeWrongCategories)
+      .then(addMissingData)
       .then(sortChannels)
-      .then(p => p.save())
+      .then(playlist => {
+        if (file.read(playlist.url) !== playlist.toString()) {
+          log.print('updated')
+          playlist.updated = true
+        }
+
+        playlist.save()
+      })
   }
 
   log.print('\n')
   log.finish()
 }
 
+async function removeWrongCategories(playlist) {
+  for (const channel of playlist.channels) {
+    if (!channel.category) continue
+    const valid = categories.find(c => c.name === channel.category)
+    if (!valid) {
+      channel.category = ''
+    }
+  }
+
+  return playlist
+}
+
+async function addMissingData(playlist) {
+  for (const channel of playlist.channels) {
+    const code = file.getBasename(playlist.url)
+    // tvg-name
+    if (!channel.tvg.name && code !== 'unsorted' && channel.name) {
+      channel.tvg.name = channel.name.replace(/\"/gi, '')
+    }
+    // tvg-id
+    if (!channel.tvg.id && code !== 'unsorted' && channel.tvg.name) {
+      const id = utils.name2id(channel.tvg.name)
+      channel.tvg.id = id ? `${id}.${code}` : ''
+    }
+    // country
+    if (!channel.countries.length) {
+      const name = utils.code2name(code)
+      channel.countries = name ? [{ code, name }] : []
+      channel.tvg.country = channel.countries.map(c => c.code.toUpperCase()).join(';')
+    }
+  }
+
+  return playlist
+}
+
 async function sortChannels(playlist) {
   const channels = [...playlist.channels]
   utils.sortBy(channels, ['name', 'url'])
-
-  if (JSON.stringify(playlist.channels) !== JSON.stringify(channels)) {
-    log.print('updated')
-    playlist.channels = channels
-    playlist.updated = true
-  }
+  playlist.channels = channels
 
   return playlist
 }

From b48457fe624c5c5e53560b504bf220cc5f178564 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Mon, 2 Aug 2021 21:15:33 +0300
Subject: [PATCH 49/95] Update Playlist.js

---
 scripts/helpers/Playlist.js | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/scripts/helpers/Playlist.js b/scripts/helpers/Playlist.js
index aaaf676ff..6b797c96c 100644
--- a/scripts/helpers/Playlist.js
+++ b/scripts/helpers/Playlist.js
@@ -7,9 +7,7 @@ module.exports = class Playlist {
     this.name = name
     this.country = country
     this.header = header
-    this.channels = items
-      .map(item => new Channel({ data: item, header, sourceUrl: url }))
-      .filter(channel => channel.url)
+    this.channels = items.map(item => new Channel(item)).filter(channel => channel.url)
     this.updated = false
   }
 

From d13bbd92eb3e9852d3d94bc388c34b9a4ef2de90 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Mon, 2 Aug 2021 21:15:40 +0300
Subject: [PATCH 50/95] Update Channel.js

---
 scripts/helpers/Channel.js | 152 ++++++++++++-------------------------
 1 file changed, 48 insertions(+), 104 deletions(-)

diff --git a/scripts/helpers/Channel.js b/scripts/helpers/Channel.js
index 1dccf41ad..eef2e8a89 100644
--- a/scripts/helpers/Channel.js
+++ b/scripts/helpers/Channel.js
@@ -6,36 +6,46 @@ const sfwCategories = categories.filter(c => !c.nsfw).map(c => c.name)
 const nsfwCategories = categories.filter(c => c.nsfw).map(c => c.name)
 
 module.exports = class Channel {
-  constructor({ data, header, sourceUrl }) {
-    this.parseData(data)
-
-    this.filename = file.getBasename(sourceUrl)
-    if (!this.countries.length) {
-      const countryName = utils.code2name(this.filename)
-      this.countries = countryName ? [{ code: this.filename, name: countryName }] : []
-      this.tvg.country = this.countries.map(c => c.code.toUpperCase()).join(';')
-    }
-  }
-
-  parseData(data) {
-    const title = this.parseTitle(data.name)
-
+  constructor(data) {
+    this.raw = data.raw
     this.tvg = data.tvg
     this.http = data.http
     this.url = data.url
     this.logo = data.tvg.logo
-    this.name = title.channelName
-    this.status = title.streamStatus
-    this.resolution = title.streamResolution
+    this.category = data.group.title
+    this.name = this.parseName(data.name)
+    this.status = this.parseStatus(data.name)
+    this.resolution = this.parseResolution(data.name)
     this.countries = this.parseCountries(data.tvg.country)
     this.languages = this.parseLanguages(data.tvg.language)
-    this.category = this.parseCategory(data.group.title)
-    this.raw = data.raw
+  }
+
+  parseName(title) {
+    return title
+      .trim()
+      .split(' ')
+      .map(s => s.trim())
+      .filter(s => {
+        return !/\[|\]/i.test(s) && !/\((\d+)P\)/i.test(s)
+      })
+      .join(' ')
+  }
+
+  parseStatus(title) {
+    const match = title.match(/\[(.*)\]/i)
+    return match ? match[1] : null
+  }
+
+  parseResolution(title) {
+    const match = title.match(/\((\d+)P\)/i)
+    const height = match ? parseInt(match[1]) : null
+
+    return { width: null, height }
   }
 
   parseCountries(string) {
-    let arr = string
-      .split(';')
+    const list = string.split(';')
+    return list
       .reduce((acc, curr) => {
         const codes = utils.region2codes(curr)
         if (codes.length) {
@@ -50,95 +60,37 @@ module.exports = class Channel {
 
         return acc
       }, [])
-      .filter(code => code && utils.code2name(code))
+      .map(code => {
+        const name = code ? utils.code2name(code) : null
+        if (!name) return null
 
-    return arr.map(code => {
-      return { code: code.toLowerCase(), name: utils.code2name(code) }
-    })
+        return { code: code.toLowerCase(), name }
+      })
+      .filter(c => c)
   }
 
   parseLanguages(string) {
-    return string
-      .split(';')
+    const list = string.split(';')
+    return list
       .map(name => {
         const code = name ? utils.language2code(name) : null
         if (!code) return null
 
-        return {
-          code,
-          name
-        }
+        return { code, name }
       })
       .filter(l => l)
   }
 
-  parseCategory(string) {
-    const category = categories.find(c => c.id === string.toLowerCase())
-
-    return category ? category.name : ''
-  }
-
-  parseTitle(title) {
-    const channelName = title
-      .trim()
-      .split(' ')
-      .map(s => s.trim())
-      .filter(s => {
-        return !/\[|\]/i.test(s) && !/\((\d+)P\)/i.test(s)
-      })
-      .join(' ')
-
-    const streamStatusMatch = title.match(/\[(.*)\]/i)
-    const streamStatus = streamStatusMatch ? streamStatusMatch[1] : null
-
-    const streamResolutionMatch = title.match(/\((\d+)P\)/i)
-    const streamResolutionHeight = streamResolutionMatch ? parseInt(streamResolutionMatch[1]) : null
-    const streamResolution = { width: null, height: streamResolutionHeight }
-
-    return { channelName, streamStatus, streamResolution }
-  }
-
-  get tvgCountry() {
-    return this.tvg.country
-      .split(';')
-      .map(code => utils.code2name(code))
-      .join(';')
-  }
-
-  get tvgLanguage() {
-    return this.tvg.language
-  }
-
-  get tvgUrl() {
-    return this.tvg.id && this.tvg.url ? this.tvg.url : ''
-  }
-
-  get tvgId() {
-    if (this.tvg.id) {
-      return this.tvg.id
-    } else if (this.filename !== 'unsorted') {
-      const id = utils.name2id(this.tvgName)
-
-      return id ? `${id}.${this.filename}` : ''
-    }
-
-    return ''
+  isSFW() {
+    return sfwCategories.includes(this.category)
   }
 
-  get tvgName() {
-    if (this.tvg.name) {
-      return this.tvg.name
-    } else if (this.filename !== 'unsorted') {
-      return this.name.replace(/\"/gi, '')
-    }
-
-    return ''
+  isNSFW() {
+    return nsfwCategories.includes(this.category)
   }
 
   getInfo() {
-    this.tvg.country = this.tvg.country.toUpperCase()
-
-    let info = `-1 tvg-id="${this.tvgId}" tvg-name="${this.tvgName}" tvg-country="${this.tvg.country}" tvg-language="${this.tvg.language}" tvg-logo="${this.logo}"`
+    let info = `-1 tvg-id="${this.tvg.id}" tvg-name="${this.tvg.name}" tvg-country="${this.tvg.country}" tvg-language="${this.tvg.language}" tvg-logo="${this.logo}"`
 
     info += ` group-title="${this.category}",${this.name}`
 
@@ -176,18 +128,10 @@ module.exports = class Channel {
       languages: this.languages,
       countries: this.countries,
       tvg: {
-        id: this.tvgId || null,
-        name: this.tvgName || null,
-        url: this.tvgUrl || null
+        id: this.tvg.id || null,
+        name: this.tvg.name || null,
+        url: this.tvg.url || null
       }
     }
   }
-
-  isSFW() {
-    return sfwCategories.includes(this.category)
-  }
-
-  isNSFW() {
-    return nsfwCategories.includes(this.category)
-  }
 }

From 6c6fc992afe0c245cdae9f6b29db756d22d6484c Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Mon, 2 Aug 2021 21:18:59 +0300
Subject: [PATCH 51/95] Update filter.js

---
 scripts/filter.js | 21 ++++++++++++---------
 1 file changed, 12 insertions(+), 9 deletions(-)

diff --git a/scripts/filter.js b/scripts/filter.js
index c36bcfb11..da0d30b7f 100644
--- a/scripts/filter.js
+++ b/scripts/filter.js
@@ -1,6 +1,5 @@
 const blacklist = require('./helpers/blacklist.json')
 const parser = require('./helpers/parser')
-const utils = require('./helpers/utils')
 const log = require('./helpers/log')
 
 async function main() {
@@ -10,7 +9,10 @@ async function main() {
   const playlists = parser.parseIndex()
   for (const playlist of playlists) {
     log.print(`\nProcessing '${playlist.url}'...`)
-    await parser.parsePlaylist(playlist.url).then(removeBlacklisted).then(utils.savePlaylist)
+    await parser
+      .parsePlaylist(playlist.url)
+      .then(removeBlacklisted)
+      .then(p => p.save())
   }
 
   log.print('\n')
@@ -19,19 +21,20 @@ async function main() {
 
 async function removeBlacklisted(playlist) {
   const channels = playlist.channels.filter(channel => {
-    return !blacklist.find(i => {
-      const channelName = channel.name.toLowerCase()
-      return (
-        (i.name.toLowerCase() === channelName ||
-          i.aliases.map(i => i.toLowerCase()).includes(channelName)) &&
-        i.country === channel.filename
-      )
+    return !blacklist.find(item => {
+      const hasSameName =
+        item.name.toLowerCase() === channel.name.toLowerCase() ||
+        item.aliases.map(alias => alias.toLowerCase()).includes(channel.name.toLowerCase())
+      const fromSameCountry = channel.countries.find(c => c.code === item.country)
+
+      return hasSameName && fromSameCountry
     })
   })
 
   if (playlist.channels.length !== channels.length) {
     log.print(`updated`)
     playlist.channels = channels
+    playlist.updated = true
   }
 
   return playlist

From d9637bf1586c69b3851583122718340891f0f7d3 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Mon, 2 Aug 2021 21:34:54 +0300
Subject: [PATCH 52/95] Move sort operation to separate file

---
 .github/workflows/auto-update.yml | 23 +++++++++++++++++++-
 scripts/format.js                 |  9 --------
 scripts/sort.js                   | 35 +++++++++++++++++++++++++++++++
 3 files changed, 57 insertions(+), 10 deletions(-)
 create mode 100644 scripts/sort.js

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index 42d9ee167..4222339ce 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -57,9 +57,30 @@ jobs:
           commit_author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
           branch: bot/auto-update
           file_pattern: channels/*
-  filter:
+  sort:
     runs-on: ubuntu-latest
     needs: format
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+        with:
+          ref: bot/auto-update
+      - name: Install Dependencies
+        run: npm install
+      - name: Sort Channels
+        run: node scripts/sort.js
+      - name: Commit Changes
+        uses: stefanzweifel/git-auto-commit-action@v4
+        with:
+          commit_message: '[Bot] Sort channels'
+          commit_user_name: iptv-bot
+          commit_user_email: 84861620+iptv-bot[bot]@users.noreply.github.com
+          commit_author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
+          branch: bot/auto-update
+          file_pattern: channels/*
+  filter:
+    runs-on: ubuntu-latest
+    needs: sort
     steps:
       - name: Checkout
         uses: actions/checkout@v2
diff --git a/scripts/format.js b/scripts/format.js
index 58f24b4f6..087d90bba 100644
--- a/scripts/format.js
+++ b/scripts/format.js
@@ -15,7 +15,6 @@ async function main() {
       .parsePlaylist(playlist.url)
       .then(removeWrongCategories)
       .then(addMissingData)
-      .then(sortChannels)
       .then(playlist => {
         if (file.read(playlist.url) !== playlist.toString()) {
           log.print('updated')
@@ -65,12 +64,4 @@ async function addMissingData(playlist) {
   return playlist
 }
 
-async function sortChannels(playlist) {
-  const channels = [...playlist.channels]
-  utils.sortBy(channels, ['name', 'url'])
-  playlist.channels = channels
-
-  return playlist
-}
-
 main()
diff --git a/scripts/sort.js b/scripts/sort.js
new file mode 100644
index 000000000..974f2592c
--- /dev/null
+++ b/scripts/sort.js
@@ -0,0 +1,35 @@
+const parser = require('./helpers/parser')
+const utils = require('./helpers/utils')
+const log = require('./helpers/log')
+
+async function main() {
+  log.start()
+
+  log.print(`Parsing 'index.m3u'...`)
+  let playlists = parser.parseIndex().filter(i => i.url !== 'channels/unsorted.m3u')
+  for (const playlist of playlists) {
+    log.print(`\nProcessing '${playlist.url}'...`)
+    await parser
+      .parsePlaylist(playlist.url)
+      .then(sortChannels)
+      .then(p => p.save())
+  }
+
+  log.print('\n')
+  log.finish()
+}
+
+async function sortChannels(playlist) {
+  const channels = [...playlist.channels]
+  utils.sortBy(channels, ['name', 'url'])
+
+  if (JSON.stringify(channels) !== JSON.stringify(playlist.channels)) {
+    log.print('updated')
+    playlist.channels = channels
+    playlist.updated = true
+  }
+
+  return playlist
+}
+
+main()

From a580c76468e8977404aa2b59f75f934485fb100f Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Mon, 2 Aug 2021 21:36:48 +0300
Subject: [PATCH 53/95] Update CONTRIBUTING.md

---
 CONTRIBUTING.md | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index c6c1e01e3..a111b6713 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -171,9 +171,10 @@ http://example.com/stream.m3u8
   - `clean.js`: used in GitHub Action to check all links and remove broken ones.
   - `detect-resolution.js`: used in GitHub Action to detect resolution of the streams.
   - `filter.js`: used within GitHub Action to remove blacklisted channels from playlists.
-  - `format.js`: used within GitHub Action to format channel descriptions and sort playlists.
+  - `format.js`: used within GitHub Action to format channel descriptions.
   - `generate.js`: used within GitHub Action to generate all additional playlists.
   - `remove-duplicates.js`: used in GitHub Action to remove duplicates from the playlist.
+  - `sort.js`: used within GitHub Action to sort channels by name.
   - `update-readme.js`: used within GitHub Action to update the `README.md` file.
 - `CONTRIBUTING.md`: file you are currently reading.
 - `index.m3u`: main playlist that contains links to all playlists in the `channels/` folder.

From 3d761612d9b4f339bbf42426f31a79b305b84914 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Mon, 2 Aug 2021 21:48:04 +0300
Subject: [PATCH 54/95] Update format.js

---
 scripts/format.js | 14 --------------
 1 file changed, 14 deletions(-)

diff --git a/scripts/format.js b/scripts/format.js
index 087d90bba..095f7346a 100644
--- a/scripts/format.js
+++ b/scripts/format.js
@@ -1,4 +1,3 @@
-const categories = require('./helpers/categories')
 const parser = require('./helpers/parser')
 const utils = require('./helpers/utils')
 const file = require('./helpers/file')
@@ -13,7 +12,6 @@ async function main() {
     log.print(`\nProcessing '${playlist.url}'...`)
     await parser
       .parsePlaylist(playlist.url)
-      .then(removeWrongCategories)
       .then(addMissingData)
       .then(playlist => {
         if (file.read(playlist.url) !== playlist.toString()) {
@@ -29,18 +27,6 @@ async function main() {
   log.finish()
 }
 
-async function removeWrongCategories(playlist) {
-  for (const channel of playlist.channels) {
-    if (!channel.category) continue
-    const valid = categories.find(c => c.name === channel.category)
-    if (!valid) {
-      channel.category = ''
-    }
-  }
-
-  return playlist
-}
-
 async function addMissingData(playlist) {
   for (const channel of playlist.channels) {
     const code = file.getBasename(playlist.url)

From 6e189ad5bc87af7a2a4d1ec5f2d37eb249635786 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Mon, 2 Aug 2021 21:48:07 +0300
Subject: [PATCH 55/95] Update Channel.js

---
 scripts/helpers/Channel.js | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/scripts/helpers/Channel.js b/scripts/helpers/Channel.js
index eef2e8a89..71f3d4d1a 100644
--- a/scripts/helpers/Channel.js
+++ b/scripts/helpers/Channel.js
@@ -12,10 +12,11 @@ module.exports = class Channel {
     this.http = data.http
     this.url = data.url
     this.logo = data.tvg.logo
-    this.category = data.group.title
+    this.group = data.group
     this.name = this.parseName(data.name)
     this.status = this.parseStatus(data.name)
     this.resolution = this.parseResolution(data.name)
+    this.category = this.parseCategory(data.group.title)
     this.countries = this.parseCountries(data.tvg.country)
     this.languages = this.parseLanguages(data.tvg.language)
   }
@@ -43,6 +44,13 @@ module.exports = class Channel {
     return { width: null, height }
   }
 
+  parseCategory(string) {
+    const category = categories.find(c => c.id === string.toLowerCase())
+    if (!category) return ''
+
+    return category.name
+  }
+
   parseCountries(string) {
     const list = string.split(';')
     return list
@@ -92,7 +100,7 @@ module.exports = class Channel {
   getInfo() {
     let info = `-1 tvg-id="${this.tvg.id}" tvg-name="${this.tvg.name}" tvg-country="${this.tvg.country}" tvg-language="${this.tvg.language}" tvg-logo="${this.logo}"`
 
-    info += ` group-title="${this.category}",${this.name}`
+    info += ` group-title="${this.group.title}",${this.name}`
 
     if (this.resolution.height) {
       info += ` (${this.resolution.height}p)`

From a02e3db46a5b458c932c8555346a1db6b9b19a05 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Mon, 2 Aug 2021 21:55:58 +0300
Subject: [PATCH 56/95] Update format.js

---
 scripts/format.js | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/scripts/format.js b/scripts/format.js
index 095f7346a..fb77cab20 100644
--- a/scripts/format.js
+++ b/scripts/format.js
@@ -12,7 +12,7 @@ async function main() {
     log.print(`\nProcessing '${playlist.url}'...`)
     await parser
       .parsePlaylist(playlist.url)
-      .then(addMissingData)
+      .then(formatPlaylist)
       .then(playlist => {
         if (file.read(playlist.url) !== playlist.toString()) {
           log.print('updated')
@@ -27,24 +27,26 @@ async function main() {
   log.finish()
 }
 
-async function addMissingData(playlist) {
+async function formatPlaylist(playlist) {
   for (const channel of playlist.channels) {
     const code = file.getBasename(playlist.url)
-    // tvg-name
+    // add missing tvg-name
     if (!channel.tvg.name && code !== 'unsorted' && channel.name) {
       channel.tvg.name = channel.name.replace(/\"/gi, '')
     }
-    // tvg-id
+    // add missing tvg-id
     if (!channel.tvg.id && code !== 'unsorted' && channel.tvg.name) {
       const id = utils.name2id(channel.tvg.name)
       channel.tvg.id = id ? `${id}.${code}` : ''
     }
-    // country
+    // add missing country
     if (!channel.countries.length) {
       const name = utils.code2name(code)
       channel.countries = name ? [{ code, name }] : []
       channel.tvg.country = channel.countries.map(c => c.code.toUpperCase()).join(';')
     }
+    // update group-title
+    channel.group.title = channel.category
   }
 
   return playlist

From 7bd78d7bc04a75bc9f9bf885cf5a88493831e63c Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Mon, 2 Aug 2021 22:02:53 +0300
Subject: [PATCH 57/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index 4222339ce..52497dd15 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -15,7 +15,7 @@ jobs:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
         with:
           branch: 'bot/auto-update'
-  remove-duplicates:
+  format:
     runs-on: ubuntu-latest
     needs: create-branch
     steps:
@@ -25,20 +25,20 @@ jobs:
           ref: bot/auto-update
       - name: Install Dependencies
         run: npm install
-      - name: Remove Duplicates
-        run: node scripts/remove-duplicates.js
+      - name: Format Playlists
+        run: node scripts/format.js
       - name: Commit Changes
         uses: stefanzweifel/git-auto-commit-action@v4
         with:
-          commit_message: '[Bot] Remove duplicates'
+          commit_message: '[Bot] Formate playlists'
           commit_user_name: iptv-bot
           commit_user_email: 84861620+iptv-bot[bot]@users.noreply.github.com
           commit_author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
           branch: bot/auto-update
           file_pattern: channels/*
-  format:
+  remove-duplicates:
     runs-on: ubuntu-latest
-    needs: remove-duplicates
+    needs: format
     steps:
       - name: Checkout
         uses: actions/checkout@v2
@@ -46,12 +46,12 @@ jobs:
           ref: bot/auto-update
       - name: Install Dependencies
         run: npm install
-      - name: Format Playlists
-        run: node scripts/format.js
+      - name: Remove Duplicates
+        run: node scripts/remove-duplicates.js
       - name: Commit Changes
         uses: stefanzweifel/git-auto-commit-action@v4
         with:
-          commit_message: '[Bot] Formate playlists'
+          commit_message: '[Bot] Remove duplicates'
           commit_user_name: iptv-bot
           commit_user_email: 84861620+iptv-bot[bot]@users.noreply.github.com
           commit_author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
@@ -59,7 +59,7 @@ jobs:
           file_pattern: channels/*
   sort:
     runs-on: ubuntu-latest
-    needs: format
+    needs: remove-duplicates
     steps:
       - name: Checkout
         uses: actions/checkout@v2

From 06b204c4e8361b1707ab6dd2763e4e9d6213ea40 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 02:30:26 +0300
Subject: [PATCH 58/95] Update generate.js

---
 scripts/generate.js | 26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/scripts/generate.js b/scripts/generate.js
index 459497894..3c96c6d7c 100644
--- a/scripts/generate.js
+++ b/scripts/generate.js
@@ -8,15 +8,15 @@ async function main() {
   await loadDatabase()
   createRootDirectory()
   createNoJekyllFile()
-  generateIndex()
-  generateCategoryIndex()
+  // generateIndex()
+  // generateCategoryIndex()
   generateCountryIndex()
   generateLanguageIndex()
-  generateCategories()
-  generateCountries()
-  generateLanguages()
-  generateChannelsJson()
-  showResults()
+  // generateCategories()
+  // generateCountries()
+  // generateLanguages()
+  // generateChannelsJson()
+  // showResults()
 }
 
 async function loadDatabase() {
@@ -74,13 +74,13 @@ function generateCountryIndex() {
       .removeDuplicates()
       .get()
     for (const channel of channels) {
-      const category = channel.category
+      const groupTitle = channel.group.title
       const nsfw = channel.isNSFW()
-      channel.category = country.name || ''
+      channel.group.title = country.name || ''
       if (!nsfw) {
         file.append(filename, channel.toString())
       }
-      channel.category = category
+      channel.group.title = groupTitle
     }
   }
 }
@@ -97,13 +97,13 @@ function generateLanguageIndex() {
       .removeDuplicates()
       .get()
     for (const channel of channels) {
-      const category = channel.category
+      const groupTitle = channel.group.title
       const nsfw = channel.isNSFW()
-      channel.category = language.name || ''
+      channel.group.title = language.name || ''
       if (!nsfw) {
         file.append(filename, channel.toString())
       }
-      channel.category = category
+      channel.group.title = groupTitle
     }
   }
 }

From 93aa4cd31fd0faf2d4a3fac07b668dd2090c5a42 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 02:44:05 +0300
Subject: [PATCH 59/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index 52497dd15..a87e88854 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -310,6 +310,12 @@ jobs:
         uses: actions/checkout@v2
         with:
           ref: bot/auto-update
+      - name: Generate Token
+        uses: tibdex/github-app-token@v1
+        id: gh-pages-token
+        with:
+          app_id: ${{ secrets.APP_ID }}
+          private_key: ${{ secrets.APP_PRIVATE_KEY }}
       - name: Install Dependencies
         run: npm install
       - name: Generate Playlists
@@ -319,6 +325,10 @@ jobs:
         with:
           branch: gh-pages
           folder: .gh-pages
+          token: ${{ steps.gh-pages-token.outputs.token }}
+          git-config-name: iptv-bot
+          git-config-email: 84861620+iptv-bot[bot]@users.noreply.github.com
+          commit-message: '[Bot] Deploy to GitHub Pages'
   update-readme:
     runs-on: ubuntu-latest
     needs: generate
@@ -350,7 +360,7 @@ jobs:
           ref: bot/auto-update
       - name: Generate Token
         uses: tibdex/github-app-token@v1
-        id: generate-token
+        id: pr-token
         with:
           app_id: ${{ secrets.APP_ID }}
           private_key: ${{ secrets.APP_PRIVATE_KEY }}
@@ -366,7 +376,7 @@ jobs:
           committer: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
           branch: bot/auto-update
           delete-branch: true
-          token: ${{ steps.generate-token.outputs.token }}
+          token: ${{ steps.pr-token.outputs.token }}
       - name: Enable Pull Request Automerge
         if: steps.pr.outputs.pull-request-operation == 'created'
         uses: peter-evans/enable-pull-request-automerge@v1

From b0fcb71a58b8166bc29dedc030be4bffd1e9e5a8 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 03:23:24 +0300
Subject: [PATCH 60/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 40 +++++++++++++++++++++++--------
 1 file changed, 30 insertions(+), 10 deletions(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index a87e88854..49423e3d2 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -293,6 +293,8 @@ jobs:
           ref: bot/auto-update
       - name: Download Artifacts
         uses: actions/download-artifact@v2
+        with:
+          name: channels
       - name: Commit Changes
         uses: stefanzweifel/git-auto-commit-action@v4
         with:
@@ -310,22 +312,40 @@ jobs:
         uses: actions/checkout@v2
         with:
           ref: bot/auto-update
+      - name: Install Dependencies
+        run: npm install
+      - name: Generate Playlists
+        run: node scripts/generate.js
+      - name: Upload Artifact
+        uses: actions/upload-artifact@v2
+        with:
+          name: gh-pages
+          path: .gh-pages/
+  deploy:
+    if: ${{ github.ref == 'refs/heads/master' }}
+    runs-on: ubuntu-latest
+    needs: generate
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+        with:
+          ref: bot/auto-update
+      - name: Download Artifacts
+        uses: actions/download-artifact@v2
+        with:
+          name: gh-pages
       - name: Generate Token
         uses: tibdex/github-app-token@v1
-        id: gh-pages-token
+        id: generate-token
         with:
           app_id: ${{ secrets.APP_ID }}
           private_key: ${{ secrets.APP_PRIVATE_KEY }}
-      - name: Install Dependencies
-        run: npm install
-      - name: Generate Playlists
-        run: node scripts/generate.js
       - name: Deploy to GitHub Pages
         uses: JamesIves/github-pages-deploy-action@4.1.1
         with:
           branch: gh-pages
           folder: .gh-pages
-          token: ${{ steps.gh-pages-token.outputs.token }}
+          token: ${{ steps.generate-token.outputs.token }}
           git-config-name: iptv-bot
           git-config-email: 84861620+iptv-bot[bot]@users.noreply.github.com
           commit-message: '[Bot] Deploy to GitHub Pages'
@@ -360,7 +380,7 @@ jobs:
           ref: bot/auto-update
       - name: Generate Token
         uses: tibdex/github-app-token@v1
-        id: pr-token
+        id: generate-token
         with:
           app_id: ${{ secrets.APP_ID }}
           private_key: ${{ secrets.APP_PRIVATE_KEY }}
@@ -374,18 +394,18 @@ jobs:
 
             https://github.com/iptv-org/iptv/actions/runs/${{ github.run_id }}
           committer: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
-          branch: bot/auto-update
+          branch: bot/auto-pull-request
           delete-branch: true
           token: ${{ steps.pr-token.outputs.token }}
       - name: Enable Pull Request Automerge
-        if: steps.pr.outputs.pull-request-operation == 'created'
+        if: steps.generate-token.outputs.pull-request-operation == 'created'
         uses: peter-evans/enable-pull-request-automerge@v1
         with:
           token: ${{ secrets.PAT }}
           pull-request-number: ${{ steps.pr.outputs.pull-request-number }}
           merge-method: squash
       - name: Approve Pull Request
-        if: steps.pr.outputs.pull-request-operation == 'created'
+        if: github.ref == 'refs/heads/master' && steps.pr.outputs.pull-request-operation == 'created'
         uses: juliangruber/approve-pull-request-action@v1
         with:
           github-token: ${{ secrets.PAT }}

From 9f3f8aa2ee1217b8ed01d515b65da888c432d667 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 03:24:20 +0300
Subject: [PATCH 61/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 323 +++++++++++++++---------------
 1 file changed, 161 insertions(+), 162 deletions(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index 49423e3d2..0ed783cd1 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -106,168 +106,167 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        country:
-          [
-            ad,
-            ae,
-            af,
-            ag,
-            al,
-            am,
-            an,
-            ao,
-            ar,
-            at,
-            au,
-            aw,
-            az,
-            ba,
-            bb,
-            bd,
-            be,
-            bf,
-            bg,
-            bh,
-            bn,
-            bo,
-            br,
-            bs,
-            by,
-            ca,
-            cd,
-            cg,
-            ch,
-            ci,
-            cl,
-            cm,
-            cn,
-            co,
-            cr,
-            cu,
-            cw,
-            cy,
-            cz,
-            de,
-            dk,
-            do,
-            dz,
-            ec,
-            ee,
-            eg,
-            es,
-            et,
-            fi,
-            fj,
-            fo,
-            fr,
-            ge,
-            gh,
-            gm,
-            gn,
-            gp,
-            gq,
-            gr,
-            gt,
-            hk,
-            hn,
-            hr,
-            ht,
-            hu,
-            id,
-            ie,
-            il,
-            in,
-            iq,
-            ir,
-            is,
-            it,
-            jm,
-            jo,
-            jp,
-            ke,
-            kg,
-            kh,
-            kp,
-            kr,
-            kw,
-            kz,
-            la,
-            lb,
-            li,
-            lk,
-            lt,
-            lu,
-            lv,
-            ly,
-            ma,
-            mc,
-            md,
-            me,
-            mk,
-            ml,
-            mm,
-            mn,
-            mo,
-            mt,
-            mv,
-            mx,
-            my,
-            mz,
-            ne,
-            ng,
-            ni,
-            nl,
-            no,
-            np,
-            nz,
-            om,
-            pa,
-            pe,
-            ph,
-            pk,
-            pl,
-            pr,
-            ps,
-            pt,
-            py,
-            qa,
-            ro,
-            rs,
-            ru,
-            rw,
-            sa,
-            sd,
-            se,
-            sg,
-            si,
-            sk,
-            sl,
-            sm,
-            sn,
-            so,
-            sv,
-            sy,
-            th,
-            tj,
-            tm,
-            tn,
-            tr,
-            tt,
-            tw,
-            tz,
-            ua,
-            ug,
-            uk,
-            us,
-            uy,
-            uz,
-            va,
-            ve,
-            vi,
-            vn,
-            xk,
-            ye,
-            zm
+        country: [
+            # ad,
+            # ae,
+            # af,
+            # ag,
+            # al,
+            # am,
+            an
+            # ao,
+            # ar,
+            # at,
+            # au,
+            # aw,
+            # az,
+            # ba,
+            # bb,
+            # bd,
+            # be,
+            # bf,
+            # bg,
+            # bh,
+            # bn,
+            # bo,
+            # br,
+            # bs,
+            # by,
+            # ca,
+            # cd,
+            # cg,
+            # ch,
+            # ci,
+            # cl,
+            # cm,
+            # cn,
+            # co,
+            # cr,
+            # cu,
+            # cw,
+            # cy,
+            # cz,
+            # de,
+            # dk,
+            # do,
+            # dz,
+            # ec,
+            # ee,
+            # eg,
+            # es,
+            # et,
+            # fi,
+            # fj,
+            # fo,
+            # fr,
+            # ge,
+            # gh,
+            # gm,
+            # gn,
+            # gp,
+            # gq,
+            # gr,
+            # gt,
+            # hk,
+            # hn,
+            # hr,
+            # ht,
+            # hu,
+            # id,
+            # ie,
+            # il,
+            # in,
+            # iq,
+            # ir,
+            # is,
+            # it,
+            # jm,
+            # jo,
+            # jp,
+            # ke,
+            # kg,
+            # kh,
+            # kp,
+            # kr,
+            # kw,
+            # kz,
+            # la,
+            # lb,
+            # li,
+            # lk,
+            # lt,
+            # lu,
+            # lv,
+            # ly,
+            # ma,
+            # mc,
+            # md,
+            # me,
+            # mk,
+            # ml,
+            # mm,
+            # mn,
+            # mo,
+            # mt,
+            # mv,
+            # mx,
+            # my,
+            # mz,
+            # ne,
+            # ng,
+            # ni,
+            # nl,
+            # no,
+            # np,
+            # nz,
+            # om,
+            # pa,
+            # pe,
+            # ph,
+            # pk,
+            # pl,
+            # pr,
+            # ps,
+            # pt,
+            # py,
+            # qa,
+            # ro,
+            # rs,
+            # ru,
+            # rw,
+            # sa,
+            # sd,
+            # se,
+            # sg,
+            # si,
+            # sk,
+            # sl,
+            # sm,
+            # sn,
+            # so,
+            # sv,
+            # sy,
+            # th,
+            # tj,
+            # tm,
+            # tn,
+            # tr,
+            # tt,
+            # tw,
+            # tz,
+            # ua,
+            # ug,
+            # uk,
+            # us,
+            # uy,
+            # uz,
+            # va,
+            # ve,
+            # vi,
+            # vn,
+            # xk,
+            # ye,
+            # zm
           ]
     steps:
       - name: Checkout

From 97cc28f5feceb7b1a67360fca50eb901660c714c Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 03:39:16 +0300
Subject: [PATCH 62/95] Update generate.js

---
 scripts/generate.js | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/scripts/generate.js b/scripts/generate.js
index 3c96c6d7c..6aa31c224 100644
--- a/scripts/generate.js
+++ b/scripts/generate.js
@@ -8,15 +8,15 @@ async function main() {
   await loadDatabase()
   createRootDirectory()
   createNoJekyllFile()
-  // generateIndex()
-  // generateCategoryIndex()
+  generateIndex()
+  generateCategoryIndex()
   generateCountryIndex()
   generateLanguageIndex()
-  // generateCategories()
-  // generateCountries()
-  // generateLanguages()
-  // generateChannelsJson()
-  // showResults()
+  generateCategories()
+  generateCountries()
+  generateLanguages()
+  generateChannelsJson()
+  showResults()
 }
 
 async function loadDatabase() {

From 1cae681686e6ad338dcac3239a4691d5dee93a8b Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 03:55:23 +0300
Subject: [PATCH 63/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index 0ed783cd1..51565ffd5 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -343,7 +343,7 @@ jobs:
         uses: JamesIves/github-pages-deploy-action@4.1.1
         with:
           branch: gh-pages
-          folder: .gh-pages
+          folder: gh-pages
           token: ${{ steps.generate-token.outputs.token }}
           git-config-name: iptv-bot
           git-config-email: 84861620+iptv-bot[bot]@users.noreply.github.com
@@ -392,7 +392,7 @@ jobs:
             This pull request is created automatically by `auto-update` action.
 
             https://github.com/iptv-org/iptv/actions/runs/${{ github.run_id }}
-          committer: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
+          author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
           branch: bot/auto-pull-request
           delete-branch: true
           token: ${{ steps.pr-token.outputs.token }}

From 64bd7fd46b5437040c25f84ab8ae27f1c0019738 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 04:01:16 +0300
Subject: [PATCH 64/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index 51565ffd5..eddc7fdb5 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -395,7 +395,7 @@ jobs:
           author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
           branch: bot/auto-pull-request
           delete-branch: true
-          token: ${{ steps.pr-token.outputs.token }}
+          token: ${{ steps.generate-token.outputs.token }}
       - name: Enable Pull Request Automerge
         if: steps.generate-token.outputs.pull-request-operation == 'created'
         uses: peter-evans/enable-pull-request-automerge@v1

From c97e9c334fcee7ce6e726252559e9d1a51912d5a Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 04:30:15 +0300
Subject: [PATCH 65/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index eddc7fdb5..62ecd92e2 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -389,15 +389,14 @@ jobs:
         with:
           title: '[Bot] Update playlists'
           body: |
-            This pull request is created automatically by `auto-update` action.
+            This pull request is created by [auto-update][1] workflow.
 
-            https://github.com/iptv-org/iptv/actions/runs/${{ github.run_id }}
+            [1]: https://github.com/iptv-org/iptv/actions/runs/${{ github.run_id }}
           author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
-          branch: bot/auto-pull-request
           delete-branch: true
           token: ${{ steps.generate-token.outputs.token }}
       - name: Enable Pull Request Automerge
-        if: steps.generate-token.outputs.pull-request-operation == 'created'
+        if: steps.pr.outputs.pull-request-operation == 'created'
         uses: peter-evans/enable-pull-request-automerge@v1
         with:
           token: ${{ secrets.PAT }}

From 3db437354497790d339faaf0bff3c55986731e3b Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 04:35:02 +0300
Subject: [PATCH 66/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index 62ecd92e2..d9f0c3672 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -408,3 +408,8 @@ jobs:
         with:
           github-token: ${{ secrets.PAT }}
           number: ${{ steps.pr.outputs.pull-request-number }}
+      - name: Delete Branch
+        uses: dawidd6/action-delete-branch@v3
+        with:
+          github_token: ${{ secrets.GITHUB_TOKEN }}
+          branches: 'bot/auto-update'

From 05fbaf9634b03ba6f78f0d7d04c88d804dd4f164 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 04:42:23 +0300
Subject: [PATCH 67/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index d9f0c3672..5f46a9e06 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -408,6 +408,12 @@ jobs:
         with:
           github-token: ${{ secrets.PAT }}
           number: ${{ steps.pr.outputs.pull-request-number }}
+  delete-branch:
+    runs-on: ubuntu-latest
+    needs: pull-request
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
       - name: Delete Branch
         uses: dawidd6/action-delete-branch@v3
         with:

From dba2872f434bb868750957cb6ac6d3f185c88cdd Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 04:49:45 +0300
Subject: [PATCH 68/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index 5f46a9e06..f34b61dde 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -394,6 +394,7 @@ jobs:
             [1]: https://github.com/iptv-org/iptv/actions/runs/${{ github.run_id }}
           author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
           delete-branch: true
+          base: master
           token: ${{ steps.generate-token.outputs.token }}
       - name: Enable Pull Request Automerge
         if: steps.pr.outputs.pull-request-operation == 'created'

From 9d5862cfa190013959171edfbe1564aadcd4ca35 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 05:11:48 +0300
Subject: [PATCH 69/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index f34b61dde..c53e27680 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -393,6 +393,7 @@ jobs:
 
             [1]: https://github.com/iptv-org/iptv/actions/runs/${{ github.run_id }}
           author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
+          branch: bot/auto-update
           delete-branch: true
           base: master
           token: ${{ steps.generate-token.outputs.token }}

From 93e8bec1afd493528cfe653837571c4afb3c405e Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 05:23:20 +0300
Subject: [PATCH 70/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index c53e27680..d64d73b4a 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -375,8 +375,6 @@ jobs:
     steps:
       - name: Checkout
         uses: actions/checkout@v2
-        with:
-          ref: bot/auto-update
       - name: Generate Token
         uses: tibdex/github-app-token@v1
         id: generate-token
@@ -393,9 +391,8 @@ jobs:
 
             [1]: https://github.com/iptv-org/iptv/actions/runs/${{ github.run_id }}
           author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
-          branch: bot/auto-update
           delete-branch: true
-          base: master
+          base: bot/auto-update
           token: ${{ steps.generate-token.outputs.token }}
       - name: Enable Pull Request Automerge
         if: steps.pr.outputs.pull-request-operation == 'created'

From 08f885b6eb3faa07276db4576b0de4cbe49403e8 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 05:34:41 +0300
Subject: [PATCH 71/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index d64d73b4a..f2bc2ccab 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -375,6 +375,8 @@ jobs:
     steps:
       - name: Checkout
         uses: actions/checkout@v2
+        with:
+          ref: master
       - name: Generate Token
         uses: tibdex/github-app-token@v1
         id: generate-token

From f7e716301a74df0cdb5c50451b42a1c30a6fa1d7 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 05:45:07 +0300
Subject: [PATCH 72/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index f2bc2ccab..ada40de73 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -393,8 +393,8 @@ jobs:
 
             [1]: https://github.com/iptv-org/iptv/actions/runs/${{ github.run_id }}
           author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
+          branch: bot/auto-update
           delete-branch: true
-          base: bot/auto-update
           token: ${{ steps.generate-token.outputs.token }}
       - name: Enable Pull Request Automerge
         if: steps.pr.outputs.pull-request-operation == 'created'

From 16bbeab12cae690235bcf703ee79c926aaac9ab6 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 05:45:51 +0300
Subject: [PATCH 73/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 11 -----------
 1 file changed, 11 deletions(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index ada40de73..7ce3811f8 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -409,14 +409,3 @@ jobs:
         with:
           github-token: ${{ secrets.PAT }}
           number: ${{ steps.pr.outputs.pull-request-number }}
-  delete-branch:
-    runs-on: ubuntu-latest
-    needs: pull-request
-    steps:
-      - name: Checkout
-        uses: actions/checkout@v2
-      - name: Delete Branch
-        uses: dawidd6/action-delete-branch@v3
-        with:
-          github_token: ${{ secrets.GITHUB_TOKEN }}
-          branches: 'bot/auto-update'

From b5561a78d9e48dfa64a3328da139d92663045c98 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 05:58:44 +0300
Subject: [PATCH 74/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index 7ce3811f8..d4bc28679 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -376,7 +376,7 @@ jobs:
       - name: Checkout
         uses: actions/checkout@v2
         with:
-          ref: master
+          fetch-depth: 0
       - name: Generate Token
         uses: tibdex/github-app-token@v1
         id: generate-token
@@ -394,7 +394,8 @@ jobs:
             [1]: https://github.com/iptv-org/iptv/actions/runs/${{ github.run_id }}
           author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
           branch: bot/auto-update
-          delete-branch: true
+          delete-branch: false
+          base: master
           token: ${{ steps.generate-token.outputs.token }}
       - name: Enable Pull Request Automerge
         if: steps.pr.outputs.pull-request-operation == 'created'

From 03d15db8a9a6464e6d1d04120faa91f72c70ca77 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 06:10:00 +0300
Subject: [PATCH 75/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index d4bc28679..83daf3a77 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -376,6 +376,7 @@ jobs:
       - name: Checkout
         uses: actions/checkout@v2
         with:
+          ref: master
           fetch-depth: 0
       - name: Generate Token
         uses: tibdex/github-app-token@v1
@@ -395,7 +396,6 @@ jobs:
           author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
           branch: bot/auto-update
           delete-branch: false
-          base: master
           token: ${{ steps.generate-token.outputs.token }}
       - name: Enable Pull Request Automerge
         if: steps.pr.outputs.pull-request-operation == 'created'

From d5fdfb3afbb795a6d19e912094454f6dd1e96ed7 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 06:20:11 +0300
Subject: [PATCH 76/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index 83daf3a77..5afdc9a9e 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -376,8 +376,7 @@ jobs:
       - name: Checkout
         uses: actions/checkout@v2
         with:
-          ref: master
-          fetch-depth: 0
+          ref: bot/auto-update
       - name: Generate Token
         uses: tibdex/github-app-token@v1
         id: generate-token
@@ -394,8 +393,8 @@ jobs:
 
             [1]: https://github.com/iptv-org/iptv/actions/runs/${{ github.run_id }}
           author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
-          branch: bot/auto-update
-          delete-branch: false
+          delete-branch: true
+          base: origin/master
           token: ${{ steps.generate-token.outputs.token }}
       - name: Enable Pull Request Automerge
         if: steps.pr.outputs.pull-request-operation == 'created'

From e792dc67c26b473155cf2a882f37f02d18425222 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 06:25:03 +0300
Subject: [PATCH 77/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index 5afdc9a9e..07b68886d 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -394,7 +394,7 @@ jobs:
             [1]: https://github.com/iptv-org/iptv/actions/runs/${{ github.run_id }}
           author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
           delete-branch: true
-          base: origin/master
+          base: master
           token: ${{ steps.generate-token.outputs.token }}
       - name: Enable Pull Request Automerge
         if: steps.pr.outputs.pull-request-operation == 'created'

From 6cbca8c98522752bf5616522c6e49938c5af7dc6 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 06:43:08 +0300
Subject: [PATCH 78/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index 07b68886d..a68fac664 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -393,7 +393,8 @@ jobs:
 
             [1]: https://github.com/iptv-org/iptv/actions/runs/${{ github.run_id }}
           author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
-          delete-branch: true
+          branch: 'bot/auto-update'
+          delete-branch: false
           base: master
           token: ${{ steps.generate-token.outputs.token }}
       - name: Enable Pull Request Automerge

From 30b211bc5bdd6bc7b5656c9793692bc592e60d12 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 06:59:20 +0300
Subject: [PATCH 79/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index a68fac664..8f1df1173 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -383,6 +383,8 @@ jobs:
         with:
           app_id: ${{ secrets.APP_ID }}
           private_key: ${{ secrets.APP_PRIVATE_KEY }}
+      - name: Update from master
+        run: git merge origin/master
       - name: Create Pull Request
         id: pr
         uses: peter-evans/create-pull-request@v3

From a56479f4f7d84244619dc5e2d4d2cde785320cde Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 07:13:03 +0300
Subject: [PATCH 80/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index 8f1df1173..d29e771a8 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -9,12 +9,10 @@ jobs:
     steps:
       - name: Checkout
         uses: actions/checkout@v2
-      - name: Create Branch
-        uses: peterjgrainger/action-create-branch@v2.0.1
-        env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
         with:
-          branch: 'bot/auto-update'
+          ref: bot/auto-update
+      - name: Push Branch
+        run: git push -u origin bot/auto-update
   format:
     runs-on: ubuntu-latest
     needs: create-branch

From cf57b3fe1d4da004ecb0c3e0785eaab75a6cad8d Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 07:20:34 +0300
Subject: [PATCH 81/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index d29e771a8..ca8e80203 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -10,9 +10,13 @@ jobs:
       - name: Checkout
         uses: actions/checkout@v2
         with:
-          ref: bot/auto-update
-      - name: Push Branch
-        run: git push -u origin bot/auto-update
+          ref: patch-2
+      - name: Create Branch
+        uses: peterjgrainger/action-create-branch@v2.0.1
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        with:
+          branch: 'bot/auto-update'
   format:
     runs-on: ubuntu-latest
     needs: create-branch

From c4908737adceeacb564efd0e486b2b7e76fb6cbe Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 07:28:52 +0300
Subject: [PATCH 82/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index ca8e80203..046b698f4 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -386,7 +386,7 @@ jobs:
           app_id: ${{ secrets.APP_ID }}
           private_key: ${{ secrets.APP_PRIVATE_KEY }}
       - name: Update from master
-        run: git merge origin/master
+        run: git fetch && git merge origin/master
       - name: Create Pull Request
         id: pr
         uses: peter-evans/create-pull-request@v3

From 831179989afb3498576dd2d63f7e20276d812f9d Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 07:30:37 +0300
Subject: [PATCH 83/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index 046b698f4..b31cdd05b 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -10,7 +10,7 @@ jobs:
       - name: Checkout
         uses: actions/checkout@v2
         with:
-          ref: patch-2
+          ref: ${{ github.ref }}
       - name: Create Branch
         uses: peterjgrainger/action-create-branch@v2.0.1
         env:

From 1280fb776936a99e13b22d6647236b143fbe844c Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 07:45:05 +0300
Subject: [PATCH 84/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index b31cdd05b..19eeeafdb 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -385,8 +385,10 @@ jobs:
         with:
           app_id: ${{ secrets.APP_ID }}
           private_key: ${{ secrets.APP_PRIVATE_KEY }}
+      - name: Fetch Origin
+        run: git fetch origin
       - name: Update from master
-        run: git fetch && git merge origin/master
+        run: git merge origin/master
       - name: Create Pull Request
         id: pr
         uses: peter-evans/create-pull-request@v3

From d4ee156d61fdc5988b894fa69039bdec1b0d7a3f Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 07:52:55 +0300
Subject: [PATCH 85/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index 19eeeafdb..680564674 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -385,10 +385,6 @@ jobs:
         with:
           app_id: ${{ secrets.APP_ID }}
           private_key: ${{ secrets.APP_PRIVATE_KEY }}
-      - name: Fetch Origin
-        run: git fetch origin
-      - name: Update from master
-        run: git merge origin/master
       - name: Create Pull Request
         id: pr
         uses: peter-evans/create-pull-request@v3

From ff9f80a97d95c73657df577f5935a945dfd930b6 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 08:04:45 +0300
Subject: [PATCH 86/95] Create test.yml

---
 .github/workflows/test.yml | 70 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 70 insertions(+)
 create mode 100644 .github/workflows/test.yml

diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 000000000..20443dd36
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,70 @@
+name: test
+on:
+  workflow_dispatch:
+jobs:
+  create-branch:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+        with:
+          ref: ${{ github.ref }}
+      - name: Create Branch
+        uses: peterjgrainger/action-create-branch@v2.0.1
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        with:
+          branch: 'bot/auto-update'
+  sort:
+    runs-on: ubuntu-latest
+    needs: create-branch
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+        with:
+          ref: bot/auto-update
+      - name: Install Dependencies
+        run: npm install
+      - name: Sort Channels
+        run: node scripts/sort.js
+      - name: Commit Changes
+        uses: stefanzweifel/git-auto-commit-action@v4
+        with:
+          commit_message: '[Bot] Sort channels'
+          commit_user_name: iptv-bot
+          commit_user_email: 84861620+iptv-bot[bot]@users.noreply.github.com
+          commit_author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
+          branch: bot/auto-update
+          file_pattern: channels/*
+  pull-request:
+    needs: sort
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+        with:
+          ref: bot/auto-update
+      - name: Generate Token
+        uses: tibdex/github-app-token@v1
+        id: generate-token
+        with:
+          app_id: ${{ secrets.APP_ID }}
+          private_key: ${{ secrets.APP_PRIVATE_KEY }}
+      - name: Fetch Origin
+        run: git fetch origin --allow-unrelated-histories
+      - name: Update from master
+        run: git merge origin/master
+      - name: Create Pull Request
+        id: pr
+        uses: peter-evans/create-pull-request@v3
+        with:
+          title: '[Bot] Update playlists'
+          body: |
+            This pull request is created by [auto-update][1] workflow.
+
+            [1]: https://github.com/iptv-org/iptv/actions/runs/${{ github.run_id }}
+          author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
+          branch: 'bot/auto-update'
+          delete-branch: false
+          base: master
+          token: ${{ steps.generate-token.outputs.token }}

From b98e192f5fa89712d8ce022c655be0219d06b1cb Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 08:11:20 +0300
Subject: [PATCH 87/95] Delete test.yml

---
 .github/workflows/test.yml | 70 --------------------------------------
 1 file changed, 70 deletions(-)
 delete mode 100644 .github/workflows/test.yml

diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
deleted file mode 100644
index 20443dd36..000000000
--- a/.github/workflows/test.yml
+++ /dev/null
@@ -1,70 +0,0 @@
-name: test
-on:
-  workflow_dispatch:
-jobs:
-  create-branch:
-    runs-on: ubuntu-latest
-    steps:
-      - name: Checkout
-        uses: actions/checkout@v2
-        with:
-          ref: ${{ github.ref }}
-      - name: Create Branch
-        uses: peterjgrainger/action-create-branch@v2.0.1
-        env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-        with:
-          branch: 'bot/auto-update'
-  sort:
-    runs-on: ubuntu-latest
-    needs: create-branch
-    steps:
-      - name: Checkout
-        uses: actions/checkout@v2
-        with:
-          ref: bot/auto-update
-      - name: Install Dependencies
-        run: npm install
-      - name: Sort Channels
-        run: node scripts/sort.js
-      - name: Commit Changes
-        uses: stefanzweifel/git-auto-commit-action@v4
-        with:
-          commit_message: '[Bot] Sort channels'
-          commit_user_name: iptv-bot
-          commit_user_email: 84861620+iptv-bot[bot]@users.noreply.github.com
-          commit_author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
-          branch: bot/auto-update
-          file_pattern: channels/*
-  pull-request:
-    needs: sort
-    runs-on: ubuntu-latest
-    steps:
-      - name: Checkout
-        uses: actions/checkout@v2
-        with:
-          ref: bot/auto-update
-      - name: Generate Token
-        uses: tibdex/github-app-token@v1
-        id: generate-token
-        with:
-          app_id: ${{ secrets.APP_ID }}
-          private_key: ${{ secrets.APP_PRIVATE_KEY }}
-      - name: Fetch Origin
-        run: git fetch origin --allow-unrelated-histories
-      - name: Update from master
-        run: git merge origin/master
-      - name: Create Pull Request
-        id: pr
-        uses: peter-evans/create-pull-request@v3
-        with:
-          title: '[Bot] Update playlists'
-          body: |
-            This pull request is created by [auto-update][1] workflow.
-
-            [1]: https://github.com/iptv-org/iptv/actions/runs/${{ github.run_id }}
-          author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
-          branch: 'bot/auto-update'
-          delete-branch: false
-          base: master
-          token: ${{ steps.generate-token.outputs.token }}

From ce18f5e6e997038260ec71bac40270342c879723 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 08:12:42 +0300
Subject: [PATCH 88/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 354 +-----------------------------
 1 file changed, 6 insertions(+), 348 deletions(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index 680564674..fba565387 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -17,51 +17,9 @@ jobs:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
         with:
           branch: 'bot/auto-update'
-  format:
-    runs-on: ubuntu-latest
-    needs: create-branch
-    steps:
-      - name: Checkout
-        uses: actions/checkout@v2
-        with:
-          ref: bot/auto-update
-      - name: Install Dependencies
-        run: npm install
-      - name: Format Playlists
-        run: node scripts/format.js
-      - name: Commit Changes
-        uses: stefanzweifel/git-auto-commit-action@v4
-        with:
-          commit_message: '[Bot] Formate playlists'
-          commit_user_name: iptv-bot
-          commit_user_email: 84861620+iptv-bot[bot]@users.noreply.github.com
-          commit_author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
-          branch: bot/auto-update
-          file_pattern: channels/*
-  remove-duplicates:
-    runs-on: ubuntu-latest
-    needs: format
-    steps:
-      - name: Checkout
-        uses: actions/checkout@v2
-        with:
-          ref: bot/auto-update
-      - name: Install Dependencies
-        run: npm install
-      - name: Remove Duplicates
-        run: node scripts/remove-duplicates.js
-      - name: Commit Changes
-        uses: stefanzweifel/git-auto-commit-action@v4
-        with:
-          commit_message: '[Bot] Remove duplicates'
-          commit_user_name: iptv-bot
-          commit_user_email: 84861620+iptv-bot[bot]@users.noreply.github.com
-          commit_author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
-          branch: bot/auto-update
-          file_pattern: channels/*
   sort:
     runs-on: ubuntu-latest
-    needs: remove-duplicates
+    needs: create-branch
     steps:
       - name: Checkout
         uses: actions/checkout@v2
@@ -80,305 +38,18 @@ jobs:
           commit_author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
           branch: bot/auto-update
           file_pattern: channels/*
-  filter:
-    runs-on: ubuntu-latest
-    needs: sort
-    steps:
-      - name: Checkout
-        uses: actions/checkout@v2
-        with:
-          ref: bot/auto-update
-      - name: Install Dependencies
-        run: npm install
-      - name: Filter Playlists
-        run: node scripts/filter.js
-      - name: Commit Changes
-        uses: stefanzweifel/git-auto-commit-action@v4
-        with:
-          commit_message: '[Bot] Filter channels'
-          commit_user_name: iptv-bot
-          commit_user_email: 84861620+iptv-bot[bot]@users.noreply.github.com
-          commit_author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
-          branch: bot/auto-update
-          file_pattern: channels/*
-  detect-resolution:
-    runs-on: ubuntu-latest
-    needs: filter
-    continue-on-error: true
-    strategy:
-      fail-fast: false
-      matrix:
-        country: [
-            # ad,
-            # ae,
-            # af,
-            # ag,
-            # al,
-            # am,
-            an
-            # ao,
-            # ar,
-            # at,
-            # au,
-            # aw,
-            # az,
-            # ba,
-            # bb,
-            # bd,
-            # be,
-            # bf,
-            # bg,
-            # bh,
-            # bn,
-            # bo,
-            # br,
-            # bs,
-            # by,
-            # ca,
-            # cd,
-            # cg,
-            # ch,
-            # ci,
-            # cl,
-            # cm,
-            # cn,
-            # co,
-            # cr,
-            # cu,
-            # cw,
-            # cy,
-            # cz,
-            # de,
-            # dk,
-            # do,
-            # dz,
-            # ec,
-            # ee,
-            # eg,
-            # es,
-            # et,
-            # fi,
-            # fj,
-            # fo,
-            # fr,
-            # ge,
-            # gh,
-            # gm,
-            # gn,
-            # gp,
-            # gq,
-            # gr,
-            # gt,
-            # hk,
-            # hn,
-            # hr,
-            # ht,
-            # hu,
-            # id,
-            # ie,
-            # il,
-            # in,
-            # iq,
-            # ir,
-            # is,
-            # it,
-            # jm,
-            # jo,
-            # jp,
-            # ke,
-            # kg,
-            # kh,
-            # kp,
-            # kr,
-            # kw,
-            # kz,
-            # la,
-            # lb,
-            # li,
-            # lk,
-            # lt,
-            # lu,
-            # lv,
-            # ly,
-            # ma,
-            # mc,
-            # md,
-            # me,
-            # mk,
-            # ml,
-            # mm,
-            # mn,
-            # mo,
-            # mt,
-            # mv,
-            # mx,
-            # my,
-            # mz,
-            # ne,
-            # ng,
-            # ni,
-            # nl,
-            # no,
-            # np,
-            # nz,
-            # om,
-            # pa,
-            # pe,
-            # ph,
-            # pk,
-            # pl,
-            # pr,
-            # ps,
-            # pt,
-            # py,
-            # qa,
-            # ro,
-            # rs,
-            # ru,
-            # rw,
-            # sa,
-            # sd,
-            # se,
-            # sg,
-            # si,
-            # sk,
-            # sl,
-            # sm,
-            # sn,
-            # so,
-            # sv,
-            # sy,
-            # th,
-            # tj,
-            # tm,
-            # tn,
-            # tr,
-            # tt,
-            # tw,
-            # tz,
-            # ua,
-            # ug,
-            # uk,
-            # us,
-            # uy,
-            # uz,
-            # va,
-            # ve,
-            # vi,
-            # vn,
-            # xk,
-            # ye,
-            # zm
-          ]
-    steps:
-      - name: Checkout
-        uses: actions/checkout@v2
-        with:
-          ref: bot/auto-update
-      - name: Install Dependencies
-        run: npm install
-      - name: Detect Resolution
-        run: node scripts/detect-resolution.js --country=${{ matrix.country }}
-      - name: Upload Artifact
-        uses: actions/upload-artifact@v2
-        with:
-          name: channels
-          path: channels/${{ matrix.country }}.m3u
-  commit-changes:
-    runs-on: ubuntu-latest
-    needs: detect-resolution
-    steps:
-      - name: Checkout
-        uses: actions/checkout@v2
-        with:
-          ref: bot/auto-update
-      - name: Download Artifacts
-        uses: actions/download-artifact@v2
-        with:
-          name: channels
-      - name: Commit Changes
-        uses: stefanzweifel/git-auto-commit-action@v4
-        with:
-          commit_message: '[Bot] Detect resolution'
-          commit_user_name: iptv-bot
-          commit_user_email: 84861620+iptv-bot[bot]@users.noreply.github.com
-          commit_author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
-          branch: bot/auto-update
-          file_pattern: channels/*
-  generate:
-    runs-on: ubuntu-latest
-    needs: commit-changes
-    steps:
-      - name: Checkout
-        uses: actions/checkout@v2
-        with:
-          ref: bot/auto-update
-      - name: Install Dependencies
-        run: npm install
-      - name: Generate Playlists
-        run: node scripts/generate.js
-      - name: Upload Artifact
-        uses: actions/upload-artifact@v2
-        with:
-          name: gh-pages
-          path: .gh-pages/
-  deploy:
-    if: ${{ github.ref == 'refs/heads/master' }}
-    runs-on: ubuntu-latest
-    needs: generate
-    steps:
-      - name: Checkout
-        uses: actions/checkout@v2
-        with:
-          ref: bot/auto-update
-      - name: Download Artifacts
-        uses: actions/download-artifact@v2
-        with:
-          name: gh-pages
-      - name: Generate Token
-        uses: tibdex/github-app-token@v1
-        id: generate-token
-        with:
-          app_id: ${{ secrets.APP_ID }}
-          private_key: ${{ secrets.APP_PRIVATE_KEY }}
-      - name: Deploy to GitHub Pages
-        uses: JamesIves/github-pages-deploy-action@4.1.1
-        with:
-          branch: gh-pages
-          folder: gh-pages
-          token: ${{ steps.generate-token.outputs.token }}
-          git-config-name: iptv-bot
-          git-config-email: 84861620+iptv-bot[bot]@users.noreply.github.com
-          commit-message: '[Bot] Deploy to GitHub Pages'
-  update-readme:
-    runs-on: ubuntu-latest
-    needs: generate
-    steps:
-      - name: Checkout
-        uses: actions/checkout@v2
-        with:
-          ref: bot/auto-update
-      - name: Install Dependencies
-        run: npm install
-      - name: Update README.md
-        run: node scripts/update-readme.js
-      - name: Commit Changes
-        uses: stefanzweifel/git-auto-commit-action@v4
-        with:
-          commit_message: '[Bot] Update README.md'
-          commit_user_name: iptv-bot
-          commit_user_email: 84861620+iptv-bot[bot]@users.noreply.github.com
-          commit_author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
-          branch: bot/auto-update
-          file_pattern: README.md
   pull-request:
-    needs: update-readme
+    needs: sort
     runs-on: ubuntu-latest
     steps:
       - name: Checkout
         uses: actions/checkout@v2
         with:
           ref: bot/auto-update
+      - name: Fetch Origin
+        run: git fetch origin --allow-unrelated-histories
+      - name: Update from master
+        run: git merge origin/master
       - name: Generate Token
         uses: tibdex/github-app-token@v1
         id: generate-token
@@ -399,16 +70,3 @@ jobs:
           delete-branch: false
           base: master
           token: ${{ steps.generate-token.outputs.token }}
-      - name: Enable Pull Request Automerge
-        if: steps.pr.outputs.pull-request-operation == 'created'
-        uses: peter-evans/enable-pull-request-automerge@v1
-        with:
-          token: ${{ secrets.PAT }}
-          pull-request-number: ${{ steps.pr.outputs.pull-request-number }}
-          merge-method: squash
-      - name: Approve Pull Request
-        if: github.ref == 'refs/heads/master' && steps.pr.outputs.pull-request-operation == 'created'
-        uses: juliangruber/approve-pull-request-action@v1
-        with:
-          github-token: ${{ secrets.PAT }}
-          number: ${{ steps.pr.outputs.pull-request-number }}

From 0c62d8348a2246b11adf5e9c2206300a94f73862 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 08:16:05 +0300
Subject: [PATCH 89/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index fba565387..09293c74e 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -47,9 +47,9 @@ jobs:
         with:
           ref: bot/auto-update
       - name: Fetch Origin
-        run: git fetch origin --allow-unrelated-histories
+        run: git fetch origin
       - name: Update from master
-        run: git merge origin/master
+        run: git merge --allow-unrelated-histories origin/master
       - name: Generate Token
         uses: tibdex/github-app-token@v1
         id: generate-token

From 3beb6247964ee683ca1e279b6f6fdb9b12ddff66 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 08:23:04 +0300
Subject: [PATCH 90/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 18 ++++++------------
 1 file changed, 6 insertions(+), 12 deletions(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index 09293c74e..fd904506a 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -46,10 +46,6 @@ jobs:
         uses: actions/checkout@v2
         with:
           ref: bot/auto-update
-      - name: Fetch Origin
-        run: git fetch origin
-      - name: Update from master
-        run: git merge --allow-unrelated-histories origin/master
       - name: Generate Token
         uses: tibdex/github-app-token@v1
         id: generate-token
@@ -58,15 +54,13 @@ jobs:
           private_key: ${{ secrets.APP_PRIVATE_KEY }}
       - name: Create Pull Request
         id: pr
-        uses: peter-evans/create-pull-request@v3
+        uses: tretuna/sync-branches@1.2.0
         with:
-          title: '[Bot] Update playlists'
-          body: |
+          FROM_BRANCH: 'bot/auto-update'
+          TO_BRANCH: 'master'
+          PULL_REQUEST_TITLE: '[Bot] Update playlists'
+          PULL_REQUEST_BODY: |
             This pull request is created by [auto-update][1] workflow.
 
             [1]: https://github.com/iptv-org/iptv/actions/runs/${{ github.run_id }}
-          author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
-          branch: 'bot/auto-update'
-          delete-branch: false
-          base: master
-          token: ${{ steps.generate-token.outputs.token }}
+          GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }}

From 6fd9421f02aac784bb1a2e5ddd0ea985194d4a72 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 08:26:55 +0300
Subject: [PATCH 91/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index fd904506a..f6c704ce1 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -54,13 +54,13 @@ jobs:
           private_key: ${{ secrets.APP_PRIVATE_KEY }}
       - name: Create Pull Request
         id: pr
-        uses: tretuna/sync-branches@1.2.0
+        uses: repo-sync/pull-request@v2
         with:
-          FROM_BRANCH: 'bot/auto-update'
-          TO_BRANCH: 'master'
-          PULL_REQUEST_TITLE: '[Bot] Update playlists'
-          PULL_REQUEST_BODY: |
+          source_branch: 'bot/auto-update'
+          destination_branch: 'master'
+          pr_title: '[Bot] Update playlists'
+          pr_body: |
             This pull request is created by [auto-update][1] workflow.
 
             [1]: https://github.com/iptv-org/iptv/actions/runs/${{ github.run_id }}
-          GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }}
+          github_token: ${{ steps.generate-token.outputs.token }}

From ebd9105690af6605cb7ac51992aa73fe551c17ab Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 08:33:07 +0300
Subject: [PATCH 92/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 350 +++++++++++++++++++++++++++++-
 1 file changed, 348 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index f6c704ce1..c03596bf4 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -17,9 +17,51 @@ jobs:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
         with:
           branch: 'bot/auto-update'
-  sort:
+  format:
     runs-on: ubuntu-latest
     needs: create-branch
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+        with:
+          ref: bot/auto-update
+      - name: Install Dependencies
+        run: npm install
+      - name: Format Playlists
+        run: node scripts/format.js
+      - name: Commit Changes
+        uses: stefanzweifel/git-auto-commit-action@v4
+        with:
+          commit_message: '[Bot] Formate playlists'
+          commit_user_name: iptv-bot
+          commit_user_email: 84861620+iptv-bot[bot]@users.noreply.github.com
+          commit_author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
+          branch: bot/auto-update
+          file_pattern: channels/*
+  remove-duplicates:
+    runs-on: ubuntu-latest
+    needs: format
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+        with:
+          ref: bot/auto-update
+      - name: Install Dependencies
+        run: npm install
+      - name: Remove Duplicates
+        run: node scripts/remove-duplicates.js
+      - name: Commit Changes
+        uses: stefanzweifel/git-auto-commit-action@v4
+        with:
+          commit_message: '[Bot] Remove duplicates'
+          commit_user_name: iptv-bot
+          commit_user_email: 84861620+iptv-bot[bot]@users.noreply.github.com
+          commit_author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
+          branch: bot/auto-update
+          file_pattern: channels/*
+  sort:
+    runs-on: ubuntu-latest
+    needs: remove-duplicates
     steps:
       - name: Checkout
         uses: actions/checkout@v2
@@ -38,8 +80,299 @@ jobs:
           commit_author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
           branch: bot/auto-update
           file_pattern: channels/*
-  pull-request:
+  filter:
+    runs-on: ubuntu-latest
     needs: sort
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+        with:
+          ref: bot/auto-update
+      - name: Install Dependencies
+        run: npm install
+      - name: Filter Playlists
+        run: node scripts/filter.js
+      - name: Commit Changes
+        uses: stefanzweifel/git-auto-commit-action@v4
+        with:
+          commit_message: '[Bot] Filter channels'
+          commit_user_name: iptv-bot
+          commit_user_email: 84861620+iptv-bot[bot]@users.noreply.github.com
+          commit_author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
+          branch: bot/auto-update
+          file_pattern: channels/*
+  detect-resolution:
+    runs-on: ubuntu-latest
+    needs: filter
+    continue-on-error: true
+    strategy:
+      fail-fast: false
+      matrix:
+        country: [
+            # ad,
+            # ae,
+            # af,
+            # ag,
+            # al,
+            # am,
+            an
+            # ao,
+            # ar,
+            # at,
+            # au,
+            # aw,
+            # az,
+            # ba,
+            # bb,
+            # bd,
+            # be,
+            # bf,
+            # bg,
+            # bh,
+            # bn,
+            # bo,
+            # br,
+            # bs,
+            # by,
+            # ca,
+            # cd,
+            # cg,
+            # ch,
+            # ci,
+            # cl,
+            # cm,
+            # cn,
+            # co,
+            # cr,
+            # cu,
+            # cw,
+            # cy,
+            # cz,
+            # de,
+            # dk,
+            # do,
+            # dz,
+            # ec,
+            # ee,
+            # eg,
+            # es,
+            # et,
+            # fi,
+            # fj,
+            # fo,
+            # fr,
+            # ge,
+            # gh,
+            # gm,
+            # gn,
+            # gp,
+            # gq,
+            # gr,
+            # gt,
+            # hk,
+            # hn,
+            # hr,
+            # ht,
+            # hu,
+            # id,
+            # ie,
+            # il,
+            # in,
+            # iq,
+            # ir,
+            # is,
+            # it,
+            # jm,
+            # jo,
+            # jp,
+            # ke,
+            # kg,
+            # kh,
+            # kp,
+            # kr,
+            # kw,
+            # kz,
+            # la,
+            # lb,
+            # li,
+            # lk,
+            # lt,
+            # lu,
+            # lv,
+            # ly,
+            # ma,
+            # mc,
+            # md,
+            # me,
+            # mk,
+            # ml,
+            # mm,
+            # mn,
+            # mo,
+            # mt,
+            # mv,
+            # mx,
+            # my,
+            # mz,
+            # ne,
+            # ng,
+            # ni,
+            # nl,
+            # no,
+            # np,
+            # nz,
+            # om,
+            # pa,
+            # pe,
+            # ph,
+            # pk,
+            # pl,
+            # pr,
+            # ps,
+            # pt,
+            # py,
+            # qa,
+            # ro,
+            # rs,
+            # ru,
+            # rw,
+            # sa,
+            # sd,
+            # se,
+            # sg,
+            # si,
+            # sk,
+            # sl,
+            # sm,
+            # sn,
+            # so,
+            # sv,
+            # sy,
+            # th,
+            # tj,
+            # tm,
+            # tn,
+            # tr,
+            # tt,
+            # tw,
+            # tz,
+            # ua,
+            # ug,
+            # uk,
+            # us,
+            # uy,
+            # uz,
+            # va,
+            # ve,
+            # vi,
+            # vn,
+            # xk,
+            # ye,
+            # zm
+          ]
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+        with:
+          ref: bot/auto-update
+      - name: Install Dependencies
+        run: npm install
+      - name: Detect Resolution
+        run: node scripts/detect-resolution.js --country=${{ matrix.country }}
+      - name: Upload Artifact
+        uses: actions/upload-artifact@v2
+        with:
+          name: channels
+          path: channels/${{ matrix.country }}.m3u
+  commit-changes:
+    runs-on: ubuntu-latest
+    needs: detect-resolution
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+        with:
+          ref: bot/auto-update
+      - name: Download Artifacts
+        uses: actions/download-artifact@v2
+        with:
+          name: channels
+      - name: Commit Changes
+        uses: stefanzweifel/git-auto-commit-action@v4
+        with:
+          commit_message: '[Bot] Detect resolution'
+          commit_user_name: iptv-bot
+          commit_user_email: 84861620+iptv-bot[bot]@users.noreply.github.com
+          commit_author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
+          branch: bot/auto-update
+          file_pattern: channels/*
+  generate:
+    runs-on: ubuntu-latest
+    needs: commit-changes
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+        with:
+          ref: bot/auto-update
+      - name: Install Dependencies
+        run: npm install
+      - name: Generate Playlists
+        run: node scripts/generate.js
+      - name: Upload Artifact
+        uses: actions/upload-artifact@v2
+        with:
+          name: gh-pages
+          path: .gh-pages/
+  deploy:
+    if: ${{ github.ref == 'refs/heads/master' }}
+    runs-on: ubuntu-latest
+    needs: generate
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+        with:
+          ref: bot/auto-update
+      - name: Download Artifacts
+        uses: actions/download-artifact@v2
+        with:
+          name: gh-pages
+      - name: Generate Token
+        uses: tibdex/github-app-token@v1
+        id: generate-token
+        with:
+          app_id: ${{ secrets.APP_ID }}
+          private_key: ${{ secrets.APP_PRIVATE_KEY }}
+      - name: Deploy to GitHub Pages
+        uses: JamesIves/github-pages-deploy-action@4.1.1
+        with:
+          branch: gh-pages
+          folder: gh-pages
+          token: ${{ steps.generate-token.outputs.token }}
+          git-config-name: iptv-bot
+          git-config-email: 84861620+iptv-bot[bot]@users.noreply.github.com
+          commit-message: '[Bot] Deploy to GitHub Pages'
+  update-readme:
+    runs-on: ubuntu-latest
+    needs: generate
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+        with:
+          ref: bot/auto-update
+      - name: Install Dependencies
+        run: npm install
+      - name: Update README.md
+        run: node scripts/update-readme.js
+      - name: Commit Changes
+        uses: stefanzweifel/git-auto-commit-action@v4
+        with:
+          commit_message: '[Bot] Update README.md'
+          commit_user_name: iptv-bot
+          commit_user_email: 84861620+iptv-bot[bot]@users.noreply.github.com
+          commit_author: 'iptv-bot[bot] <84861620+iptv-bot[bot]@users.noreply.github.com>'
+          branch: bot/auto-update
+          file_pattern: README.md
+  pull-request:
+    needs: update-readme
     runs-on: ubuntu-latest
     steps:
       - name: Checkout
@@ -64,3 +397,16 @@ jobs:
 
             [1]: https://github.com/iptv-org/iptv/actions/runs/${{ github.run_id }}
           github_token: ${{ steps.generate-token.outputs.token }}
+      - name: Enable Pull Request Automerge
+        if: steps.pr.outputs.pull-request-operation == 'created'
+        uses: peter-evans/enable-pull-request-automerge@v1
+        with:
+          token: ${{ secrets.PAT }}
+          pull-request-number: ${{ steps.pr.outputs.pull-request-number }}
+          merge-method: squash
+      - name: Approve Pull Request
+        if: github.ref == 'refs/heads/master' && steps.pr.outputs.pull-request-operation == 'created'
+        uses: juliangruber/approve-pull-request-action@v1
+        with:
+          github-token: ${{ secrets.PAT }}
+          number: ${{ steps.pr.outputs.pull-request-number }}

From 07627d7bb0324126a7df41c578798c02d09fce87 Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 08:36:25 +0300
Subject: [PATCH 93/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index c03596bf4..27f27f99f 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -398,15 +398,14 @@ jobs:
             [1]: https://github.com/iptv-org/iptv/actions/runs/${{ github.run_id }}
           github_token: ${{ steps.generate-token.outputs.token }}
       - name: Enable Pull Request Automerge
-        if: steps.pr.outputs.pull-request-operation == 'created'
         uses: peter-evans/enable-pull-request-automerge@v1
         with:
           token: ${{ secrets.PAT }}
-          pull-request-number: ${{ steps.pr.outputs.pull-request-number }}
+          pull-request-number: ${{ steps.pr.outputs.pr_number }}
           merge-method: squash
       - name: Approve Pull Request
-        if: github.ref == 'refs/heads/master' && steps.pr.outputs.pull-request-operation == 'created'
+        if: github.ref == 'refs/heads/master'
         uses: juliangruber/approve-pull-request-action@v1
         with:
           github-token: ${{ secrets.PAT }}
-          number: ${{ steps.pr.outputs.pull-request-number }}
+          number: ${{ steps.pr.outputs.pr_number }}

From 561d7fedcb08c655dbc4a7b24514e732b3f03dec Mon Sep 17 00:00:00 2001
From: Aleksandr Statciuk <free.arhey@gmail.com>
Date: Tue, 3 Aug 2021 08:44:21 +0300
Subject: [PATCH 94/95] Update auto-update.yml

---
 .github/workflows/auto-update.yml | 323 +++++++++++++++---------------
 1 file changed, 162 insertions(+), 161 deletions(-)

diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml
index 27f27f99f..e56848709 100644
--- a/.github/workflows/auto-update.yml
+++ b/.github/workflows/auto-update.yml
@@ -108,167 +108,168 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        country: [
-            # ad,
-            # ae,
-            # af,
-            # ag,
-            # al,
-            # am,
-            an
-            # ao,
-            # ar,
-            # at,
-            # au,
-            # aw,
-            # az,
-            # ba,
-            # bb,
-            # bd,
-            # be,
-            # bf,
-            # bg,
-            # bh,
-            # bn,
-            # bo,
-            # br,
-            # bs,
-            # by,
-            # ca,
-            # cd,
-            # cg,
-            # ch,
-            # ci,
-            # cl,
-            # cm,
-            # cn,
-            # co,
-            # cr,
-            # cu,
-            # cw,
-            # cy,
-            # cz,
-            # de,
-            # dk,
-            # do,
-            # dz,
-            # ec,
-            # ee,
-            # eg,
-            # es,
-            # et,
-            # fi,
-            # fj,
-            # fo,
-            # fr,
-            # ge,
-            # gh,
-            # gm,
-            # gn,
-            # gp,
-            # gq,
-            # gr,
-            # gt,
-            # hk,
-            # hn,
-            # hr,
-            # ht,
-            # hu,
-            # id,
-            # ie,
-            # il,
-            # in,
-            # iq,
-            # ir,
-            # is,
-            # it,
-            # jm,
-            # jo,
-            # jp,
-            # ke,
-            # kg,
-            # kh,
-            # kp,
-            # kr,
-            # kw,
-            # kz,
-            # la,
-            # lb,
-            # li,
-            # lk,
-            # lt,
-            # lu,
-            # lv,
-            # ly,
-            # ma,
-            # mc,
-            # md,
-            # me,
-            # mk,
-            # ml,
-            # mm,
-            # mn,
-            # mo,
-            # mt,
-            # mv,
-            # mx,
-            # my,
-            # mz,
-            # ne,
-            # ng,
-            # ni,
-            # nl,
-            # no,
-            # np,
-            # nz,
-            # om,
-            # pa,
-            # pe,
-            # ph,
-            # pk,
-            # pl,
-            # pr,
-            # ps,
-            # pt,
-            # py,
-            # qa,
-            # ro,
-            # rs,
-            # ru,
-            # rw,
-            # sa,
-            # sd,
-            # se,
-            # sg,
-            # si,
-            # sk,
-            # sl,
-            # sm,
-            # sn,
-            # so,
-            # sv,
-            # sy,
-            # th,
-            # tj,
-            # tm,
-            # tn,
-            # tr,
-            # tt,
-            # tw,
-            # tz,
-            # ua,
-            # ug,
-            # uk,
-            # us,
-            # uy,
-            # uz,
-            # va,
-            # ve,
-            # vi,
-            # vn,
-            # xk,
-            # ye,
-            # zm
+        country:
+          [
+            ad,
+            ae,
+            af,
+            ag,
+            al,
+            am,
+            an,
+            ao,
+            ar,
+            at,
+            au,
+            aw,
+            az,
+            ba,
+            bb,
+            bd,
+            be,
+            bf,
+            bg,
+            bh,
+            bn,
+            bo,
+            br,
+            bs,
+            by,
+            ca,
+            cd,
+            cg,
+            ch,
+            ci,
+            cl,
+            cm,
+            cn,
+            co,
+            cr,
+            cu,
+            cw,
+            cy,
+            cz,
+            de,
+            dk,
+            do,
+            dz,
+            ec,
+            ee,
+            eg,
+            es,
+            et,
+            fi,
+            fj,
+            fo,
+            fr,
+            ge,
+            gh,
+            gm,
+            gn,
+            gp,
+            gq,
+            gr,
+            gt,
+            hk,
+            hn,
+            hr,
+            ht,
+            hu,
+            id,
+            ie,
+            il,
+            in,
+            iq,
+            ir,
+            is,
+            it,
+            jm,
+            jo,
+            jp,
+            ke,
+            kg,
+            kh,
+            kp,
+            kr,
+            kw,
+            kz,
+            la,
+            lb,
+            li,
+            lk,
+            lt,
+            lu,
+            lv,
+            ly,
+            ma,
+            mc,
+            md,
+            me,
+            mk,
+            ml,
+            mm,
+            mn,
+            mo,
+            mt,
+            mv,
+            mx,
+            my,
+            mz,
+            ne,
+            ng,
+            ni,
+            nl,
+            no,
+            np,
+            nz,
+            om,
+            pa,
+            pe,
+            ph,
+            pk,
+            pl,
+            pr,
+            ps,
+            pt,
+            py,
+            qa,
+            ro,
+            rs,
+            ru,
+            rw,
+            sa,
+            sd,
+            se,
+            sg,
+            si,
+            sk,
+            sl,
+            sm,
+            sn,
+            so,
+            sv,
+            sy,
+            th,
+            tj,
+            tm,
+            tn,
+            tr,
+            tt,
+            tw,
+            tz,
+            ua,
+            ug,
+            uk,
+            us,
+            uy,
+            uz,
+            va,
+            ve,
+            vi,
+            vn,
+            xk,
+            ye,
+            zm
           ]
     steps:
       - name: Checkout

From 4aadad83c279e39387e723c9a0d12b5cf2e7bbc4 Mon Sep 17 00:00:00 2001
From: MH <8741821+MH140000@users.noreply.github.com>
Date: Tue, 3 Aug 2021 14:04:10 +0430
Subject: [PATCH 95/95] Update ir.m3u

---
 channels/ir.m3u | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/channels/ir.m3u b/channels/ir.m3u
index ba5e77d21..0d46e0dde 100644
--- a/channels/ir.m3u
+++ b/channels/ir.m3u
@@ -39,16 +39,16 @@ http://159.69.58.154/ekran/ekrantv.m3u8
 https://faraztv.net/hls/stream.m3u8
 #EXTINF:-1 tvg-id="Film1.ir" tvg-name="Film 1" tvg-country="IR" tvg-language="Persian" tvg-logo="" group-title="Movies",Film 1
 http://159.69.58.154/film1/film1tv.m3u8
-#EXTINF:-1 tvg-id="GEM24b.ir" tvg-name="GEM 24b" tvg-country="IR" tvg-language="Persian" tvg-logo="https://www.gemonline.tv/Assets/channels-box/24b.png" group-title="Entertainment",GEM 24b [Geo-blocked]
+#EXTINF:-1 tvg-id="GEM24b.ir" tvg-name="GEM 24b" tvg-country="IR" tvg-language="Persian" tvg-logo="https://www.gemonline.tv/Assets/channels-box/24b.png" group-title="Music",GEM 24b [Geo-blocked]
 #EXTVLCOPT:http-referrer=https://www.gemonline.tv/en-US/Live/Index?channelname=24b
 https://d2e40kvaojifd6.cloudfront.net/stream/24b/playlist.m3u8
 #EXTINF:-1 tvg-id="GEMAcademy.ir" tvg-name="GEM Academy" tvg-country="IR" tvg-language="Persian" tvg-logo="https://www.gemonline.tv/Assets/channels-box/gemusa.png" group-title="Movies",GEM Academy [Geo-blocked]
 #EXTVLCOPT:http-referrer=https://www.gemonline.tv/en-US/Live/Index?channelname=gemusa
 https://d2e40kvaojifd6.cloudfront.net/stream/gem_usa/playlist.m3u8
-#EXTINF:-1 tvg-id="GEMArabia.ir" tvg-name="GEM Arabia" tvg-country="IR" tvg-language="Arabic" tvg-logo="https://www.gemonline.tv/Assets/channels-box/gem_arabia.png" group-title="Entertainment",GEM Arabia [Geo-blocked]
+#EXTINF:-1 tvg-id="GEMArabia.ir" tvg-name="GEM Arabia" tvg-country="IR" tvg-language="Arabic" tvg-logo="https://www.gemonline.tv/Assets/channels-box/gem_arabia.png" group-title="Music",GEM Arabia [Geo-blocked]
 #EXTVLCOPT:http-referrer=https://www.gemonline.tv/en-US/Live/Index?channelname=gem_arabia
 https://d2e40kvaojifd6.cloudfront.net/stream/gem_arabia/playlist.m3u8
-#EXTINF:-1 tvg-id="GEMAZ.ir" tvg-name="GEM AZ" tvg-country="IR" tvg-language="Persian" tvg-logo="https://www.gemonline.tv/Assets/channels-box/gem_az.png" group-title="Entertainment",GEM AZ [Geo-blocked]
+#EXTINF:-1 tvg-id="GEMAZ.ir" tvg-name="GEM AZ" tvg-country="IR" tvg-language="Persian" tvg-logo="https://www.gemonline.tv/Assets/channels-box/gem_az.png" group-title="Music",GEM AZ [Geo-blocked]
 #EXTVLCOPT:http-referrer=https://www.gemonline.tv/en-US/Live/Index?channelname=gem_az
 https://d2e40kvaojifd6.cloudfront.net/stream/gem_az/playlist.m3u8
 #EXTINF:-1 tvg-id="GEMBollywood.ir" tvg-name="GEM Bollywood" tvg-country="IR" tvg-language="Persian" tvg-logo="https://www.gemonline.tv/Assets/channels-box/gembollywood.png" group-title="Movies",GEM Bollywood [Geo-blocked]
@@ -87,7 +87,7 @@ https://d2e40kvaojifd6.cloudfront.net/stream/gem_life/playlist.m3u8
 #EXTINF:-1 tvg-id="GEMMaxx.ir" tvg-name="GEM Maxx" tvg-country="IR" tvg-language="Persian" tvg-logo="https://www.gemonline.tv/Assets/channels-box/maxx.png?14" group-title="Entertainment",GEM Maxx [Geo-blocked]
 #EXTVLCOPT:http-referrer=https://www.gemonline.tv/en-US/Live/Index?channelname=maxx
 https://d2e40kvaojifd6.cloudfront.net/stream/maxx/playlist.m3u8
-#EXTINF:-1 tvg-id="GEMMifa.ir" tvg-name="GEM Mifa" tvg-country="IR" tvg-language="Persian" tvg-logo="https://www.gemonline.tv/Assets/channels-box/mifa.png" group-title="Entertainment",GEM Mifa [Geo-blocked]
+#EXTINF:-1 tvg-id="GEMMifa.ir" tvg-name="GEM Mifa" tvg-country="IR" tvg-language="Persian" tvg-logo="https://www.gemonline.tv/Assets/channels-box/mifa.png" group-title="Music",GEM Mifa [Geo-blocked]
 #EXTVLCOPT:http-referrer=https://www.gemonline.tv/en-US/Live/Index?channelname=mifa
 https://d2e40kvaojifd6.cloudfront.net/stream/mifa/playlist.m3u8
 #EXTINF:-1 tvg-id="GEMModernEconomy.ir" tvg-name="GEM ModernEconomy" tvg-country="IR" tvg-language="Persian" tvg-logo="https://www.gemonline.tv/Assets/channels-box/modern_economy.png" group-title="Documentary",GEM Modern Economy [Geo-blocked]