diff --git a/main/commons.py b/main/commons.py new file mode 100644 index 00000000..be69b658 --- /dev/null +++ b/main/commons.py @@ -0,0 +1,17 @@ +from functools import wraps +from threading import Thread + + +def run_task(func): + """ Runs function in separate thread """ + + @wraps(func) + def wrapper(*args, **kwargs): + task = Thread(target=func(*args, **kwargs)) + task.start() + + return wrapper + + +if __name__ == "__main__": + pass diff --git a/main/eparser/__constants.py b/main/eparser/__constants.py index 49618ece..35539df9 100644 --- a/main/eparser/__constants.py +++ b/main/eparser/__constants.py @@ -1,11 +1,6 @@ """ This module only for common constants """ -from collections import namedtuple from enum import Enum -Channel = namedtuple("Channel", ["service", "package", "service_type", - "ssid", "freq", "rate", "pol", "fec", - "system", "pos", "data_id", "fav_id"]) - class Type(Enum): """ Types of DVB transponders """ diff --git a/main/eparser/__init__.py b/main/eparser/__init__.py index eeda3569..4708c7a7 100644 --- a/main/eparser/__init__.py +++ b/main/eparser/__init__.py @@ -1,5 +1,5 @@ from .lamedb import get_channels, write_channels -from .bouquets import get_bouquets, get_bouquet, write_bouquet +from .bouquets import get_bouquets, write_bouquets, Bouquet, Bouquets from .satxml import get_satellites, write_satellites, Satellite, Transponder diff --git a/main/eparser/bouquets.py b/main/eparser/bouquets.py index 4a4c68ce..bb700936 100644 --- a/main/eparser/bouquets.py +++ b/main/eparser/bouquets.py @@ -1,18 +1,37 @@ """ Module for parsing bouquets """ from collections import namedtuple -__BOUQUETS_PATH = "../data/" +_BOUQUETS_PATH = "../data/" +_TV_ROOT_FILE_NAME = "bouquets.tv" +_RADIO_ROOT_FILE_NAME = "bouquets.radio" Bouquet = namedtuple("Bouquet", ["name", "type", "services"]) -Bouquets = namedtuple("Bouquets", ["name", "bouquets"]) +Bouquets = namedtuple("Bouquets", ["name", "type", "bouquets"]) def get_bouquets(path): return parse_bouquets(path, "bouquets.tv", "tv"), parse_bouquets(path, "bouquets.radio", "radio") +def write_bouquets(path, bouquets, bouquets_services): + srv_line = '#SERVICE 1:7:1:0:0:0:0:0:0:0:FROM BOUQUET "userbouquet.{}.{}" ORDER BY bouquet\n' + line = [] + + for bqs in bouquets: + line.clear() + line.append("#NAME {}\n".format(bqs.name)) + + for bq in bqs.bouquets: + line.append(srv_line.format(bq.name, bq.type)) + write_bouquet(path, bq.name, bq.services) + + with open(path + "bouquets.{}".format(bqs.type), "w") as file: + file.writelines(line) + + def write_bouquet(path, name, channels): bouquet = ["#NAME {}\n".format(name)] + for ch in channels: data_type = int(ch.data_id.split(":")[-2]) if data_type == 22: @@ -20,6 +39,7 @@ def write_bouquet(path, name, channels): elif data_type == 25: data_type = 19 bouquet.append("#SERVICE {}:0:{}:{}:0:0:0:\n".format(1, data_type, ch.fav_id)) + with open(path + "_userbouquet.{}.tv".format(name), "w") as file: file.writelines(bouquet) @@ -32,6 +52,7 @@ def get_bouquet(path, name, bq_type): for ch in chs_list.split("#SERVICE")[1:]: ch_data = ch.strip().split(":") ids.append("{}:{}:{}:{}".format(ch_data[3], ch_data[4], ch_data[5], ch_data[6])) + return ids @@ -40,13 +61,15 @@ def parse_bouquets(path, bq_name, bq_type): lines = file.readlines() bouquets = None nm_sep = "#NAME" + for line in lines: if nm_sep in line: _, _, name = line.partition(nm_sep) - bouquets = Bouquets(name.strip(), []) + bouquets = Bouquets(name.strip(), bq_type, []) if bouquets and "#SERVICE" in line: name = line.split(".")[1] - bouquets[1].append(Bouquet(name=name, type=bq_type, services=get_bouquet(path, name, bq_type))) + bouquets[2].append(Bouquet(name=name, type=bq_type, services=get_bouquet(path, name, bq_type))) + return bouquets diff --git a/main/eparser/lamedb.py b/main/eparser/lamedb.py index 257fdff2..b619dd0f 100644 --- a/main/eparser/lamedb.py +++ b/main/eparser/lamedb.py @@ -3,12 +3,18 @@ Currently implemented only for satellite channels!!! Description of format taken from here: http://www.satsupreme.com/showthread.php/194074-Lamedb-format-explained """ -from main.eparser.__constants import POLARIZATION, SYSTEM, FEC, Channel, SERVICE_TYPE +from collections import namedtuple + +from main.eparser.__constants import POLARIZATION, SYSTEM, FEC, SERVICE_TYPE _HEADER = "eDVB services /4/" _FILE_PATH = "../data/lamedb" _SEP = ":" # separator +Channel = namedtuple("Channel", ["service", "package", "service_type", + "ssid", "freq", "rate", "pol", "fec", + "system", "pos", "data_id", "fav_id"]) + def get_channels(path): return parse(path) @@ -25,6 +31,7 @@ def parse(path): transponders, sep, services = data.partition("transponders") # 1 step transponders, sep, services = services.partition("services") # 2 step services, sep, _ = services.partition("end") # 3 step + return parse_channels(services.split("\n"), transponders.split("/")) @@ -35,18 +42,19 @@ def parse_transponders(arg): tr = ar.replace("\n", "").split("\t") if len(tr) == 2: transponders[tr[0]] = tr[1] + return transponders def parse_channels(*args): """ Parsing channels """ + channels = [] transponders = parse_transponders(args[1]) srv = split(args[0], 3) if srv[0][0] == "": # remove first empty element srv.remove(srv[0]) - channels = [] for ch in srv: data = str(ch[0]).split(_SEP) sp = "0" @@ -73,6 +81,7 @@ def split(itr, size): if i % size == 0: srv.append(tuple(tmp)) tmp.clear() + return srv diff --git a/main/eparser/satxml.py b/main/eparser/satxml.py index 1256a312..d45ca42d 100644 --- a/main/eparser/satxml.py +++ b/main/eparser/satxml.py @@ -16,8 +16,7 @@ from collections import namedtuple from xml.dom.minidom import parse, Document from main.eparser.__constants import POLARIZATION, FEC, SYSTEM, MODULATION, PLS_MODE -# temporary -__XML_PATH = "../data/" +__FILE_NAME = "satellites.xml" Satellite = namedtuple("Satellite", ["name", "flags", "position", "transponders"]) @@ -59,12 +58,14 @@ def write_satellites(satellites, data_path): doc.appendChild(comment) root = doc.createElement("satellites") doc.appendChild(root) + for sat in satellites: # Create Element sat_child = doc.createElement("sat") sat_child.setAttribute("name", sat.name) sat_child.setAttribute("flags", sat.flags) sat_child.setAttribute("position", sat.position) + for tr in sat.transponders: transponder_child = doc.createElement("transponder") transponder_child.setAttribute("frequency", tr.frequency) @@ -81,7 +82,7 @@ def write_satellites(satellites, data_path): transponder_child.setAttribute("is_id", tr.is_id) sat_child.appendChild(transponder_child) root.appendChild(sat_child) - doc.writexml(open(data_path + "new_satellites.xml", "w"), + doc.writexml(open(data_path + __FILE_NAME, "w"), # indent="", addindent=" ", newl='\n', @@ -120,9 +121,11 @@ def parse_satellites(path): """ Parsing satellites from xml""" dom = parse(path) satellites = [] + for elem in dom.getElementsByTagName("sat"): if elem.hasAttributes(): satellites.append(parse_sat(elem)) + return satellites @@ -133,9 +136,4 @@ def get_key_by_value(dictionary, value): if __name__ == "__main__": - sats = get_satellites(__XML_PATH) - for sat in sats: - trnsp = sat.transponders - for tr in trnsp: - if tr.pls_mode or tr.pls_code or tr.is_id: - print(tr, sat.name) + pass diff --git a/main/ftp.py b/main/ftp.py index 9908f78b..0e26f5a2 100644 --- a/main/ftp.py +++ b/main/ftp.py @@ -13,6 +13,7 @@ def download_data(*, properties): ftp.cwd(properties["services_path"]) files = [] ftp.dir(files.append) + for file in files: name = str(file).strip() if name.endswith(__DATA_FILES_LIST): @@ -23,19 +24,23 @@ def download_data(*, properties): ftp.cwd(properties["satellites_xml_path"]) files.clear() ftp.dir(files.append) + for file in files: name = str(file).strip() xml_file = "satellites.xml" if name.endswith(xml_file): with open(save_path + xml_file, 'wb') as f: ftp.retrbinary('RETR ' + xml_file, f.write) + for name in os.listdir(save_path): print(name) + return ftp.voidcmd("NOOP") def upload_data(*, properties): load_path = properties["data_dir_path"] + for file_name in os.listdir(load_path): print(file_name) # Open the file for transfer in binary mode diff --git a/main/properties.py b/main/properties.py index 47acfa1e..09aae814 100644 --- a/main/properties.py +++ b/main/properties.py @@ -11,6 +11,7 @@ DATA_PATH = "data/" def get_config(): os.makedirs(os.path.dirname(CONFIG_PATH), exist_ok=True) # create dir if not exist os.makedirs(os.path.dirname(DATA_PATH), exist_ok=True) + if not os.path.isfile(CONFIG_FILE) or os.stat(CONFIG_FILE).st_size == 0: with open(CONFIG_FILE, "w") as default_config_file: json.dump(get_default_settings(), default_config_file) diff --git a/main/ui/main_app_window.py b/main/ui/main_app_window.py index dbddf3eb..7e0d10ad 100644 --- a/main/ui/main_app_window.py +++ b/main/ui/main_app_window.py @@ -1,7 +1,7 @@ from contextlib import suppress -from threading import Thread -from main.eparser import get_channels, get_bouquets, write_bouquet, write_channels +from main.commons import run_task +from main.eparser import get_channels, get_bouquets, write_bouquets, write_channels, Bouquets, Bouquet from main.ftp import download_data, upload_data from main.properties import get_config, write_config from . import Gtk, Gdk @@ -98,17 +98,24 @@ def on_paste(view): selection = view.get_selection() dest_index = 0 bq_selected = is_bouquet_selected() - if bq_selected: - fav_bouquet = __bouquets[bq_selected] + + if not bq_selected: + return + + fav_bouquet = __bouquets[bq_selected] model, paths = selection.get_selected_rows() + if paths: dest_index = int(paths[0][0]) + for row in __rows_buffer: dest_index += 1 model.insert(dest_index, row) fav_bouquet.insert(dest_index, row[-1]) + if model.get_name() == FAV_LIST_NAME: update_fav_num_column(model) + __rows_buffer.clear() @@ -126,11 +133,14 @@ def on_delete(item): rows = [model.get(in_itr, *[x for x in range(view.get_n_columns())]) for in_itr in itrs] bq_selected = is_bouquet_selected() fav_bouquet = None + if bq_selected: fav_bouquet = __bouquets[bq_selected] + for itr in itrs: if fav_bouquet and model_name == FAV_LIST_NAME: del fav_bouquet[int(model.get_path(itr)[0])] + if model_name == BOUQUETS_LIST_NAME: if model.iter_has_child(itr): show_message_dialog("This item is not allowed to be removed!") @@ -138,9 +148,10 @@ def on_delete(item): else: __bouquets.pop(bq_selected) model.remove(itr) + if model_name == FAV_LIST_NAME: update_fav_num_column(model) - if model_name == SERVICE_LIST_NAME: + elif model_name == SERVICE_LIST_NAME: for row in rows: # There are channels with the same parameters except for the name. # None because it can have duplicates! Need fix @@ -153,12 +164,14 @@ def on_delete(item): __fav_model.clear() if bq_selected: update_bouquet_channels(__fav_model, None, bq_selected) + return rows def on_to_fav_move(view): """ Move items from main to fav list """ selection = get_selection(view) + if selection: receive_selection(view=__fav_view, drop_info=None, data=selection) @@ -167,6 +180,7 @@ def get_selection(view): """ Creates a string from the iterators of the selected rows """ selection = view.get_selection() model, paths = selection.get_selected_rows() + if len(paths) > 0: itrs = [model.get_iter(path) for path in paths] return "{}:{}".format(",".join([model.get_string_from_iter(itr) for itr in itrs]), model.get_name()) @@ -175,20 +189,26 @@ def get_selection(view): def receive_selection(*, view, drop_info, data): """ Update fav view after data received """ bq_selected = is_bouquet_selected() + if not bq_selected: show_message_dialog("Error. No bouquet is selected!") return + model = view.get_model() dest_index = 0 + if drop_info: path, position = drop_info dest_iter = model.get_iter(path) if dest_iter: dest_index = model.get_value(dest_iter, 0) + itr_str, sep, source = data.partition(":") itrs = itr_str.split(",") + try: fav_bouquet = __bouquets[bq_selected] + if source == SERVICE_LIST_NAME: ext_model = __services_view.get_model() ext_itrs = [ext_model.get_iter_from_string(itr) for itr in itrs] @@ -246,21 +266,24 @@ def on_satellite_editor_show(model): show_satellites_dialog(__main_window, __options["data_dir_path"]) -def data_open(model): +@run_task +def on_data_open(model): try: model.clear() __fav_model.clear() model_name = model.get_name() data_path = get_config()["data_dir_path"] + if model_name == SERVICE_LIST_NAME: for ch in get_channels(data_path + "lamedb"): # adding channels to dict with fav_id as keys __channels[ch.fav_id] = ch model.append(ch) + if model_name == BOUQUETS_LIST_NAME: bouquets = get_bouquets(data_path) for bouquet in bouquets: - parent = model.append(None, [bouquet.name, None]) + parent = model.append(None, [bouquet.name, bouquet.type]) for bt in bouquet.bouquets: name, bt_type = bt.name, bt.type model.append(parent, [name, bt_type]) @@ -269,23 +292,36 @@ def data_open(model): __status_bar.push(1, getattr(e, "message", repr(e))) -def on_data_open(model): - # Maybe is not necessary? Need testing. - task = Thread(target=data_open(model)) - task.start() - - +@run_task def on_data_save(*args): # Perhaps needs a dialog to choose what we need to save!!! - bouquet_selected = is_bouquet_selected() path = __options["data_dir_path"] - if bouquet_selected and __fav_view.is_focus(): # bouquets - fav_ids = [] - __fav_model.foreach(lambda model, p, itr: fav_ids.append(model.get(model.get_iter(p), 4))) - channels = [__channels[fav_id[0]] for fav_id in fav_ids] - write_bouquet(path, bouquet_selected, channels) - elif __services_view.is_focus(): - write_channels(path, __channels.values()) + bouquets = [] + services = [] + services_model = __services_view.get_model() + s_n_columns = services_model.get_n_columns() + + def parse_bouquets(model, b_path, itr): + if model.iter_has_child(itr): + num_of_children = model.iter_n_children(itr) + bqs = [] + + for num in range(num_of_children): + bq_itr = model.iter_nth_child(itr, num) + bq_name, bq_type = model.get(bq_itr, 0, 1) + favs = __bouquets["{}:{}".format(bq_name, bq_type)] + bq = Bouquet(bq_name, bq_type, [__channels[f_id] for f_id in favs]) + bqs.append(bq) + bqs = Bouquets(*model.get(itr, 0, 1), bqs) + bouquets.append(bqs) + + # Getting bouquets + __bouquets_view.get_model().foreach(parse_bouquets) + write_bouquets(path + "tmp/", bouquets, __bouquets) + # Getting services + services_model.foreach(lambda model, s_path, itr: + services.append(model.get(itr, *[item for item in range(s_n_columns)]))) + write_channels(path + "tmp/", services) def on_services_selection(model, path, column): @@ -298,6 +334,7 @@ def on_fav_selection(model, path, column): def on_bouquets_selection(model, path, column): __fav_model.clear() + if len(path) > 1: delete_selection(__services_view) update_bouquet_channels(model, path) @@ -308,8 +345,10 @@ def update_bouquet_channels(model, path, bq_key=None): tree_iter = None if path: tree_iter = model.get_iter(path) + key = bq_key if bq_key else "{}:{}".format(*model.get(tree_iter, 0, 1)) services = __bouquets[key] + for num, ch_id in enumerate(services): channel = __channels.get(ch_id, None) if channel: @@ -323,8 +362,10 @@ def is_bouquet_selected(): """ selection = __bouquets_view.get_selection() model, path = selection.get_selected_rows() + if len(path) < 1 or model.iter_has_child(model.get_iter(path)): return False + return "{}:{}".format(*model.get(model.get_iter(path), 0, 1)) diff --git a/main/ui/satellites_dialog.py b/main/ui/satellites_dialog.py index 911dbc0d..f2c257c4 100644 --- a/main/ui/satellites_dialog.py +++ b/main/ui/satellites_dialog.py @@ -1,3 +1,4 @@ +from main.commons import run_task from main.eparser import get_satellites, write_satellites, Satellite, Transponder from . import Gtk, Gdk @@ -19,11 +20,13 @@ def show_satellites_dialog(transient, data_path): dialog.destroy() +@run_task def on_satellites_list_load(model): """ Load satellites data into model """ satellites = get_satellites(__data_path) model.clear() aggr = [None for x in range(9)] + for name, flags, pos, transponders in satellites: parent = model.append(None, [name, *aggr, flags, pos]) for transponder in transponders: @@ -38,22 +41,26 @@ def on_remove(view): model.remove(itr) +@run_task def on_save(view): model = view.get_model() satellites = [] model.foreach(parse_data, satellites) - write_satellites(satellites, __data_path) + write_satellites(satellites, __data_path + "tmp/") def parse_data(model, path, itr, sats): if model.iter_has_child(itr): num_of_children = model.iter_n_children(itr) transponders = [] + num_columns = model.get_n_columns() + for num in range(num_of_children): transponder_itr = model.iter_nth_child(itr, num) - transponder = model.get(transponder_itr, *[item for item in range(model.get_n_columns())]) + transponder = model.get(transponder_itr, *[item for item in range(num_columns)]) transponders.append(Transponder(*transponder[1:-2])) - sat = model.get(itr, *[item for item in range(model.get_n_columns())]) + + sat = model.get(itr, *[item for item in range(num_columns)]) satellite = Satellite(sat[0], sat[-2], sat[-1], transponders) sats.append(satellite)