diff --git a/app/connections.py b/app/connections.py index 58750413..53b57f2d 100644 --- a/app/connections.py +++ b/app/connections.py @@ -1,13 +1,14 @@ +import json import os import socket import time +import urllib from enum import Enum from ftplib import FTP, error_perm from telnetlib import Telnet from urllib.error import HTTPError, URLError from urllib.parse import urlencode from urllib.request import urlopen, HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler, build_opener, install_opener -from xml.dom.minidom import parse from app.commons import log from app.properties import Profile @@ -29,6 +30,12 @@ class DownloadType(Enum): WEBTV = 4 +class HttpRequestType(Enum): + ZAP = "zap?sRef=" + INFO = "about" + SIGNAL = "tunersignal" + + class TestException(Exception): pass @@ -71,7 +78,7 @@ def upload_data(*, properties, download_type=DownloadType.ALL, remove_unused=Fal callback=None, done_callback=None, use_http=False): data_path = properties["data_dir_path"] host = properties["host"] - base_url = "http://{}:{}/web/".format(host, properties.get("http_port", "80")) + base_url = "http://{}:{}/api/".format(host, properties.get("http_port", "80")) tn, ht = None, None # telnet, http try: @@ -216,12 +223,7 @@ def http(user, password, url, callback): while True: url = yield with urlopen(url, timeout=5) as f: - dom = parse(f) - msg = None - for elem in dom.getElementsByTagName("e2simplexmlresult"): - for ch in elem.childNodes: - if ch.nodeType == ch.ELEMENT_NODE: - msg = "".join(t.nodeValue for t in ch.childNodes if t.nodeType == t.TEXT_NODE) + msg = json.load(f).get("message", None) if msg: callback("HTTP: {}\n".format(msg)) @@ -251,6 +253,27 @@ def telnet(host, port=23, user="", password="", timeout=5): yield +# ***************** http api *******************# + +def http_request(host, port, user, password): + base_url = "http://{}:{}/api/".format(host, port) + init_auth(user, password, base_url) + while True: + req_type, ref = yield + url = base_url + if req_type is HttpRequestType.ZAP: + url = base_url + "zap?sRef={}".format(urllib.parse.quote(ref)) + elif req_type is HttpRequestType.INFO: + url = base_url + HttpRequestType.INFO.value + elif req_type is HttpRequestType.SIGNAL: + url = base_url + HttpRequestType.SIGNAL.value + + try: + with urlopen(url, timeout=5) as f: + yield json.load(f) + except (URLError, HTTPError): + yield None + # ***************** Connections testing *******************# @@ -265,18 +288,12 @@ def test_ftp(host, port, user, password, timeout=5): def test_http(host, port, user, password, timeout=5): try: params = urlencode({"text": "Connection test", "type": 2, "timeout": timeout}) - url = "http://{}:{}/web/message?{}".format(host, port, params) + url = "http://{}:{}/api/message?{}".format(host, port, params) # authentication init_auth(user, password, url) with urlopen(url, timeout=5) as f: - dom = parse(f) - msg = "" - for elem in dom.getElementsByTagName("e2simplexmlresult"): - for ch in elem.childNodes: - if ch.nodeType == ch.ELEMENT_NODE: - msg = "".join(t.nodeValue for t in ch.childNodes if t.nodeType == t.TEXT_NODE) - return msg + return json.load(f).get("message", "") except (URLError, HTTPError) as e: raise TestException(e) diff --git a/app/ui/main_app_window.py b/app/ui/main_app_window.py index e6a892c5..c8acb7ac 100644 --- a/app/ui/main_app_window.py +++ b/app/ui/main_app_window.py @@ -7,6 +7,7 @@ from functools import lru_cache from gi.repository import GLib from app.commons import run_idle, log, run_task, run_with_delay +from app.connections import http_request, HttpRequestType from app.eparser import get_blacklist, write_blacklist, parse_m3u from app.eparser import get_services, get_bouquets, write_bouquets, write_services, Bouquets, Bouquet, Service from app.eparser.ecommons import CAS, Flag @@ -161,6 +162,9 @@ class MainAppWindow: self._player = None self._full_screen = False self._drawing_area_xid = None + # http api + self._http_api = None + self._monitor_signal = False builder = Gtk.Builder() builder.set_translation_domain("demon-editor") @@ -191,9 +195,16 @@ class MainAppWindow: self._player_frame = builder.get_object("player_frame") self._header_bar = builder.get_object("header_bar") self._bq_name_label = builder.get_object("bq_name_label") + # Status bar self._ip_label = builder.get_object("ip_label") self._ip_label.set_text(self._options.get(self._profile).get("host")) self.update_profile_label() + self._receiver_info_box = builder.get_object("receiver_info_box") + self._receiver_info_label = builder.get_object("receiver_info_label") + self._signal_box = builder.get_object("signal_box") + self._service_name_label = builder.get_object("service_name_label") + self._signal_level_bar = builder.get_object("signal_level_bar") + self.init_http_api() # Dynamically active elements depending on the selected view self._tool_elements = {k: builder.get_object(k) for k in self._DYNAMIC_ELEMENTS} self._cas_label = builder.get_object("cas_label") @@ -949,6 +960,7 @@ class MainAppWindow: self.update_services_counts() self.update_profile_label() + self.init_http_api() def on_tree_view_key_press(self, view, event): """ Handling keystrokes on press """ @@ -1208,6 +1220,7 @@ class MainAppWindow: @run_idle def on_player_play(self, item=None): + self.on_zap() url = self.get_stream_url() self.update_player_buttons() if not url: @@ -1305,6 +1318,80 @@ class MainAppWindow: self._status_bar_box.set_visible(full) self._player_tool_bar.set_visible(full) + # ************************ HTTP API ****************************# + @run_task + def init_http_api(self): + if self._http_api: + self._http_api.close() + self._http_api = None + + prp = self._options.get(self._profile) + if prp is Profile.NEUTRINO_MP: + self.update_info_boxes_visible(False) + return + + self._http_api = http_request(prp.get("host", "127.0.0.1"), prp.get("http_port", "80"), + prp.get("http_user", ""), prp.get("http_password", "")) + + next(self._http_api) + GLib.timeout_add_seconds(1, self.update_receiver_info) + + @run_idle + def on_zap(self): + path, column = self._fav_view.get_cursor() + if not path or not self._http_api: + return + + row = self._fav_model[path][:] + srv = self._services.get(row[-2], None) + if srv and srv.transponder: + ref = srv.picon_id.rstrip(".png").replace("_", ":") + req = self._http_api.send((HttpRequestType.ZAP, ref)) + next(self._http_api) + if req and req.get("result", False): + GLib.timeout_add_seconds(2, self.update_service_info) + + @run_task + def update_receiver_info(self): + info = self._http_api.send((HttpRequestType.INFO, None)) + next(self._http_api) + if not info: + self._http_api.close() + self._http_api = None + GLib.idle_add(self.update_info_boxes_visible, False) + return + + service_info = info.get("service", None) + res_info = info.get("info", None) + if res_info: + image = res_info.get("friendlyimagedistro", "") + image_ver = res_info.get("imagever", "") + brand = res_info.get("brand", "") + model = res_info.get("model", "") + info_text = "{} {} Image: {} {}".format(brand, model, image, image_ver) + GLib.idle_add(self._receiver_info_label.set_text, info_text) + GLib.idle_add(self._receiver_info_box.set_visible, res_info) + + if service_info: + GLib.idle_add(self._service_name_label.set_text, service_info.get("name", "")) + GLib.timeout_add_seconds(2, self.update_signal) + GLib.idle_add(self._signal_box.set_visible, service_info) + + def update_signal(self): + sig = self._http_api.send((HttpRequestType.SIGNAL, None)) + next(self._http_api) + self._signal_level_bar.set_value(sig.get("snr", 0)) + return self._monitor_signal + + def update_service_info(self): + info = self._http_api.send((HttpRequestType.INFO, None)) + next(self._http_api) + if info: + service_info = info.get("service", None) + if service_info: + GLib.idle_add(self._service_name_label.set_text, service_info.get("name", "")) + GLib.timeout_add_seconds(1, self.update_signal) + # ***************** Filter and search *********************# def on_filter_toggled(self, toggle_button: Gtk.ToggleToolButton): @@ -1591,6 +1678,11 @@ class MainAppWindow: def get_format_version(self): return 5 if self._options.get(self._profile).get("v5_support", False) else 4 + @run_idle + def update_info_boxes_visible(self, visible): + self._signal_box.set_visible(visible) + self._receiver_info_box.set_visible(visible) + def start_app(): MainAppWindow() diff --git a/app/ui/main_window.glade b/app/ui/main_window.glade index e8fbbcc5..9c0c16cb 100644 --- a/app/ui/main_window.glade +++ b/app/ui/main_window.glade @@ -1897,7 +1897,7 @@ Author: Dmitriy Yefremov - 22 + 26 True False queue @@ -2213,7 +2213,7 @@ Author: Dmitriy Yefremov - 20 + 24 True False 2 @@ -2394,7 +2394,7 @@ Author: Dmitriy Yefremov - 20 + 24 True False 2 @@ -2485,7 +2485,34 @@ Author: Dmitriy Yefremov 24 True False - True + + + False + start + + + True + False + Receiver info + 10 + Receiver info + + + + + + False + True + 0 + + + + + True + True + 0 + + True @@ -2525,19 +2552,68 @@ Author: Dmitriy Yefremov - False + True True 5 1 + + + False + end + 5 + + + True + False + Current service + Service name + fill + 1 + + + + + + True + True + 1 + + + + + 70 + 10 + True + False + Tuner signal + center + 10 + 100 + + + False + False + end + 2 + + + + + True + True + end + 2 + + False - False + True end 1