added basic http api skeleton

This commit is contained in:
DYefremov
2018-11-17 23:19:17 +03:00
parent f9ef03b827
commit 506207f2e5
3 changed files with 207 additions and 22 deletions

View File

@@ -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)

View File

@@ -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()

View File

@@ -1897,7 +1897,7 @@ Author: Dmitriy Yefremov
</child>
<child>
<object class="GtkBox" id="services_bar_box">
<property name="height_request">22</property>
<property name="height_request">26</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="resize_mode">queue</property>
@@ -2213,7 +2213,7 @@ Author: Dmitriy Yefremov
</child>
<child>
<object class="GtkBox" id="fav_bar_box">
<property name="height_request">20</property>
<property name="height_request">24</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">2</property>
@@ -2394,7 +2394,7 @@ Author: Dmitriy Yefremov
</child>
<child>
<object class="GtkBox" id="bouquet_bar_box">
<property name="height_request">20</property>
<property name="height_request">24</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">2</property>
@@ -2485,7 +2485,34 @@ Author: Dmitriy Yefremov
<property name="height_request">24</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="homogeneous">True</property>
<child>
<object class="GtkBox" id="receiver_info_box">
<property name="can_focus">False</property>
<property name="halign">start</property>
<child>
<object class="GtkLabel" id="receiver_info_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Receiver info</property>
<property name="margin_left">10</property>
<property name="label" translatable="yes">Receiver info</property>
<attributes>
<attribute name="size" value="8000"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="ip_status_box">
<property name="visible">True</property>
@@ -2525,19 +2552,68 @@ Author: Dmitriy Yefremov
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="padding">5</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkBox" id="signal_box">
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="spacing">5</property>
<child>
<object class="GtkLabel" id="service_name_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Current service</property>
<property name="label" translatable="yes">Service name</property>
<property name="justify">fill</property>
<property name="xalign">1</property>
<attributes>
<attribute name="size" value="8000"/>
</attributes>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLevelBar" id="signal_level_bar">
<property name="width_request">70</property>
<property name="height_request">10</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Tuner signal</property>
<property name="valign">center</property>
<property name="margin_right">10</property>
<property name="max_value">100</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="pack_type">end</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">2</property>
</packing>
</child>
<style>
<class name="primary-toolbar"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">1</property>
</packing>