Update util.js
- replaced `playlist-parser` with `m3u8-file-parser` - added EPG parser - added Playlist and Channel classes - removed deprecated functions
This commit is contained in:
		
							
								
								
									
										169
									
								
								helpers/util.js
									
									
									
									
									
								
							
							
						
						
									
										169
									
								
								helpers/util.js
									
									
									
									
									
								
							| @@ -1,37 +1,39 @@ | |||||||
| const parsers = require('playlist-parser') |  | ||||||
| const M3U = parsers.M3U |  | ||||||
| const fs = require("fs") | const fs = require("fs") | ||||||
| const path = require('path') | const path = require('path') | ||||||
|  | const M3U8FileParser = require('m3u8-file-parser') | ||||||
|  | const https = require("https") | ||||||
|  | const zlib = require("zlib") | ||||||
|  | const DOMParser = require('xmldom').DOMParser; | ||||||
|  |  | ||||||
| function parsePlaylist(filename) { | class Playlist { | ||||||
|   return M3U.parse(fs.readFileSync(path.resolve(__dirname) + `/../${filename}`, { encoding: "utf8" })) |   constructor(data) { | ||||||
|  |     this.attrs = data.attrs | ||||||
|  |     this.items = data.items | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   getHeader() { | ||||||
|  |     let parts = ['#EXTM3U'] | ||||||
|  |     for(let key in this.attrs) { | ||||||
|  |       let value = this.attrs[key] | ||||||
|  |       parts.push(`${key}="${value}"`) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return `${parts.join(' ')}\n` | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| function parseChannelData(item) { | class Channel { | ||||||
|   const info = getInfo(item) |   constructor(data) { | ||||||
|  |     this.id = data.id | ||||||
|   function getTvgId(info) { |     this.name = data.name | ||||||
|     const matches = info.match(/tvg\-id\=\"(.*?)\"/i) |     this.logo = data.logo | ||||||
|  |     this.group = this._getGroup(data.group) | ||||||
|     return (matches && matches.length > 1) ? matches[1] : '' |     this.url = data.url | ||||||
|  |     this.title = data.title | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   function getTvgName(info) { |   _getGroup(groupTitle) { | ||||||
|     const matches = info.match(/tvg\-name\=\"(.*?)\"/i) |  | ||||||
|  |  | ||||||
|     return (matches && matches.length > 1) ? matches[1] : '' |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   function getTvgLogo(info) { |  | ||||||
|     const matches = info.match(/tvg\-logo\=\"(.*?)\"/i) |  | ||||||
|  |  | ||||||
|     return (matches && matches.length > 1) ? matches[1] : '' |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   function getGroupTitle(item) { |  | ||||||
|     const supportedGroups = [ 'Auto','Business', 'CCTV', 'Classic','Comedy','Documentary','Education','Entertainment', 'Family','Fashion','Food', 'General', 'Health', 'History', 'Hobby', 'Kids', 'Legislative','Lifestyle','Local', 'Movies', 'Music', 'News', 'Quiz','Radio', 'Religious','Sci-Fi', 'Shop', 'Sport', 'Travel', 'Weather', 'XXX' ] |     const supportedGroups = [ 'Auto','Business', 'CCTV', 'Classic','Comedy','Documentary','Education','Entertainment', 'Family','Fashion','Food', 'General', 'Health', 'History', 'Hobby', 'Kids', 'Legislative','Lifestyle','Local', 'Movies', 'Music', 'News', 'Quiz','Radio', 'Religious','Sci-Fi', 'Shop', 'Sport', 'Travel', 'Weather', 'XXX' ] | ||||||
|     const matches = info.match(/group\-title\=\"(.*?)\"/i) |  | ||||||
|     let groupTitle = (matches && matches.length > 1) ? matches[1] : '' |  | ||||||
|     const groupIndex = supportedGroups.map(g => g.toLowerCase()).indexOf(groupTitle.toLowerCase()) |     const groupIndex = supportedGroups.map(g => g.toLowerCase()).indexOf(groupTitle.toLowerCase()) | ||||||
|  |  | ||||||
|     if(groupIndex === -1) { |     if(groupIndex === -1) { | ||||||
| @@ -43,24 +45,101 @@ function parseChannelData(item) { | |||||||
|     return groupTitle |     return groupTitle | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   return { |   toString() { | ||||||
|     title: getTitle(info), |     const info = `-1 tvg-id="${this.id}" tvg-name="${this.name}" tvg-logo="${this.logo}" group-title="${this.group}",${this.title}` | ||||||
|     file: item.file, |  | ||||||
|     id: getTvgId(info), |     return '#EXTINF:' + info + '\n' + this.url + '\n' | ||||||
|     name: getTvgName(info), |  | ||||||
|     logo: getTvgLogo(info), |  | ||||||
|     group: getGroupTitle(info) |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| function getInfo(item) { | function getGzipped(url) { | ||||||
|   return (item.artist) ? item.artist + '-' + item.title : item.title |   return new Promise((resolve, reject) => { | ||||||
|  |     var buffer = [] | ||||||
|  |     https.get(url, function(res) { | ||||||
|  |       var gunzip = zlib.createGunzip()          | ||||||
|  |       res.pipe(gunzip) | ||||||
|  |       gunzip.on('data', function(data) { | ||||||
|  |         buffer.push(data.toString()) | ||||||
|  |       }).on("end", function() { | ||||||
|  |         resolve(buffer.join("")) | ||||||
|  |       }).on("error", function(e) { | ||||||
|  |         reject(e) | ||||||
|  |       }) | ||||||
|  |     }).on('error', function(e) { | ||||||
|  |       reject(e) | ||||||
|  |     }) | ||||||
|  |   }) | ||||||
| } | } | ||||||
|  |  | ||||||
| function getTitle(info) { | function readFile(filename) { | ||||||
|   const parts = info.split(',') |   return fs.readFileSync(path.resolve(__dirname) + `/../${filename}`, { encoding: "utf8" }) | ||||||
|  | } | ||||||
|  |  | ||||||
|   return parts[parts.length - 1].trim() | async function loadEPG(url) { | ||||||
|  |   const data = await getGzipped(url) | ||||||
|  |   const doc = new DOMParser().parseFromString(data, 'text/xml') | ||||||
|  |   const channelElements = doc.getElementsByTagName('channel') | ||||||
|  |   let channels = {} | ||||||
|  |   for(let i = 0; i < channelElements.length; i++) { | ||||||
|  |     let channel = {} | ||||||
|  |     let channelElement = channelElements[i] | ||||||
|  |     channel.id = channelElement.getAttribute('id') | ||||||
|  |     channel.names = [] | ||||||
|  |     for(let nameElement of Object.values(channelElement.getElementsByTagName('display-name'))) { | ||||||
|  |       if(nameElement.firstChild) { | ||||||
|  |         channel.names.push(nameElement.firstChild.nodeValue) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     channel.names = channel.names.filter(n => n) | ||||||
|  |     const iconElements = channelElement.getElementsByTagName('icon') | ||||||
|  |     if(iconElements.length) { | ||||||
|  |       channel.icon = iconElements[0].getAttribute('src') | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     channels[channel.id] = channel | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return Promise.resolve({  | ||||||
|  |     url,  | ||||||
|  |     channels  | ||||||
|  |   }) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function createChannel(data) { | ||||||
|  |   return new Channel({ | ||||||
|  |     id: data.id, | ||||||
|  |     name: data.name, | ||||||
|  |     logo: data.logo, | ||||||
|  |     group: data.group, | ||||||
|  |     url: data.url, | ||||||
|  |     title: data.title | ||||||
|  |   }) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function parsePlaylist(filename) { | ||||||
|  |   const parser = new M3U8FileParser() | ||||||
|  |   const content = readFile(filename) | ||||||
|  |   parser.read(content) | ||||||
|  |   let results = parser.getResult() | ||||||
|  |   let contentMatches = content.match(/^.+(?=#|\n|\r)/g) | ||||||
|  |   let head = contentMatches.length ? contentMatches[0] : null | ||||||
|  |   let attrs = {} | ||||||
|  |   if(head) { | ||||||
|  |     const parts = head.split(' ').filter(p => p !== '#EXTM3U').filter(p => p) | ||||||
|  |  | ||||||
|  |     for(const attr of parts) { | ||||||
|  |       let attrParts = attr.split('=') | ||||||
|  |        | ||||||
|  |       attrs[attrParts[0]] = attrParts[1].replace(/\"/g, '') | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   results.attrs = attrs | ||||||
|  |  | ||||||
|  |   return new Playlist({ | ||||||
|  |     attrs: results.attrs, | ||||||
|  |     items: results.segments | ||||||
|  |   }) | ||||||
| } | } | ||||||
|  |  | ||||||
| function byTitle(a, b) { | function byTitle(a, b) { | ||||||
| @@ -80,7 +159,7 @@ function sortByTitle(arr) { | |||||||
|   return arr.sort(byTitle) |   return arr.sort(byTitle) | ||||||
| } | } | ||||||
|  |  | ||||||
| function writeToFile(filename, data) { | function appendToFile(filename, data) { | ||||||
|   fs.appendFileSync(path.resolve(__dirname) + '/../' + filename, data) |   fs.appendFileSync(path.resolve(__dirname) + '/../' + filename, data) | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -88,16 +167,12 @@ function createFile(filename, data) { | |||||||
|   fs.writeFileSync(path.resolve(__dirname) + '/../' + filename, data) |   fs.writeFileSync(path.resolve(__dirname) + '/../' + filename, data) | ||||||
| } | } | ||||||
|  |  | ||||||
| function getBasename(filename) { |  | ||||||
|   return path.basename(filename, path.extname(filename)) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| module.exports = { | module.exports = { | ||||||
|   parsePlaylist, |   parsePlaylist, | ||||||
|   parseChannelData, |  | ||||||
|   getTitle, |  | ||||||
|   sortByTitle, |   sortByTitle, | ||||||
|   writeToFile, |   appendToFile, | ||||||
|   createFile, |   createFile, | ||||||
|   getBasename |   readFile, | ||||||
|  |   loadEPG, | ||||||
|  |   createChannel | ||||||
| } | } | ||||||
		Reference in New Issue
	
	Block a user