2018-03-04 19:37:41 +03:00
|
|
|
import glob
|
2018-01-12 14:32:36 +03:00
|
|
|
import os
|
2018-04-23 14:42:41 +03:00
|
|
|
import re
|
2018-01-12 14:32:36 +03:00
|
|
|
import shutil
|
2018-04-23 14:42:41 +03:00
|
|
|
|
2018-01-15 14:56:17 +03:00
|
|
|
from collections import namedtuple
|
2018-01-12 14:32:36 +03:00
|
|
|
from html.parser import HTMLParser
|
|
|
|
|
|
2018-10-20 07:27:12 +03:00
|
|
|
from app.commons import run_task
|
2019-12-13 13:31:07 +03:00
|
|
|
from app.settings import Profile
|
2018-01-12 14:32:36 +03:00
|
|
|
|
2018-10-21 00:17:22 +03:00
|
|
|
_ENIGMA2_PICON_KEY = "{:X}:{:X}:{}"
|
2018-02-05 14:44:42 +03:00
|
|
|
_NEUTRINO_PICON_KEY = "{:x}{:04x}{:04x}.png"
|
2018-01-28 23:10:54 +03:00
|
|
|
|
2018-10-18 19:19:40 +03:00
|
|
|
Provider = namedtuple("Provider", ["logo", "name", "pos", "url", "on_id", "ssid", "single", "selected"])
|
2018-01-15 14:56:17 +03:00
|
|
|
Picon = namedtuple("Picon", ["ref", "ssid", "v_pid"])
|
|
|
|
|
|
2018-01-12 14:32:36 +03:00
|
|
|
|
|
|
|
|
class PiconsParser(HTMLParser):
|
2018-01-15 14:56:17 +03:00
|
|
|
""" Parser for package html page. (https://www.lyngsat.com/packages/*provider-name*.html) """
|
2018-01-12 14:32:36 +03:00
|
|
|
|
2018-10-18 19:19:40 +03:00
|
|
|
def __init__(self, entities=False, separator=' ', single=None):
|
2018-01-12 14:32:36 +03:00
|
|
|
|
|
|
|
|
HTMLParser.__init__(self)
|
|
|
|
|
|
|
|
|
|
self._parse_html_entities = entities
|
|
|
|
|
self._separator = separator
|
2018-10-18 19:19:40 +03:00
|
|
|
self._single = single
|
2018-01-12 14:32:36 +03:00
|
|
|
self._is_td = False
|
|
|
|
|
self._is_th = False
|
|
|
|
|
self._current_row = []
|
|
|
|
|
self._current_cell = []
|
2018-01-16 01:16:03 +03:00
|
|
|
self.picons = []
|
2018-01-12 14:32:36 +03:00
|
|
|
|
|
|
|
|
def handle_starttag(self, tag, attrs):
|
|
|
|
|
if tag == 'td':
|
|
|
|
|
self._is_td = True
|
|
|
|
|
if tag == 'th':
|
|
|
|
|
self._is_th = True
|
|
|
|
|
if tag == "img":
|
|
|
|
|
self._current_row.append(attrs[0][1])
|
|
|
|
|
|
|
|
|
|
def handle_data(self, data):
|
|
|
|
|
""" Save content to a cell """
|
|
|
|
|
if self._is_td or self._is_th:
|
|
|
|
|
self._current_cell.append(data.strip())
|
|
|
|
|
|
|
|
|
|
def handle_endtag(self, tag):
|
|
|
|
|
if tag == 'td':
|
|
|
|
|
self._is_td = False
|
|
|
|
|
elif tag == 'th':
|
|
|
|
|
self._is_th = False
|
|
|
|
|
|
|
|
|
|
if tag in ('td', 'th'):
|
|
|
|
|
final_cell = self._separator.join(self._current_cell).strip()
|
|
|
|
|
self._current_row.append(final_cell)
|
|
|
|
|
self._current_cell = []
|
|
|
|
|
elif tag == 'tr':
|
|
|
|
|
row = self._current_row
|
|
|
|
|
ln = len(row)
|
2018-01-16 01:16:03 +03:00
|
|
|
|
2018-10-18 19:19:40 +03:00
|
|
|
if self._single and ln == 4 and row[0].startswith("../../logo/"):
|
|
|
|
|
self.picons.append(Picon(row[0].strip("../"), "0", "0"))
|
|
|
|
|
else:
|
|
|
|
|
if 9 < ln < 13:
|
|
|
|
|
url = None
|
|
|
|
|
if row[0].startswith("../logo/"):
|
|
|
|
|
url = row[0]
|
|
|
|
|
elif row[1].startswith("../logo/"):
|
|
|
|
|
url = row[1]
|
|
|
|
|
|
|
|
|
|
ssid = row[-4]
|
|
|
|
|
if url and len(ssid) > 2:
|
|
|
|
|
self.picons.append(Picon(url, ssid, row[-3]))
|
2018-01-16 01:16:03 +03:00
|
|
|
|
2018-01-12 14:32:36 +03:00
|
|
|
self._current_row = []
|
|
|
|
|
|
|
|
|
|
def error(self, message):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
2018-10-18 19:19:40 +03:00
|
|
|
def parse(open_path, picons_path, tmp_path, provider, picon_ids, profile=Profile.ENIGMA_2):
|
2018-01-12 14:32:36 +03:00
|
|
|
with open(open_path, encoding="utf-8", errors="replace") as f:
|
2018-10-18 19:19:40 +03:00
|
|
|
on_id, pos, ssid, single = provider.on_id, provider.pos, provider.ssid, provider.single
|
2018-10-21 00:17:22 +03:00
|
|
|
neg_pos = pos.endswith("W")
|
|
|
|
|
pos = int("".join(c for c in pos if c.isdigit()))
|
|
|
|
|
# For negative (West) positions 3600 - numeric position value!!!
|
|
|
|
|
if neg_pos:
|
|
|
|
|
pos = 3600 - pos
|
2018-10-18 19:19:40 +03:00
|
|
|
parser = PiconsParser(single=single)
|
2018-01-12 14:32:36 +03:00
|
|
|
parser.reset()
|
|
|
|
|
parser.feed(f.read())
|
2018-01-16 01:16:03 +03:00
|
|
|
picons = parser.picons
|
|
|
|
|
if picons:
|
2018-01-12 14:32:36 +03:00
|
|
|
os.makedirs(picons_path, exist_ok=True)
|
2018-01-16 01:16:03 +03:00
|
|
|
for p in picons:
|
|
|
|
|
try:
|
2018-10-21 00:17:22 +03:00
|
|
|
if single:
|
|
|
|
|
on_id, freq = on_id.strip().split("::")
|
|
|
|
|
namespace = "{:X}{:X}".format(int(pos), int(freq))
|
|
|
|
|
else:
|
|
|
|
|
namespace = "{:X}0000".format(int(pos))
|
|
|
|
|
name = PiconsParser.format(ssid if single else p.ssid, on_id, namespace, picon_ids, profile)
|
2018-02-05 14:44:42 +03:00
|
|
|
p_name = picons_path + (name if name else os.path.basename(p.ref))
|
|
|
|
|
shutil.copyfile(tmp_path + "www.lyngsat.com/" + p.ref.lstrip("."), p_name)
|
2018-01-16 01:16:03 +03:00
|
|
|
except (TypeError, ValueError) as e:
|
2018-10-18 19:19:40 +03:00
|
|
|
msg = "Picons format parse error: {}".format(p) + "\n" + str(e)
|
|
|
|
|
# log(msg)
|
|
|
|
|
print(msg)
|
2018-01-12 14:32:36 +03:00
|
|
|
|
|
|
|
|
@staticmethod
|
2018-10-21 00:17:22 +03:00
|
|
|
def format(ssid, on_id, namespace, picon_ids, profile: Profile):
|
2018-01-12 14:32:36 +03:00
|
|
|
if profile is Profile.ENIGMA_2:
|
2018-10-21 00:17:22 +03:00
|
|
|
return picon_ids.get(_ENIGMA2_PICON_KEY.format(int(ssid), int(on_id), namespace), None)
|
2018-01-12 14:32:36 +03:00
|
|
|
elif profile is Profile.NEUTRINO_MP:
|
2018-10-20 07:27:12 +03:00
|
|
|
tr_id = int(ssid[:-2] if len(ssid) < 4 else ssid[:2])
|
2018-02-05 14:44:42 +03:00
|
|
|
return _NEUTRINO_PICON_KEY.format(tr_id, int(on_id), int(ssid))
|
2018-01-12 14:32:36 +03:00
|
|
|
else:
|
|
|
|
|
return "{}.png".format(ssid)
|
|
|
|
|
|
|
|
|
|
|
2018-01-15 14:56:17 +03:00
|
|
|
class ProviderParser(HTMLParser):
|
|
|
|
|
""" Parser for satellite html page. (https://www.lyngsat.com/*sat-name*.html) """
|
|
|
|
|
|
2018-03-07 23:56:21 +03:00
|
|
|
_POSITION_PATTERN = re.compile("at\s\d+\..*(?:E|W)']")
|
2018-10-21 00:17:22 +03:00
|
|
|
_ONID_TID_PATTERN = re.compile("^\d+-\d+.*")
|
|
|
|
|
_TRANSPONDER_FREQUENCY_PATTERN = re.compile("^\d+ [HVLR]+")
|
2018-09-19 23:02:26 +03:00
|
|
|
_DOMAIN = "https://www.lyngsat.com"
|
|
|
|
|
_TV_DOMAIN = _DOMAIN + "/tvchannels/"
|
|
|
|
|
_RADIO_DOMAIN = _DOMAIN + "/radiochannels/"
|
|
|
|
|
_PKG_DOMAIN = _DOMAIN + "/packages/"
|
2018-03-07 23:56:21 +03:00
|
|
|
|
2018-01-15 14:56:17 +03:00
|
|
|
def __init__(self, entities=False, separator=' '):
|
|
|
|
|
|
|
|
|
|
HTMLParser.__init__(self)
|
2018-10-17 21:36:02 +03:00
|
|
|
self.convert_charrefs = False
|
2018-01-15 14:56:17 +03:00
|
|
|
|
|
|
|
|
self._parse_html_entities = entities
|
|
|
|
|
self._separator = separator
|
|
|
|
|
self._is_td = False
|
|
|
|
|
self._is_th = False
|
2018-10-20 07:27:12 +03:00
|
|
|
self._is_onid_tid = False
|
2018-01-15 14:56:17 +03:00
|
|
|
self._is_provider = False
|
|
|
|
|
self._current_row = []
|
|
|
|
|
self._current_cell = []
|
|
|
|
|
self.rows = []
|
|
|
|
|
self._ids = set()
|
2018-09-19 23:02:26 +03:00
|
|
|
self._prv_names = set()
|
2018-02-04 18:09:37 +03:00
|
|
|
self._positon = None
|
2018-10-20 07:27:12 +03:00
|
|
|
self._on_id = None
|
2018-10-21 00:17:22 +03:00
|
|
|
self._freq = None
|
2018-01-15 14:56:17 +03:00
|
|
|
|
|
|
|
|
def handle_starttag(self, tag, attrs):
|
|
|
|
|
if tag == 'td':
|
|
|
|
|
self._is_td = True
|
|
|
|
|
if tag == 'tr':
|
|
|
|
|
self._is_th = True
|
|
|
|
|
if tag == "img":
|
|
|
|
|
if attrs[0][1].startswith("logo/"):
|
|
|
|
|
self._current_row.append(attrs[0][1])
|
|
|
|
|
if tag == "a":
|
2018-09-19 23:02:26 +03:00
|
|
|
url = attrs[0][1]
|
|
|
|
|
if url.startswith((self._PKG_DOMAIN, self._TV_DOMAIN, self._RADIO_DOMAIN)):
|
|
|
|
|
self._current_row.append(url)
|
2018-10-20 07:27:12 +03:00
|
|
|
if tag == "font" and len(attrs) == 1:
|
|
|
|
|
atr = attrs[0]
|
|
|
|
|
if len(atr) == 2 and atr[1] == "darkgreen":
|
|
|
|
|
self._is_onid_tid = True
|
2018-01-15 14:56:17 +03:00
|
|
|
|
|
|
|
|
def handle_data(self, data):
|
|
|
|
|
""" Save content to a cell """
|
|
|
|
|
if self._is_td or self._is_th:
|
|
|
|
|
self._current_cell.append(data.strip())
|
2018-10-20 07:27:12 +03:00
|
|
|
if self._is_onid_tid:
|
2018-10-21 00:17:22 +03:00
|
|
|
m = self._ONID_TID_PATTERN.match(data)
|
|
|
|
|
if m:
|
|
|
|
|
self._on_id, tid = m.group().split("-")
|
2018-10-20 07:27:12 +03:00
|
|
|
self._is_onid_tid = False
|
2018-01-15 14:56:17 +03:00
|
|
|
|
|
|
|
|
def handle_endtag(self, tag):
|
|
|
|
|
if tag == 'td':
|
|
|
|
|
self._is_td = False
|
|
|
|
|
elif tag == 'tr':
|
|
|
|
|
self._is_th = False
|
|
|
|
|
|
|
|
|
|
if tag in ('td', 'th'):
|
|
|
|
|
final_cell = self._separator.join(self._current_cell).strip()
|
|
|
|
|
self._current_row.append(final_cell)
|
|
|
|
|
self._current_cell = []
|
|
|
|
|
elif tag == 'tr':
|
2018-09-19 23:02:26 +03:00
|
|
|
r = self._current_row
|
2018-02-04 18:09:37 +03:00
|
|
|
# Satellite position
|
2018-03-07 23:56:21 +03:00
|
|
|
if not self._positon:
|
2018-09-19 23:02:26 +03:00
|
|
|
pos = re.findall(self._POSITION_PATTERN, str(r))
|
2018-03-07 23:56:21 +03:00
|
|
|
if pos:
|
|
|
|
|
self._positon = "".join(c for c in str(pos) if c.isdigit() or c in ".EW")
|
2018-02-04 18:09:37 +03:00
|
|
|
|
2018-09-19 23:02:26 +03:00
|
|
|
len_row = len(r)
|
2018-10-21 00:17:22 +03:00
|
|
|
if len_row > 2:
|
|
|
|
|
m = self._TRANSPONDER_FREQUENCY_PATTERN.match(r[1])
|
|
|
|
|
if m:
|
|
|
|
|
self._freq = m.group().split()[0]
|
2018-09-19 23:02:26 +03:00
|
|
|
|
|
|
|
|
if len_row == 12:
|
2018-10-18 19:19:40 +03:00
|
|
|
# Providers
|
2018-09-19 23:02:26 +03:00
|
|
|
name = r[5]
|
|
|
|
|
self._prv_names.add(name)
|
2018-10-21 00:17:22 +03:00
|
|
|
m = self._ONID_TID_PATTERN.match(str(r[-2]))
|
|
|
|
|
if m:
|
|
|
|
|
on_id, tid = m.group().split("-")
|
|
|
|
|
if on_id not in self._ids:
|
|
|
|
|
r[-2] = on_id
|
|
|
|
|
self._ids.add(on_id)
|
|
|
|
|
r[0] = self._positon
|
|
|
|
|
if name + on_id not in self._prv_names:
|
|
|
|
|
self._prv_names.add(name + on_id)
|
|
|
|
|
self.rows.append(Provider(logo=r[2], name=name, pos=self._positon, url=r[6], on_id=on_id,
|
|
|
|
|
ssid=None, single=False, selected=True))
|
2018-10-17 21:36:02 +03:00
|
|
|
elif 6 < len_row < 10:
|
2018-10-18 19:19:40 +03:00
|
|
|
# Single services
|
|
|
|
|
name, url, ssid = None, None, None
|
2018-10-17 21:36:02 +03:00
|
|
|
if r[0].startswith("http"):
|
2018-10-18 19:19:40 +03:00
|
|
|
name, url, ssid = r[1], r[0], r[4]
|
2018-10-17 21:36:02 +03:00
|
|
|
elif r[1].startswith("http"):
|
2018-10-18 19:19:40 +03:00
|
|
|
name, url, ssid = r[2], r[1], r[5]
|
2018-10-21 00:17:22 +03:00
|
|
|
|
2018-10-18 19:19:40 +03:00
|
|
|
if name and url:
|
2018-10-21 00:17:22 +03:00
|
|
|
on_id = "{}::{}".format(self._on_id if self._on_id else "1", self._freq)
|
|
|
|
|
self.rows.append(Provider(logo=None, name=name, pos=self._positon, url=url, on_id=on_id,
|
2018-10-20 07:27:12 +03:00
|
|
|
ssid=ssid, single=True, selected=False))
|
2018-09-19 23:02:26 +03:00
|
|
|
|
2018-01-15 14:56:17 +03:00
|
|
|
self._current_row = []
|
|
|
|
|
|
|
|
|
|
def error(self, message):
|
|
|
|
|
pass
|
|
|
|
|
|
2018-02-05 14:44:42 +03:00
|
|
|
def reset(self):
|
|
|
|
|
super().reset()
|
2018-02-04 18:09:37 +03:00
|
|
|
|
2018-01-15 14:56:17 +03:00
|
|
|
|
|
|
|
|
def parse_providers(open_path):
|
2018-02-05 14:44:42 +03:00
|
|
|
parser = ProviderParser()
|
|
|
|
|
parser.reset()
|
|
|
|
|
|
2018-01-15 14:56:17 +03:00
|
|
|
with open(open_path, encoding="utf-8", errors="replace") as f:
|
|
|
|
|
parser.feed(f.read())
|
|
|
|
|
|
2018-09-19 23:02:26 +03:00
|
|
|
return parser.rows
|
2018-01-15 14:56:17 +03:00
|
|
|
|
|
|
|
|
|
2018-03-04 19:37:41 +03:00
|
|
|
@run_task
|
|
|
|
|
def convert_to(src_path, dest_path, profile, callback, done_callback):
|
|
|
|
|
""" Converts names format of picons.
|
|
|
|
|
|
|
|
|
|
Copies resulting files from src to dest and writes state to callback.
|
|
|
|
|
"""
|
|
|
|
|
pattern = "/*_0_0_0.png" if profile is Profile.ENIGMA_2 else "/*.png"
|
|
|
|
|
for file in glob.glob(src_path + pattern):
|
|
|
|
|
base_name = os.path.basename(file)
|
|
|
|
|
pic_data = base_name.rstrip(".png").split("_")
|
|
|
|
|
dest_file = _NEUTRINO_PICON_KEY.format(int(pic_data[4], 16), int(pic_data[5], 16), int(pic_data[3], 16))
|
|
|
|
|
dest = "{}/{}".format(dest_path, dest_file)
|
|
|
|
|
callback('Converting "{}" to "{}"\n'.format(base_name, dest_file))
|
|
|
|
|
shutil.copyfile(file, dest)
|
|
|
|
|
|
|
|
|
|
done_callback()
|
|
|
|
|
|
|
|
|
|
|
2018-01-12 14:32:36 +03:00
|
|
|
if __name__ == "__main__":
|
|
|
|
|
pass
|