Files
DemonEditor/app/tools/epg.py

145 lines
5.5 KiB
Python
Raw Normal View History

2022-01-24 16:39:59 +03:00
# -*- coding: utf-8 -*-
#
# The MIT License (MIT)
#
# Copyright (c) 2018-2022 Dmitriy Yefremov
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# Author: Dmitriy Yefremov
#
""" Module for working with epg.dat file. """
2019-04-18 23:05:19 +03:00
import struct
from datetime import datetime
2019-04-22 20:25:19 +03:00
from xml.dom.minidom import parse, Node, Document
2019-04-18 23:05:19 +03:00
from app.eparser.ecommons import BqServiceType, BouquetService
2019-04-18 23:05:19 +03:00
class EPG:
@staticmethod
def get_epg_refs(path):
""" The read algorithm was taken from the eEPGCache::load() function from this source:
2022-01-24 16:39:59 +03:00
https://github.com/OpenPLi/enigma2/blob/develop/lib/dvb/epgcache.cpp#L955
2019-04-18 23:05:19 +03:00
"""
refs = set()
2019-04-18 23:05:19 +03:00
with open(path, mode="rb") as f:
crc = struct.unpack("<I", f.read(4))[0]
if crc != int(0x98765432):
raise ValueError("Epg file has incorrect byte order!")
header = f.read(13).decode()
2022-01-24 16:39:59 +03:00
if header == "ENIGMA_EPG_V7":
epg_ver = 7
elif header == "ENIGMA_EPG_V8":
epg_ver = 8
else:
2019-04-18 23:05:19 +03:00
raise ValueError("Unsupported format of epd.dat file!")
channels_count = struct.unpack("<I", f.read(4))[0]
2022-01-24 16:39:59 +03:00
_len_read_size = 3 if epg_ver == 8 else 2
_type_read_str = f"<{'H' if epg_ver == 8 else 'B'}B"
2019-04-18 23:05:19 +03:00
for i in range(channels_count):
sid, nid, tsid, events_size = struct.unpack("<IIII", f.read(16))
2022-01-24 16:39:59 +03:00
service_id = f"{sid:X}:{tsid:X}:{nid:X}"
2019-04-18 23:05:19 +03:00
for j in range(events_size):
2022-01-24 16:39:59 +03:00
_type, _len = struct.unpack(_type_read_str, f.read(_len_read_size))
2019-04-18 23:05:19 +03:00
f.read(10)
n_crc = (_len - 10) // 4
if n_crc > 0:
[f.read(4) for n in range(n_crc)]
refs.add(service_id)
2019-04-18 23:05:19 +03:00
return refs
class ChannelsParser:
2019-04-22 20:25:19 +03:00
_COMMENT = "File was created in DemonEditor"
2019-04-18 23:05:19 +03:00
@staticmethod
def get_refs_from_xml(path):
""" Returns tuple from references and description. """
refs = []
2019-04-18 23:05:19 +03:00
dom = parse(path)
2019-04-30 14:17:45 +03:00
description = "".join(n.data + "\n" for n in dom.childNodes if n.nodeType == Node.COMMENT_NODE)
2019-04-18 23:05:19 +03:00
for elem in dom.getElementsByTagName("channels"):
c_count = 0
comment_count = 0
current_data = ""
if elem.hasChildNodes():
for n in elem.childNodes:
if n.nodeType == Node.COMMENT_NODE:
c_count += 1
comment_count += 1
txt = n.data.strip()
if comment_count:
comment_count -= 1
else:
2019-04-24 20:27:47 +03:00
ref_data = current_data.split(":")
refs.append(BouquetService(name=txt,
type=BqServiceType.DEFAULT,
data="{}:{}:{}:{}".format(*ref_data[3:7]).upper(),
num="{}:{}:{}".format(*ref_data[3:6]).upper()))
2019-04-24 20:27:47 +03:00
2019-04-18 23:05:19 +03:00
if n.hasChildNodes():
for s_node in n.childNodes:
if s_node.nodeType == Node.TEXT_NODE:
comment_count -= 1
current_data = s_node.data
2019-04-30 14:17:45 +03:00
return refs, description
2019-04-18 23:05:19 +03:00
2019-04-22 20:25:19 +03:00
@staticmethod
def write_refs_to_xml(path, services):
header = '<?xml version="1.0" encoding="utf-8"?>\n<!-- {} -->\n<!-- {} -->\n<channels>\n'.format(
2019-06-04 13:06:02 +03:00
"Created in DemonEditor.", datetime.now().strftime("%d.%m.%Y %H:%M:%S"))
2019-04-22 20:25:19 +03:00
doc = Document()
lines = [header]
for srv in services:
srv_type = srv.type
if srv_type is BqServiceType.IPTV:
channel_child = doc.createElement("channel")
channel_child.setAttribute("id", srv.name)
2019-04-22 20:25:19 +03:00
data = srv.data.strip().split(":")
channel_child.appendChild(doc.createTextNode(":".join(data[:10])))
comment = doc.createComment(srv.name)
lines.append(f"{channel_child.toxml()} {comment.toxml()}\n")
2019-04-22 20:25:19 +03:00
elif srv_type is BqServiceType.MARKER:
comment = doc.createComment(srv.name)
lines.append(f"{comment.toxml()}\n")
2019-04-22 20:25:19 +03:00
lines.append("</channels>")
doc.unlink()
with open(path, "w", encoding="utf-8") as f:
f.writelines(lines)
2019-04-18 23:05:19 +03:00
if __name__ == "__main__":
pass