mirror of
https://github.com/DYefremov/DemonEditor.git
synced 2026-05-08 18:07:05 +02:00
Compare commits
111 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1489b3ba4f | ||
|
|
e871e88f46 | ||
|
|
a1dada9a55 | ||
|
|
bfcab961d5 | ||
|
|
c3f4390d11 | ||
|
|
dce3f0104d | ||
|
|
0c4c0da17f | ||
|
|
1dec2c8fc1 | ||
|
|
6eb112eb38 | ||
|
|
8307f153cd | ||
|
|
4796a558bb | ||
|
|
61d257f801 | ||
|
|
af74e7c32c | ||
|
|
bf474ee8d0 | ||
|
|
26e6c40a1b | ||
|
|
5723f29b60 | ||
|
|
bc65e1b446 | ||
|
|
1842bec2aa | ||
|
|
9cc33b9a33 | ||
|
|
c8fefae571 | ||
|
|
6d6616425c | ||
|
|
cb4ed7ebd1 | ||
|
|
a51929068a | ||
|
|
dff2071fa3 | ||
|
|
fdd2d61a28 | ||
|
|
1532b213e4 | ||
|
|
d2b76b08e1 | ||
|
|
af40443730 | ||
|
|
62d9e21433 | ||
|
|
ddfd3db7fa | ||
|
|
a50a7c426e | ||
|
|
e93760b2ac | ||
|
|
4a80da7515 | ||
|
|
962db5f736 | ||
|
|
1c0ca0dbeb | ||
|
|
40faa4029d | ||
|
|
86351c61ae | ||
|
|
1b56899636 | ||
|
|
b92a7f1bdb | ||
|
|
7b1db69867 | ||
|
|
8ae34fa6e6 | ||
|
|
a507f9d401 | ||
|
|
ac724fc36f | ||
|
|
90564859b2 | ||
|
|
562a5e5c6d | ||
|
|
49605b87f9 | ||
|
|
27bdac7b4f | ||
|
|
b92c02fd63 | ||
|
|
aec3874e0b | ||
|
|
08a31e9aa0 | ||
|
|
8850ff910c | ||
|
|
5f79f5b523 | ||
|
|
62bf6eadac | ||
|
|
c7575c4646 | ||
|
|
57ae0c8d53 | ||
|
|
c37838d2c0 | ||
|
|
fa9949d562 | ||
|
|
bf54187f28 | ||
|
|
696bce8201 | ||
|
|
5d01bd5479 | ||
|
|
403426ba75 | ||
|
|
43a884159b | ||
|
|
f925aa5642 | ||
|
|
8bdbe45a57 | ||
|
|
b2a24974a3 | ||
|
|
9dd6633c6a | ||
|
|
1dc5461f90 | ||
|
|
15418d234d | ||
|
|
0ac439cc84 | ||
|
|
b4eda74c6d | ||
|
|
35d598f1f4 | ||
|
|
d2272e5715 | ||
|
|
252c2245f7 | ||
|
|
0e3d5df4bf | ||
|
|
e476c26bb5 | ||
|
|
fb0996f94e | ||
|
|
1da72666d7 | ||
|
|
f790f7d0b5 | ||
|
|
76a0f43485 | ||
|
|
0dcbf98d1f | ||
|
|
bc0eb37775 | ||
|
|
d494702257 | ||
|
|
db60217474 | ||
|
|
a399660a15 | ||
|
|
2eeb53537a | ||
|
|
ab5f98a2b6 | ||
|
|
6d92ed667f | ||
|
|
ff123b579f | ||
|
|
f9a66f8f75 | ||
|
|
32f33815c2 | ||
|
|
ea9ea98e1a | ||
|
|
9c32d24a20 | ||
|
|
25f483d760 | ||
|
|
d9e471eaec | ||
|
|
e4f8a075f4 | ||
|
|
0a3dc8f79d | ||
|
|
ad69df0b63 | ||
|
|
2a3a9e124b | ||
|
|
8afd1e8a80 | ||
|
|
ee8cc5b139 | ||
|
|
bed490f491 | ||
|
|
620ff4bd60 | ||
|
|
99ecb0f22e | ||
|
|
31603bfd41 | ||
|
|
abd803a58c | ||
|
|
f84e77cbce | ||
|
|
170c8ffc55 | ||
|
|
bf3ba96fb9 | ||
|
|
b3e057a5a3 | ||
|
|
78c07f3934 | ||
|
|
249a49aff5 |
@@ -8,4 +8,4 @@ Exec=bash -c 'cd $(dirname %k) && ./start.py'
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Utility;Application;
|
||||
StartupNotify=true
|
||||
StartupNotify=false
|
||||
|
||||
12
README.md
12
README.md
@@ -4,7 +4,6 @@
|
||||
Experimental support of Neutrino-MP or others on the same basis (BPanther, etc).
|
||||
Focused on the convenience of working in lists from the keyboard. The mouse is also fully supported (Drag and Drop etc)
|
||||
### Keyboard shortcuts:
|
||||
**Ctrl + X, C, V, Up, Down, PageUp, PageDown, Home, End, S, T, E, L, H, Space; Insert, Delete, F2, Enter, P.**
|
||||
* **Insert** - copies the selected channels from the main list to the bouquet or inserts (creates) a new bouquet.
|
||||
* **Ctrl + X** - only in bouquet list. **Ctrl + C** - only in services list.
|
||||
Clipboard is **"rubber"**. There is an accumulation before the insertion!
|
||||
@@ -13,8 +12,7 @@ Clipboard is **"rubber"**. There is an accumulation before the insertion!
|
||||
* **Ctrl + S, T** in Satellites edit tool for create satellite or transponder.
|
||||
* **Ctrl + L** - parental lock.
|
||||
* **Ctrl + H** - hide/skip.
|
||||
* **P** - enable/disable preview mode for IPTV in the bouquet list.
|
||||
* **Enter** - start play IPTV or other stream in the bouquet list.
|
||||
* **Ctrl + P** - start play IPTV or other stream in the bouquet list.
|
||||
* **Space** - select/deselect.
|
||||
* **Left/Right** - remove selection.
|
||||
* **Ctrl + Up, Down, PageUp, PageDown, Home, End** - move selected items in the list.
|
||||
@@ -22,14 +20,14 @@ Clipboard is **"rubber"**. There is an accumulation before the insertion!
|
||||
* Multiple selections in lists only with Space key (as in file managers).
|
||||
* Ability to import IPTV into bouquet (Neutrino WEBTV) from m3u files.
|
||||
* Ability to download picons and update satellites (transponders) from web.
|
||||
* Preview (playing) IPTV or other streams directly from the bouquet list(should be installed VLC).
|
||||
* Preview (playing) IPTV or other streams directly from the bouquet list(should be installed VLC).
|
||||
### Minimum requirements:
|
||||
Python >= 3.5.2 and GTK+ 3 with PyGObject bindings.
|
||||
Python >= 3.5.2 and GTK+ >= 3.16 with PyGObject bindings.
|
||||
#### Note.
|
||||
To create a simple debian package, you can use the build-deb.sh
|
||||
|
||||
Tests only in image based on OpenPLi or last BPanther(neutrino) images with GM 990 Spark Reloaded receiver
|
||||
in my preferred linux distro (Last Linux Mint 18.* - MATE 64-bit)!
|
||||
Tests only with openATV image and Formuler F1 receiver in my preferred Linux distros
|
||||
(latest Linux Mint 18.* and 19 MATE 64-bit)!
|
||||
|
||||
**Terrestrial and cable channels at the moment are not supported!**
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import logging
|
||||
from functools import wraps
|
||||
from threading import Thread
|
||||
from threading import Thread, Timer
|
||||
|
||||
from gi.repository import GLib
|
||||
|
||||
@@ -43,5 +43,31 @@ def run_task(func):
|
||||
return wrapper
|
||||
|
||||
|
||||
def run_with_delay(timeout=5):
|
||||
""" Starts the function with a delay.
|
||||
|
||||
If the previous timer still works, it will canceled!
|
||||
"""
|
||||
|
||||
def run_with(func):
|
||||
timer = None
|
||||
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
nonlocal timer
|
||||
if timer and timer.is_alive():
|
||||
timer.cancel()
|
||||
|
||||
def run():
|
||||
GLib.idle_add(func, *args, **kwargs, priority=GLib.PRIORITY_LOW)
|
||||
|
||||
timer = Timer(interval=timeout, function=run)
|
||||
timer.start()
|
||||
|
||||
return wrapper
|
||||
|
||||
return run_with
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pass
|
||||
|
||||
@@ -114,7 +114,7 @@ FEC_DEFAULT = {"0": "Auto", "1": "1/2", "2": "2/3", "3": "3/4", "4": "5/6", "5":
|
||||
|
||||
SYSTEM = {"0": "DVB-S", "1": "DVB-S2"}
|
||||
|
||||
MODULATION = {"0": "Auto", "1": "QPSK", "2": "8PSK", "3": "16APSK", "5": "32APSK"}
|
||||
MODULATION = {"0": "Auto", "1": "QPSK", "2": "8PSK", "4": "16APSK", "5": "32APSK"}
|
||||
|
||||
SERVICE_TYPE = {"-2": "Data", "1": "TV", "2": "Radio", "3": "Data", "10": "Radio", "22": "TV (H264)",
|
||||
"25": "TV (HD)", "31": "TV (UHD)"}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
""" Module for parsing bouquets """
|
||||
import re
|
||||
|
||||
from app.eparser.ecommons import BqServiceType, BouquetService, Bouquets, Bouquet, BqType
|
||||
|
||||
_TV_ROOT_FILE_NAME = "bouquets.tv"
|
||||
@@ -13,32 +15,35 @@ def get_bouquets(path):
|
||||
def write_bouquets(path, bouquets):
|
||||
srv_line = '#SERVICE 1:7:1:0:0:0:0:0:0:0:FROM BOUQUET "userbouquet.{}.{}" ORDER BY bouquet\n'
|
||||
line = []
|
||||
pattern = re.compile("[^\w_()]+")
|
||||
|
||||
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.replace(" ", "_"), bq.type))
|
||||
write_bouquet(path, bq.name, bq.type, bq.services)
|
||||
bq_name = re.sub(pattern, "_", bq.name)
|
||||
line.append(srv_line.format(bq_name, bq.type))
|
||||
write_bouquet(path + "userbouquet.{}.{}".format(bq_name, bq.type), bq.name, bq.services)
|
||||
|
||||
with open(path + "bouquets.{}".format(bqs.type), "w", encoding="utf-8") as file:
|
||||
file.writelines(line)
|
||||
|
||||
|
||||
def write_bouquet(path, name, bq_type, channels):
|
||||
def write_bouquet(path, name, channels):
|
||||
bouquet = ["#NAME {}\n".format(name)]
|
||||
|
||||
for ch in channels:
|
||||
if not ch: # if was duplicate
|
||||
continue
|
||||
|
||||
if ch.service_type == BqServiceType.IPTV.name or ch.service_type == BqServiceType.MARKER.name:
|
||||
bouquet.append("#SERVICE {}\n".format(ch.fav_id.strip()))
|
||||
else:
|
||||
bouquet.append("#SERVICE {}\n".format(to_bouquet_id(ch)))
|
||||
data = to_bouquet_id(ch)
|
||||
if ch.service:
|
||||
bouquet.append("#SERVICE {}:{}\n#DESCRIPTION {}\n".format(data, ch.service, ch.service))
|
||||
else:
|
||||
bouquet.append("#SERVICE {}\n".format(data))
|
||||
|
||||
with open(path + "userbouquet.{}.{}".format(name.replace(" ", "_"), bq_type), "w", encoding="utf-8") as file:
|
||||
with open(path, "w", encoding="utf-8") as file:
|
||||
file.writelines(bouquet)
|
||||
|
||||
|
||||
@@ -65,7 +70,10 @@ def get_bouquet(path, name, bq_type):
|
||||
services.append(BouquetService(ch_data[-1].split("\n")[0], BqServiceType.IPTV, ch, 0))
|
||||
else:
|
||||
fav_id = "{}:{}:{}:{}".format(ch_data[3], ch_data[4], ch_data[5], ch_data[6])
|
||||
services.append(BouquetService(None, BqServiceType.DEFAULT, fav_id, 0))
|
||||
name = None
|
||||
if len(ch_data) == 12:
|
||||
name, desc = str(ch_data[-1]).split("\n#DESCRIPTION")
|
||||
services.append(BouquetService(name, BqServiceType.DEFAULT, fav_id, 0))
|
||||
|
||||
return srvs[0].strip("#NAME").strip(), services
|
||||
|
||||
|
||||
@@ -59,7 +59,11 @@ def write_to_lamedb5(path, services):
|
||||
data_id = str(srv.data_id).split(_SEP)
|
||||
tr_id = "{}:{}:{}".format(data_id[1], data_id[2], data_id[3])
|
||||
tr_set.add("t:{},{}\n".format(tr_id, srv.transponder.replace(" ", ":", 1)))
|
||||
services_lines.append("s:{},\"{}\",{}\n".format(srv.data_id, srv.service, srv.flags_cas))
|
||||
# Removing empty packages
|
||||
flags = list(filter(lambda x: x != "p:", srv.flags_cas.split(",")))
|
||||
flags = ",".join(flags)
|
||||
flags = "," + flags if flags else ""
|
||||
services_lines.append("s:{},\"{}\"{}\n".format(srv.data_id, srv.service, flags))
|
||||
|
||||
lines.extend(sorted(tr_set))
|
||||
lines.extend(services_lines)
|
||||
@@ -109,7 +113,12 @@ def parse_v5(path):
|
||||
for l in lns:
|
||||
if l.startswith("s:"):
|
||||
srv_data = l.strip("s:").split(",", 2)
|
||||
srv_data[1], srv_data[2] = srv_data[1].strip("\""), srv_data[2].strip()
|
||||
srv_data[1] = srv_data[1].strip("\"")
|
||||
data_len = len(srv_data)
|
||||
if data_len == 3:
|
||||
srv_data[2] = srv_data[2].strip()
|
||||
elif data_len == 2:
|
||||
srv_data.append("p:")
|
||||
srvs.extend(srv_data)
|
||||
elif l.startswith("t:"):
|
||||
tr, srv = l.split(",")
|
||||
@@ -160,10 +169,14 @@ def parse_services(services, transponders, path):
|
||||
locked = LOCKED_ICON if fav_id in blacklist else None
|
||||
|
||||
package = list(filter(lambda x: x.startswith("p:"), all_flags))
|
||||
package = package[0][2:] if package else None
|
||||
package = package[0][2:] if package else ""
|
||||
|
||||
if transponder is not None:
|
||||
tr_type, sp, tr = str(transponder).partition(" ")
|
||||
# Skipping terrestrial and cable channels
|
||||
if tr_type in "tc":
|
||||
continue
|
||||
|
||||
tr = tr.split(_SEP)
|
||||
service_type = SERVICE_TYPE.get(data[4], SERVICE_TYPE["-2"])
|
||||
# removing all non printable symbols!
|
||||
|
||||
@@ -29,7 +29,7 @@ def parse_m3u(path, profile):
|
||||
elif count == 1:
|
||||
count = 0
|
||||
if profile is Profile.ENIGMA_2:
|
||||
fav_id = ENIGMA2_FAV_ID_FORMAT.format(StreamType.DVB_TS.value, 1, 0, 0, 0, 0,
|
||||
fav_id = ENIGMA2_FAV_ID_FORMAT.format(StreamType.NONE_TS.value, 1, 0, 0, 0, 0,
|
||||
line.strip().replace(":", "%3a"), name, name, None)
|
||||
elif profile is Profile.NEUTRINO_MP:
|
||||
fav_id = NEUTRINO_FAV_ID_FORMAT.format(line.strip(), "", 0, None, None, None, None, "", "", 1)
|
||||
|
||||
@@ -7,6 +7,7 @@ from xml.dom.minidom import parse, Document
|
||||
|
||||
import os
|
||||
|
||||
from app.commons import log
|
||||
from .ecommons import POLARIZATION, FEC, SYSTEM, MODULATION, PLS_MODE, Transponder, Satellite, get_key_by_value
|
||||
|
||||
__COMMENT = (" File was created in DemonEditor\n\n"
|
||||
@@ -21,7 +22,7 @@ __COMMENT = (" File was created in DemonEditor\n\n"
|
||||
"polarization: 0 - Horizontal, 1 - Vertical, 2 - Left Circular, 3 - Right Circular\n"
|
||||
"fec_inner: 0 - Auto, 1 - 1/2, 2 - 2/3, 3 - 3/4, 4 - 5/6, 5 - 7/8, 6 - 8/9, 7 - 3/5,\n"
|
||||
"8 - 4/5, 9 - 9/10, 15 - None\n"
|
||||
"modulation: 0 - Auto, 1 - QPSK, 2 - 8PSK, 3 - 16APSK, 5 - 32APSK\n"
|
||||
"modulation: 0 - Auto, 1 - QPSK, 2 - 8PSK, 4 - 16APSK, 5 - 32APSK\n"
|
||||
"rolloff: 0 - 0.35, 1 - 0.25, 2 - 0.20, 3 - Auto\n"
|
||||
"pilot: 0 - Off, 1 - On, 2 - Auto\n"
|
||||
"inversion: 0 = Off, 1 = On, 2 = Auto (default)\n"
|
||||
@@ -74,31 +75,38 @@ def write_satellites(satellites, data_path):
|
||||
doc.unlink()
|
||||
|
||||
|
||||
def parse_transponders(elem):
|
||||
def parse_transponders(elem, sat_name):
|
||||
""" Parsing satellite transponders """
|
||||
transponders = []
|
||||
for el in elem.getElementsByTagName("transponder"):
|
||||
if el.hasAttributes():
|
||||
atr = el.attributes
|
||||
tr = Transponder(atr["frequency"].value,
|
||||
atr["symbol_rate"].value,
|
||||
POLARIZATION[atr["polarization"].value],
|
||||
FEC[atr["fec_inner"].value],
|
||||
SYSTEM[atr["system"].value],
|
||||
MODULATION[atr["modulation"].value],
|
||||
PLS_MODE[atr["pls_mode"].value] if "pls_mode" in atr else None,
|
||||
atr["pls_code"].value if "pls_code" in atr else None,
|
||||
atr["is_id"].value if "is_id" in atr else None)
|
||||
transponders.append(tr)
|
||||
try:
|
||||
tr = Transponder(atr["frequency"].value,
|
||||
atr["symbol_rate"].value,
|
||||
POLARIZATION[atr["polarization"].value],
|
||||
FEC[atr["fec_inner"].value],
|
||||
SYSTEM[atr["system"].value],
|
||||
MODULATION[atr["modulation"].value],
|
||||
PLS_MODE[atr["pls_mode"].value] if "pls_mode" in atr else None,
|
||||
atr["pls_code"].value if "pls_code" in atr else None,
|
||||
atr["is_id"].value if "is_id" in atr else None)
|
||||
except Exception as e:
|
||||
message = "Error: can't parse transponder for '{}' satellite! {}".format(sat_name, repr(e))
|
||||
print(message)
|
||||
log(message)
|
||||
else:
|
||||
transponders.append(tr)
|
||||
return transponders
|
||||
|
||||
|
||||
def parse_sat(elem):
|
||||
""" Parsing satellite """
|
||||
return Satellite(elem.attributes["name"].value,
|
||||
sat_name = elem.attributes["name"].value
|
||||
return Satellite(sat_name,
|
||||
elem.attributes["flags"].value,
|
||||
elem.attributes["position"].value,
|
||||
parse_transponders(elem))
|
||||
parse_transponders(elem, sat_name))
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
|
||||
41
app/ftp.py
41
app/ftp.py
@@ -5,17 +5,17 @@ from enum import Enum
|
||||
from ftplib import FTP, error_perm
|
||||
from telnetlib import Telnet
|
||||
|
||||
from app.commons import log
|
||||
from app.commons import log, run_task
|
||||
from app.properties import Profile
|
||||
|
||||
__DATA_FILES_LIST = ("tv", "radio", "lamedb", "lamedb5", "blacklist", "whitelist", # enigma 2
|
||||
"services.xml", "myservices.xml", "bouquets.xml", "ubouquets.xml") # neutrino
|
||||
|
||||
_SATELLITES_XML_FILE = "satellites.xml"
|
||||
_SAT_XML_FILE = "satellites.xml"
|
||||
_WEBTV_XML_FILE = "webtv.xml"
|
||||
|
||||
|
||||
class DownloadDataType(Enum):
|
||||
class DownloadType(Enum):
|
||||
ALL = 0
|
||||
BOUQUETS = 1
|
||||
SATELLITES = 2
|
||||
@@ -23,7 +23,7 @@ class DownloadDataType(Enum):
|
||||
WEBTV = 4
|
||||
|
||||
|
||||
def download_data(*, properties, download_type=DownloadDataType.ALL, callback=None):
|
||||
def download_data(*, properties, download_type=DownloadType.ALL, callback=None):
|
||||
with FTP(host=properties["host"]) as ftp:
|
||||
ftp.login(user=properties["user"], passwd=properties["password"])
|
||||
ftp.encoding = "utf-8"
|
||||
@@ -31,7 +31,7 @@ def download_data(*, properties, download_type=DownloadDataType.ALL, callback=No
|
||||
os.makedirs(os.path.dirname(save_path), exist_ok=True)
|
||||
files = []
|
||||
# bouquets section
|
||||
if download_type is DownloadDataType.ALL or download_type is DownloadDataType.BOUQUETS:
|
||||
if download_type is DownloadType.ALL or download_type is DownloadType.BOUQUETS:
|
||||
ftp.cwd(properties["services_path"])
|
||||
ftp.dir(files.append)
|
||||
|
||||
@@ -41,25 +41,24 @@ def download_data(*, properties, download_type=DownloadDataType.ALL, callback=No
|
||||
name = name.split()[-1]
|
||||
download_file(ftp, name, save_path)
|
||||
# satellites.xml and webtv section
|
||||
if download_type in (DownloadDataType.ALL, DownloadDataType.SATELLITES, DownloadDataType.WEBTV):
|
||||
if download_type in (DownloadType.ALL, DownloadType.SATELLITES, DownloadType.WEBTV):
|
||||
ftp.cwd(properties["satellites_xml_path"])
|
||||
files.clear()
|
||||
ftp.dir(files.append)
|
||||
|
||||
for file in files:
|
||||
name = str(file).strip()
|
||||
if download_type in (DownloadDataType.ALL, DownloadDataType.SATELLITES):
|
||||
if name.endswith(_SATELLITES_XML_FILE):
|
||||
download_file(ftp, _SATELLITES_XML_FILE, save_path)
|
||||
elif download_type in (DownloadDataType.ALL, DownloadDataType.WEBTV):
|
||||
if name.endswith(_WEBTV_XML_FILE):
|
||||
download_file(ftp, _WEBTV_XML_FILE, save_path)
|
||||
if download_type in (DownloadType.ALL, DownloadType.SATELLITES) and name.endswith(_SAT_XML_FILE):
|
||||
download_file(ftp, _SAT_XML_FILE, save_path)
|
||||
if download_type in (DownloadType.ALL, DownloadType.WEBTV) and name.endswith(_WEBTV_XML_FILE):
|
||||
download_file(ftp, _WEBTV_XML_FILE, save_path)
|
||||
|
||||
if callback is not None:
|
||||
callback()
|
||||
|
||||
|
||||
def upload_data(*, properties, download_type=DownloadDataType.ALL, remove_unused=False, profile=Profile.ENIGMA_2,
|
||||
@run_task
|
||||
def upload_data(*, properties, download_type=DownloadType.ALL, remove_unused=False, profile=Profile.ENIGMA_2,
|
||||
callback=None):
|
||||
data_path = properties["data_dir_path"]
|
||||
host = properties["host"]
|
||||
@@ -74,25 +73,25 @@ def upload_data(*, properties, download_type=DownloadDataType.ALL, remove_unused
|
||||
ftp.login(user=properties["user"], passwd=properties["password"])
|
||||
ftp.encoding = "utf-8"
|
||||
|
||||
if download_type is DownloadDataType.ALL or download_type is DownloadDataType.SATELLITES:
|
||||
if download_type is DownloadType.ALL or download_type is DownloadType.SATELLITES:
|
||||
ftp.cwd(properties["satellites_xml_path"])
|
||||
send = send_file(_SATELLITES_XML_FILE, data_path, ftp)
|
||||
if download_type is DownloadDataType.SATELLITES:
|
||||
send = send_file(_SAT_XML_FILE, data_path, ftp)
|
||||
if download_type is DownloadType.SATELLITES:
|
||||
tn.send("init 3" if profile is Profile.ENIGMA_2 else "init 6")
|
||||
if callback is not None:
|
||||
callback()
|
||||
return send
|
||||
|
||||
if profile is Profile.NEUTRINO_MP and download_type in (DownloadDataType.ALL, DownloadDataType.WEBTV):
|
||||
if profile is Profile.NEUTRINO_MP and download_type in (DownloadType.ALL, DownloadType.WEBTV):
|
||||
ftp.cwd(properties["satellites_xml_path"])
|
||||
send = send_file(_WEBTV_XML_FILE, data_path, ftp)
|
||||
if download_type is DownloadDataType.WEBTV:
|
||||
if download_type is DownloadType.WEBTV:
|
||||
tn.send("init 6")
|
||||
if callback is not None:
|
||||
callback()
|
||||
return send
|
||||
|
||||
if download_type is DownloadDataType.ALL or download_type is DownloadDataType.BOUQUETS:
|
||||
if download_type is DownloadType.ALL or download_type is DownloadType.BOUQUETS:
|
||||
ftp.cwd(properties["services_path"])
|
||||
if remove_unused:
|
||||
files = []
|
||||
@@ -104,12 +103,12 @@ def upload_data(*, properties, download_type=DownloadDataType.ALL, remove_unused
|
||||
ftp.delete(name)
|
||||
|
||||
for file_name in os.listdir(data_path):
|
||||
if file_name == _SATELLITES_XML_FILE or file_name == _WEBTV_XML_FILE:
|
||||
if file_name == _SAT_XML_FILE or file_name == _WEBTV_XML_FILE:
|
||||
continue
|
||||
if file_name.endswith(__DATA_FILES_LIST):
|
||||
send_file(file_name, data_path, ftp)
|
||||
|
||||
if download_type is DownloadDataType.PICONS:
|
||||
if download_type is DownloadType.PICONS:
|
||||
picons_dir_path = properties.get("picons_dir_path")
|
||||
picons_path = properties.get("picons_path")
|
||||
try:
|
||||
|
||||
@@ -1,96 +1,53 @@
|
||||
from app.commons import run_idle
|
||||
from app.tools import vlc
|
||||
from app.ui.uicommons import Gtk, Gdk
|
||||
|
||||
MRL = "url"
|
||||
|
||||
|
||||
class Player:
|
||||
_VLC_INSTANCE = None
|
||||
|
||||
def __init__(self, url):
|
||||
handlers = {"on_play": self.on_play,
|
||||
"on_stop": self.on_stop,
|
||||
"on_drawing_area_realize": self.on_drawing_area_realize,
|
||||
"on_press": self.on_press,
|
||||
"on_key_release": self.on_key_release,
|
||||
"on_state_changed": self.on_state_changed,
|
||||
"on_close_window": self.on_close_window}
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.add_objects_from_file("player.glade", ("player_main_window",))
|
||||
builder.connect_signals(handlers)
|
||||
self._main_window = builder.get_object("player_main_window")
|
||||
self._main_box = builder.get_object("main_box")
|
||||
self._buttonbox = builder.get_object("buttonbox")
|
||||
self._frame = builder.get_object("")
|
||||
self._drawing_area = builder.get_object("drawing_area")
|
||||
self._drawing_area.set_events(Gdk.ModifierType.BUTTON1_MASK)
|
||||
self._player = Player.get_vlc_instance().media_player_new()
|
||||
self._is_played = False
|
||||
self._url = url
|
||||
self._full_screen = False
|
||||
def __init__(self):
|
||||
self._is_playing = False
|
||||
self._player = self.get_vlc_instance()
|
||||
|
||||
@staticmethod
|
||||
def get_vlc_instance():
|
||||
if Player._VLC_INSTANCE:
|
||||
return Player._VLC_INSTANCE
|
||||
_VLC_INSTANCE = vlc.Instance("--no-xlib")
|
||||
_VLC_INSTANCE = vlc.Instance("--quiet --no-xlib").media_player_new()
|
||||
return _VLC_INSTANCE
|
||||
|
||||
def on_play(self, item):
|
||||
if not self._is_played:
|
||||
def play(self, mrl=None):
|
||||
if not self._is_playing:
|
||||
if mrl:
|
||||
self._player.set_mrl(mrl)
|
||||
self._player.play()
|
||||
self._is_played = True
|
||||
self._is_playing = True
|
||||
|
||||
def on_stop(self, item):
|
||||
if self._is_played:
|
||||
def stop(self):
|
||||
if self._is_playing:
|
||||
self._player.stop()
|
||||
self._is_played = False
|
||||
self._is_playing = False
|
||||
|
||||
def on_press(self, area, event: Gdk.EventButton):
|
||||
if event.button == Gdk.BUTTON_PRIMARY and event.type == Gdk.EventType.DOUBLE_BUTTON_PRESS:
|
||||
self.change_state()
|
||||
def pause(self):
|
||||
self._player.pause()
|
||||
|
||||
def on_state_changed(self, window, event):
|
||||
if event.new_window_state & Gdk.WindowState.FULLSCREEN:
|
||||
if self._main_box in window:
|
||||
window.remove(self._main_box)
|
||||
self._drawing_area.reparent(self._main_window)
|
||||
else:
|
||||
if self._drawing_area in self._main_window:
|
||||
window.remove(self._drawing_area)
|
||||
window.add(self._main_box)
|
||||
self._main_box.pack_start(self._drawing_area, True, True, 0)
|
||||
self._main_box.reorder_child(self._drawing_area, 0)
|
||||
|
||||
def change_state(self):
|
||||
self._full_screen = not self._full_screen
|
||||
self._main_window.fullscreen() if self._full_screen else self._main_window.unfullscreen()
|
||||
|
||||
def on_key_release(self, area, key):
|
||||
if key.keyval in (Gdk.KEY_F, Gdk.KEY_f):
|
||||
self.change_state()
|
||||
|
||||
def on_drawing_area_realize(self, widget):
|
||||
win_id = widget.get_window().get_xid()
|
||||
def release(self):
|
||||
if self._player:
|
||||
self._is_played = True
|
||||
self._player.set_xwindow(win_id)
|
||||
self._player.set_mrl(self._url)
|
||||
self._player.play()
|
||||
|
||||
@run_idle
|
||||
def on_close_window(self, *args):
|
||||
if self._player:
|
||||
self.on_stop(None)
|
||||
self._is_playing = False
|
||||
self._player.stop()
|
||||
self._player.release()
|
||||
Gtk.main_quit()
|
||||
|
||||
def show(self):
|
||||
self._main_window.show()
|
||||
Gtk.main()
|
||||
def set_xwindow(self, xid):
|
||||
self._player.set_xwindow(xid)
|
||||
|
||||
def set_mrl(self, mrl):
|
||||
self._player.set_mrl(mrl)
|
||||
|
||||
def is_playing(self):
|
||||
return self._is_playing
|
||||
|
||||
def set_full_screen(self, full):
|
||||
self._player.set_fullscreen(full)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
Player(MRL).show()
|
||||
pass
|
||||
|
||||
@@ -106,6 +106,10 @@ class ProviderParser(HTMLParser):
|
||||
""" Parser for satellite html page. (https://www.lyngsat.com/*sat-name*.html) """
|
||||
|
||||
_POSITION_PATTERN = re.compile("at\s\d+\..*(?:E|W)']")
|
||||
_DOMAIN = "https://www.lyngsat.com"
|
||||
_TV_DOMAIN = _DOMAIN + "/tvchannels/"
|
||||
_RADIO_DOMAIN = _DOMAIN + "/radiochannels/"
|
||||
_PKG_DOMAIN = _DOMAIN + "/packages/"
|
||||
|
||||
def __init__(self, entities=False, separator=' '):
|
||||
|
||||
@@ -121,6 +125,7 @@ class ProviderParser(HTMLParser):
|
||||
self._current_cell = []
|
||||
self.rows = []
|
||||
self._ids = set()
|
||||
self._prv_names = set()
|
||||
self._positon = None
|
||||
|
||||
def handle_starttag(self, tag, attrs):
|
||||
@@ -132,8 +137,9 @@ class ProviderParser(HTMLParser):
|
||||
if attrs[0][1].startswith("logo/"):
|
||||
self._current_row.append(attrs[0][1])
|
||||
if tag == "a":
|
||||
if "https://www.lyngsat.com/packages/" in attrs[0][1]:
|
||||
self._current_row.append(attrs[0][1])
|
||||
url = attrs[0][1]
|
||||
if url.startswith((self._PKG_DOMAIN, self._TV_DOMAIN, self._RADIO_DOMAIN)):
|
||||
self._current_row.append(url)
|
||||
|
||||
def handle_data(self, data):
|
||||
""" Save content to a cell """
|
||||
@@ -151,20 +157,27 @@ class ProviderParser(HTMLParser):
|
||||
self._current_row.append(final_cell)
|
||||
self._current_cell = []
|
||||
elif tag == 'tr':
|
||||
row = self._current_row
|
||||
r = self._current_row
|
||||
# Satellite position
|
||||
if not self._positon:
|
||||
pos = re.findall(self._POSITION_PATTERN, str(row))
|
||||
pos = re.findall(self._POSITION_PATTERN, str(r))
|
||||
if pos:
|
||||
self._positon = "".join(c for c in str(pos) if c.isdigit() or c in ".EW")
|
||||
|
||||
if len(row) == 12:
|
||||
on_id, sep, tid = str(row[-2]).partition("-")
|
||||
len_row = len(r)
|
||||
|
||||
if len_row == 12:
|
||||
name = r[5]
|
||||
self._prv_names.add(name)
|
||||
on_id, sep, tid = str(r[-2]).partition("-")
|
||||
if tid and on_id not in self._ON_ID_BLACK_LIST and on_id not in self._ids:
|
||||
row[-2] = on_id
|
||||
self.rows.append(row)
|
||||
r[-2] = on_id
|
||||
self._ids.add(on_id)
|
||||
row[0] = self._positon
|
||||
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=r[0], url=r[6], on_id=r[-2], selected=True))
|
||||
|
||||
self._current_row = []
|
||||
|
||||
def error(self, message):
|
||||
@@ -180,10 +193,8 @@ def parse_providers(open_path):
|
||||
|
||||
with open(open_path, encoding="utf-8", errors="replace") as f:
|
||||
parser.feed(f.read())
|
||||
rows = parser.rows
|
||||
|
||||
if rows:
|
||||
return [Provider(logo=r[2], name=r[5], pos=r[0], url=r[6], on_id=r[-2], selected=True) for r in rows]
|
||||
return parser.rows
|
||||
|
||||
|
||||
@run_task
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.18.3 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.12"/>
|
||||
<object class="GtkApplicationWindow" id="player_main_window">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="title" translatable="yes">Player</property>
|
||||
<property name="icon_name">vlc</property>
|
||||
<signal name="delete-event" handler="on_close_window" swapped="no"/>
|
||||
<signal name="window-state-event" handler="on_state_changed" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkBox" id="main_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkDrawingArea" id="drawing_area">
|
||||
<property name="width_request">320</property>
|
||||
<property name="height_request">240</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="has_focus">True</property>
|
||||
<signal name="button-press-event" handler="on_press" swapped="no"/>
|
||||
<signal name="key-release-event" handler="on_key_release" swapped="no"/>
|
||||
<signal name="realize" handler="on_drawing_area_realize" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="padding">2</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparator" id="separator">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="padding">2</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButtonBox" id="buttonbox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">3</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="spacing">2</property>
|
||||
<property name="homogeneous">True</property>
|
||||
<property name="layout_style">start</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="play_button">
|
||||
<property name="label">gtk-media-play</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="clicked" handler="on_play" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="stop_button">
|
||||
<property name="label">gtk-media-stop</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="clicked" handler="on_stop" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="close_button">
|
||||
<property name="label">gtk-close</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="clicked" handler="on_close_window" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">2</property>
|
||||
<property name="secondary">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="padding">5</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
@@ -90,14 +90,28 @@ class SatellitesParser(HTMLParser):
|
||||
|
||||
return list(map(get_sat, filter(lambda x: all(x) and len(x) == 5, self._rows)))
|
||||
elif self._source is SatelliteSource.LYNGSAT:
|
||||
rows = filter(lambda x: len(x) in (5, 7), self._rows)
|
||||
extra_pattern = re.compile("^https://www\.lyngsat\.com/[\w-]+\.html")
|
||||
sats = []
|
||||
current_pos = "0"
|
||||
for row in rows:
|
||||
for row in filter(lambda x: len(x) in (5, 7, 8), self._rows):
|
||||
r_len = len(row)
|
||||
if r_len == 7:
|
||||
current_pos = self.parse_position(row[2])
|
||||
sats.append((row[4], current_pos, row[5], row[1], False))
|
||||
if r_len == 8: # for a very limited number of satellites
|
||||
data = list(filter(None, row))
|
||||
urls = set()
|
||||
sat_type = ""
|
||||
for d in data:
|
||||
url = re.match(extra_pattern, d)
|
||||
if url:
|
||||
urls.add(url.group(0))
|
||||
if d in ("C", "Ku", "CKu"):
|
||||
sat_type = d
|
||||
current_pos = self.parse_position(data[1])
|
||||
for url in urls:
|
||||
name = url.rsplit("/")[-1].rstrip(".html").replace("-", " ")
|
||||
sats.append((name, current_pos, sat_type, url, False))
|
||||
elif r_len == 5:
|
||||
sats.append((row[2], current_pos, row[3], row[1], False))
|
||||
return sats
|
||||
@@ -183,4 +197,8 @@ class SatellitesParser(HTMLParser):
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pass
|
||||
parser = SatellitesParser(source=SatelliteSource.LYNGSAT)
|
||||
satts = parser.get_satellites_list(SatelliteSource.LYNGSAT)
|
||||
if satts:
|
||||
# list(map(print, satts))
|
||||
print("Parsed: ", len(satts))
|
||||
|
||||
138
app/tools/vlc.py
138
app/tools/vlc.py
@@ -53,10 +53,10 @@ import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
__version__ = "3.0.0102"
|
||||
__libvlc_version__ = "3.0.0"
|
||||
__generator_version__ = "1.2"
|
||||
build_date = "Mon Feb 19 18:13:20 2018 3.0.0"
|
||||
__version__ = "3.0.3104"
|
||||
__libvlc_version__ = "3.0.3"
|
||||
__generator_version__ = "1.4"
|
||||
build_date = "Fri Jul 13 15:18:27 2018 3.0.3"
|
||||
|
||||
# The libvlc doc states that filenames are expected to be in UTF8, do
|
||||
# not rely on sys.getfilesystemencoding() which will be confused,
|
||||
@@ -1112,7 +1112,7 @@ class Callback(ctypes.c_void_p):
|
||||
class LogCb(ctypes.c_void_p):
|
||||
"""Callback prototype for LibVLC log message handler.
|
||||
@param data: data pointer as given to L{libvlc_log_set}().
|
||||
@param level: message level (@ref libvlc_log_level).
|
||||
@param level: message level (@ref L{LogLevel}).
|
||||
@param ctx: message context (meta-information about the message).
|
||||
@param fmt: printf() format string (as defined by ISO C11).
|
||||
@param args: variable argument list for the format @note Log message handlers B{must} be thread-safe. @warning The message context pointer, the format string parameters and the variable arguments are only valid until the callback returns.
|
||||
@@ -1318,7 +1318,7 @@ class CallbackDecorators(object):
|
||||
LogCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int, Log_ptr, ctypes.c_char_p, ctypes.c_void_p)
|
||||
LogCb.__doc__ = '''Callback prototype for LibVLC log message handler.
|
||||
@param data: data pointer as given to L{libvlc_log_set}().
|
||||
@param level: message level (@ref libvlc_log_level).
|
||||
@param level: message level (@ref L{LogLevel}).
|
||||
@param ctx: message context (meta-information about the message).
|
||||
@param fmt: printf() format string (as defined by ISO C11).
|
||||
@param args: variable argument list for the format @note Log message handlers B{must} be thread-safe. @warning The message context pointer, the format string parameters and the variable arguments are only valid until the callback returns.
|
||||
@@ -1745,7 +1745,7 @@ AudioOutputDevice._fields_ = [ # recursive struct
|
||||
|
||||
|
||||
class TitleDescription(_Cstruct):
|
||||
_fields = [
|
||||
_fields_ = [
|
||||
('duration', ctypes.c_longlong),
|
||||
('name', ctypes.c_char_p),
|
||||
('menu', ctypes.c_bool),
|
||||
@@ -1753,7 +1753,7 @@ class TitleDescription(_Cstruct):
|
||||
|
||||
|
||||
class ChapterDescription(_Cstruct):
|
||||
_fields = [
|
||||
_fields_ = [
|
||||
('time_offset', ctypes.c_longlong),
|
||||
('duration', ctypes.c_longlong),
|
||||
('name', ctypes.c_char_p),
|
||||
@@ -1761,7 +1761,7 @@ class ChapterDescription(_Cstruct):
|
||||
|
||||
|
||||
class VideoViewpoint(_Cstruct):
|
||||
_fields = [
|
||||
_fields_ = [
|
||||
('yaw', ctypes.c_float),
|
||||
('pitch', ctypes.c_float),
|
||||
('roll', ctypes.c_float),
|
||||
@@ -1769,11 +1769,22 @@ class VideoViewpoint(_Cstruct):
|
||||
]
|
||||
|
||||
|
||||
class MediaDiscovererDescription(_Cstruct):
|
||||
_fields_ = [
|
||||
('name', ctypes.c_char_p),
|
||||
('longname', ctypes.c_char_p),
|
||||
('cat', MediaDiscovererCategory),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return '%s %s (%d) - %s' % (self.__class__.__name__, self.name, self.cat, self.longname)
|
||||
|
||||
|
||||
# This struct depends on the MediaSlaveType enum that is defined only
|
||||
# in > 2.2
|
||||
if 'MediaSlaveType' in locals():
|
||||
class MediaSlave(_Cstruct):
|
||||
_fields = [
|
||||
_fields_ = [
|
||||
('psz_uri', ctypes.c_char_p),
|
||||
('i_type', MediaSlaveType),
|
||||
('i_priority', ctypes.c_uint)
|
||||
@@ -1781,7 +1792,7 @@ if 'MediaSlaveType' in locals():
|
||||
|
||||
|
||||
class RDDescription(_Cstruct):
|
||||
_fields = [
|
||||
_fields_ = [
|
||||
('name', ctypes.c_char_p),
|
||||
('longname', ctypes.c_char_p)
|
||||
]
|
||||
@@ -2612,7 +2623,7 @@ class Media(_Ctype):
|
||||
return libvlc_media_add_option_flag(self, str_to_bytes(psz_options), i_flags)
|
||||
|
||||
def retain(self):
|
||||
'''Retain a reference to a media descriptor object (libvlc_media_t). Use
|
||||
'''Retain a reference to a media descriptor object (L{Media}). Use
|
||||
L{release}() to decrement the reference count of a
|
||||
media descriptor object.
|
||||
'''
|
||||
@@ -2667,7 +2678,7 @@ class Media(_Ctype):
|
||||
'''Get current state of media descriptor object. Possible media states are
|
||||
libvlc_NothingSpecial=0, libvlc_Opening, libvlc_Playing, libvlc_Paused,
|
||||
libvlc_Stopped, libvlc_Ended, libvlc_Error.
|
||||
See libvlc_state_t.
|
||||
See L{State}.
|
||||
@return: state of media descriptor object.
|
||||
'''
|
||||
return libvlc_media_get_state(self)
|
||||
@@ -2708,7 +2719,7 @@ class Media(_Ctype):
|
||||
To track when this is over you can listen to libvlc_MediaParsedChanged
|
||||
event. However if this functions returns an error, you will not receive any
|
||||
events.
|
||||
It uses a flag to specify parse options (see libvlc_media_parse_flag_t). All
|
||||
It uses a flag to specify parse options (see L{MediaParseFlag}). All
|
||||
these flags can be combined. By default, media is parsed if it's a local
|
||||
file.
|
||||
@note: Parsing can be aborted with L{parse_stop}().
|
||||
@@ -2716,7 +2727,7 @@ class Media(_Ctype):
|
||||
See L{get_meta}
|
||||
See L{tracks_get}
|
||||
See L{get_parsed_status}
|
||||
See libvlc_media_parse_flag_t.
|
||||
See L{MediaParseFlag}.
|
||||
@param parse_flag: parse options:
|
||||
@param timeout: maximum time allowed to preparse the media. If -1, the default "preparse-timeout" option will be used as a timeout. If 0, it will wait indefinitely. If > 0, the timeout will be used (in milliseconds).
|
||||
@return: -1 in case of error, 0 otherwise.
|
||||
@@ -2736,8 +2747,8 @@ class Media(_Ctype):
|
||||
def get_parsed_status(self):
|
||||
'''Get Parsed status for media descriptor object.
|
||||
See libvlc_MediaParsedChanged
|
||||
See libvlc_media_parsed_status_t.
|
||||
@return: a value of the libvlc_media_parsed_status_t enum.
|
||||
See L{MediaParsedStatus}.
|
||||
@return: a value of the L{MediaParsedStatus} enum.
|
||||
@version: LibVLC 3.0.0 or later.
|
||||
'''
|
||||
return libvlc_media_get_parsed_status(self)
|
||||
@@ -2760,7 +2771,7 @@ class Media(_Ctype):
|
||||
def get_type(self):
|
||||
'''Get the media type of the media descriptor object.
|
||||
@return: media type.
|
||||
@version: LibVLC 3.0.0 and later. See libvlc_media_type_t.
|
||||
@version: LibVLC 3.0.0 and later. See L{MediaType}.
|
||||
'''
|
||||
return libvlc_media_get_type(self)
|
||||
|
||||
@@ -3179,7 +3190,7 @@ class MediaListPlayer(_Ctype):
|
||||
|
||||
def get_state(self):
|
||||
'''Get current libvlc_state of media list player.
|
||||
@return: libvlc_state_t for media list player.
|
||||
@return: L{State} for media list player.
|
||||
'''
|
||||
return libvlc_media_list_player_get_state(self)
|
||||
|
||||
@@ -3306,8 +3317,14 @@ class MediaPlayer(_Ctype):
|
||||
'''
|
||||
titleDescription_pp = ctypes.POINTER(TitleDescription)()
|
||||
n = libvlc_media_player_get_full_title_descriptions(self, ctypes.byref(titleDescription_pp))
|
||||
info = ctypes.cast(ctypes.titleDescription_pp, ctypes.POINTER(ctypes.POINTER(TitleDescription) * n))
|
||||
return info
|
||||
info = ctypes.cast(titleDescription_pp, ctypes.POINTER(ctypes.POINTER(TitleDescription) * n))
|
||||
try:
|
||||
contents = info.contents
|
||||
except ValueError:
|
||||
# Media not parsed, no info.
|
||||
return None
|
||||
descr = (contents[i].contents for i in range(len(contents)))
|
||||
return descr
|
||||
|
||||
def get_full_chapter_descriptions(self, i_chapters_of_title):
|
||||
'''Get the full description of available chapters.
|
||||
@@ -3317,8 +3334,14 @@ class MediaPlayer(_Ctype):
|
||||
'''
|
||||
chapterDescription_pp = ctypes.POINTER(ChapterDescription)()
|
||||
n = libvlc_media_player_get_full_chapter_descriptions(self, ctypes.byref(chapterDescription_pp))
|
||||
info = ctypes.cast(ctypes.chapterDescription_pp, ctypes.POINTER(ctypes.POINTER(ChapterDescription) * n))
|
||||
return info
|
||||
info = ctypes.cast(chapterDescription_pp, ctypes.POINTER(ctypes.POINTER(ChapterDescription) * n))
|
||||
try:
|
||||
contents = info.contents
|
||||
except ValueError:
|
||||
# Media not parsed, no info.
|
||||
return None
|
||||
descr = (contents[i].contents for i in range(len(contents)))
|
||||
return descr
|
||||
|
||||
def video_get_size(self, num=0):
|
||||
"""Get the video size in pixels as 2-tuple (width, height).
|
||||
@@ -3788,7 +3811,7 @@ class MediaPlayer(_Ctype):
|
||||
|
||||
def get_state(self):
|
||||
'''Get current movie state.
|
||||
@return: the current state of the media player (playing, paused, ...) See libvlc_state_t.
|
||||
@return: the current state of the media player (playing, paused, ...) See L{State}.
|
||||
'''
|
||||
return libvlc_media_player_get_state(self)
|
||||
|
||||
@@ -3997,7 +4020,7 @@ class MediaPlayer(_Ctype):
|
||||
def video_set_teletext(self, i_page):
|
||||
'''Set new teletext page to retrieve.
|
||||
This function can also be used to send a teletext key.
|
||||
@param i_page: teletex page number requested. This value can be 0 to disable teletext, a number in the range ]0;1000[ to show the requested page, or a \ref libvlc_teletext_key_t. 100 is the default teletext page.
|
||||
@param i_page: teletex page number requested. This value can be 0 to disable teletext, a number in the range ]0;1000[ to show the requested page, or a \ref L{TeletextKey}. 100 is the default teletext page.
|
||||
'''
|
||||
return libvlc_video_set_teletext(self, i_page)
|
||||
|
||||
@@ -4068,7 +4091,7 @@ class MediaPlayer(_Ctype):
|
||||
|
||||
def video_get_logo_int(self, option):
|
||||
'''Get integer logo option.
|
||||
@param option: logo option to get, values of libvlc_video_logo_option_t.
|
||||
@param option: logo option to get, values of L{VideoLogoOption}.
|
||||
'''
|
||||
return libvlc_video_get_logo_int(self, option)
|
||||
|
||||
@@ -4077,7 +4100,7 @@ class MediaPlayer(_Ctype):
|
||||
are ignored.
|
||||
Passing libvlc_logo_enable as option value has the side effect of
|
||||
starting (arg !0) or stopping (arg 0) the logo filter.
|
||||
@param option: logo option to set, values of libvlc_video_logo_option_t.
|
||||
@param option: logo option to set, values of L{VideoLogoOption}.
|
||||
@param value: logo option value.
|
||||
'''
|
||||
return libvlc_video_set_logo_int(self, option, value)
|
||||
@@ -4085,14 +4108,14 @@ class MediaPlayer(_Ctype):
|
||||
def video_set_logo_string(self, option, psz_value):
|
||||
'''Set logo option as string. Options that take a different type value
|
||||
are ignored.
|
||||
@param option: logo option to set, values of libvlc_video_logo_option_t.
|
||||
@param option: logo option to set, values of L{VideoLogoOption}.
|
||||
@param psz_value: logo option value.
|
||||
'''
|
||||
return libvlc_video_set_logo_string(self, option, str_to_bytes(psz_value))
|
||||
|
||||
def video_get_adjust_int(self, option):
|
||||
'''Get integer adjust option.
|
||||
@param option: adjust option to get, values of libvlc_video_adjust_option_t.
|
||||
@param option: adjust option to get, values of L{VideoAdjustOption}.
|
||||
@version: LibVLC 1.1.1 and later.
|
||||
'''
|
||||
return libvlc_video_get_adjust_int(self, option)
|
||||
@@ -4102,7 +4125,7 @@ class MediaPlayer(_Ctype):
|
||||
are ignored.
|
||||
Passing libvlc_adjust_enable as option value has the side effect of
|
||||
starting (arg !0) or stopping (arg 0) the adjust filter.
|
||||
@param option: adust option to set, values of libvlc_video_adjust_option_t.
|
||||
@param option: adust option to set, values of L{VideoAdjustOption}.
|
||||
@param value: adjust option value.
|
||||
@version: LibVLC 1.1.1 and later.
|
||||
'''
|
||||
@@ -4110,7 +4133,7 @@ class MediaPlayer(_Ctype):
|
||||
|
||||
def video_get_adjust_float(self, option):
|
||||
'''Get float adjust option.
|
||||
@param option: adjust option to get, values of libvlc_video_adjust_option_t.
|
||||
@param option: adjust option to get, values of L{VideoAdjustOption}.
|
||||
@version: LibVLC 1.1.1 and later.
|
||||
'''
|
||||
return libvlc_video_get_adjust_float(self, option)
|
||||
@@ -4118,7 +4141,7 @@ class MediaPlayer(_Ctype):
|
||||
def video_set_adjust_float(self, option, value):
|
||||
'''Set adjust option as float. Options that take a different type value
|
||||
are ignored.
|
||||
@param option: adust option to set, values of libvlc_video_adjust_option_t.
|
||||
@param option: adust option to set, values of L{VideoAdjustOption}.
|
||||
@param value: adjust option value.
|
||||
@version: LibVLC 1.1.1 and later.
|
||||
'''
|
||||
@@ -4242,13 +4265,13 @@ class MediaPlayer(_Ctype):
|
||||
|
||||
def audio_get_channel(self):
|
||||
'''Get current audio channel.
|
||||
@return: the audio channel See libvlc_audio_output_channel_t.
|
||||
@return: the audio channel See L{AudioOutputChannel}.
|
||||
'''
|
||||
return libvlc_audio_get_channel(self)
|
||||
|
||||
def audio_set_channel(self, channel):
|
||||
'''Set current audio channel.
|
||||
@param channel: the audio channel, See libvlc_audio_output_channel_t.
|
||||
@param channel: the audio channel, See L{AudioOutputChannel}.
|
||||
@return: 0 on success, -1 on error.
|
||||
'''
|
||||
return libvlc_audio_set_channel(self, channel)
|
||||
@@ -5340,7 +5363,7 @@ def libvlc_media_add_option_flag(p_md, psz_options, i_flags):
|
||||
|
||||
|
||||
def libvlc_media_retain(p_md):
|
||||
'''Retain a reference to a media descriptor object (libvlc_media_t). Use
|
||||
'''Retain a reference to a media descriptor object (L{Media}). Use
|
||||
L{libvlc_media_release}() to decrement the reference count of a
|
||||
media descriptor object.
|
||||
@param p_md: the media descriptor.
|
||||
@@ -5430,7 +5453,7 @@ def libvlc_media_get_state(p_md):
|
||||
'''Get current state of media descriptor object. Possible media states are
|
||||
libvlc_NothingSpecial=0, libvlc_Opening, libvlc_Playing, libvlc_Paused,
|
||||
libvlc_Stopped, libvlc_Ended, libvlc_Error.
|
||||
See libvlc_state_t.
|
||||
See L{State}.
|
||||
@param p_md: a media descriptor object.
|
||||
@return: state of media descriptor object.
|
||||
'''
|
||||
@@ -5495,7 +5518,7 @@ def libvlc_media_parse_with_options(p_md, parse_flag, timeout):
|
||||
To track when this is over you can listen to libvlc_MediaParsedChanged
|
||||
event. However if this functions returns an error, you will not receive any
|
||||
events.
|
||||
It uses a flag to specify parse options (see libvlc_media_parse_flag_t). All
|
||||
It uses a flag to specify parse options (see L{MediaParseFlag}). All
|
||||
these flags can be combined. By default, media is parsed if it's a local
|
||||
file.
|
||||
@note: Parsing can be aborted with L{libvlc_media_parse_stop}().
|
||||
@@ -5503,7 +5526,7 @@ def libvlc_media_parse_with_options(p_md, parse_flag, timeout):
|
||||
See L{libvlc_media_get_meta}
|
||||
See L{libvlc_media_tracks_get}
|
||||
See L{libvlc_media_get_parsed_status}
|
||||
See libvlc_media_parse_flag_t.
|
||||
See L{MediaParseFlag}.
|
||||
@param p_md: media descriptor object.
|
||||
@param parse_flag: parse options:
|
||||
@param timeout: maximum time allowed to preparse the media. If -1, the default "preparse-timeout" option will be used as a timeout. If 0, it will wait indefinitely. If > 0, the timeout will be used (in milliseconds).
|
||||
@@ -5533,9 +5556,9 @@ def libvlc_media_parse_stop(p_md):
|
||||
def libvlc_media_get_parsed_status(p_md):
|
||||
'''Get Parsed status for media descriptor object.
|
||||
See libvlc_MediaParsedChanged
|
||||
See libvlc_media_parsed_status_t.
|
||||
See L{MediaParsedStatus}.
|
||||
@param p_md: media descriptor object.
|
||||
@return: a value of the libvlc_media_parsed_status_t enum.
|
||||
@return: a value of the L{MediaParsedStatus} enum.
|
||||
@version: LibVLC 3.0.0 or later.
|
||||
'''
|
||||
f = _Cfunctions.get('libvlc_media_get_parsed_status', None) or \
|
||||
@@ -5614,7 +5637,7 @@ def libvlc_media_get_type(p_md):
|
||||
'''Get the media type of the media descriptor object.
|
||||
@param p_md: media descriptor object.
|
||||
@return: media type.
|
||||
@version: LibVLC 3.0.0 and later. See libvlc_media_type_t.
|
||||
@version: LibVLC 3.0.0 and later. See L{MediaType}.
|
||||
'''
|
||||
f = _Cfunctions.get('libvlc_media_get_type', None) or \
|
||||
_Cfunction('libvlc_media_get_type', ((1,),), None,
|
||||
@@ -7045,7 +7068,7 @@ def libvlc_media_player_set_rate(p_mi, rate):
|
||||
def libvlc_media_player_get_state(p_mi):
|
||||
'''Get current movie state.
|
||||
@param p_mi: the Media Player.
|
||||
@return: the current state of the media player (playing, paused, ...) See libvlc_state_t.
|
||||
@return: the current state of the media player (playing, paused, ...) See L{State}.
|
||||
'''
|
||||
f = _Cfunctions.get('libvlc_media_player_get_state', None) or \
|
||||
_Cfunction('libvlc_media_player_get_state', ((1,),), None,
|
||||
@@ -7324,7 +7347,7 @@ def libvlc_video_new_viewpoint():
|
||||
'''
|
||||
f = _Cfunctions.get('libvlc_video_new_viewpoint', None) or \
|
||||
_Cfunction('libvlc_video_new_viewpoint', (), None,
|
||||
VideoViewpoint)
|
||||
ctypes.POINTER(VideoViewpoint))
|
||||
return f()
|
||||
|
||||
|
||||
@@ -7339,7 +7362,7 @@ def libvlc_video_update_viewpoint(p_mi, p_viewpoint, b_absolute):
|
||||
'''
|
||||
f = _Cfunctions.get('libvlc_video_update_viewpoint', None) or \
|
||||
_Cfunction('libvlc_video_update_viewpoint', ((1,), (1,), (1,),), None,
|
||||
ctypes.c_int, MediaPlayer, VideoViewpoint, ctypes.c_bool)
|
||||
ctypes.c_int, MediaPlayer, ctypes.POINTER(VideoViewpoint), ctypes.c_bool)
|
||||
return f(p_mi, p_viewpoint, b_absolute)
|
||||
|
||||
|
||||
@@ -7507,7 +7530,7 @@ def libvlc_video_set_teletext(p_mi, i_page):
|
||||
'''Set new teletext page to retrieve.
|
||||
This function can also be used to send a teletext key.
|
||||
@param p_mi: the media player.
|
||||
@param i_page: teletex page number requested. This value can be 0 to disable teletext, a number in the range ]0;1000[ to show the requested page, or a \ref libvlc_teletext_key_t. 100 is the default teletext page.
|
||||
@param i_page: teletex page number requested. This value can be 0 to disable teletext, a number in the range ]0;1000[ to show the requested page, or a \ref L{TeletextKey}. 100 is the default teletext page.
|
||||
'''
|
||||
f = _Cfunctions.get('libvlc_video_set_teletext', None) or \
|
||||
_Cfunction('libvlc_video_set_teletext', ((1,), (1,),), None,
|
||||
@@ -7639,7 +7662,7 @@ def libvlc_video_set_marquee_string(p_mi, option, psz_text):
|
||||
def libvlc_video_get_logo_int(p_mi, option):
|
||||
'''Get integer logo option.
|
||||
@param p_mi: libvlc media player instance.
|
||||
@param option: logo option to get, values of libvlc_video_logo_option_t.
|
||||
@param option: logo option to get, values of L{VideoLogoOption}.
|
||||
'''
|
||||
f = _Cfunctions.get('libvlc_video_get_logo_int', None) or \
|
||||
_Cfunction('libvlc_video_get_logo_int', ((1,), (1,),), None,
|
||||
@@ -7653,7 +7676,7 @@ def libvlc_video_set_logo_int(p_mi, option, value):
|
||||
Passing libvlc_logo_enable as option value has the side effect of
|
||||
starting (arg !0) or stopping (arg 0) the logo filter.
|
||||
@param p_mi: libvlc media player instance.
|
||||
@param option: logo option to set, values of libvlc_video_logo_option_t.
|
||||
@param option: logo option to set, values of L{VideoLogoOption}.
|
||||
@param value: logo option value.
|
||||
'''
|
||||
f = _Cfunctions.get('libvlc_video_set_logo_int', None) or \
|
||||
@@ -7666,7 +7689,7 @@ def libvlc_video_set_logo_string(p_mi, option, psz_value):
|
||||
'''Set logo option as string. Options that take a different type value
|
||||
are ignored.
|
||||
@param p_mi: libvlc media player instance.
|
||||
@param option: logo option to set, values of libvlc_video_logo_option_t.
|
||||
@param option: logo option to set, values of L{VideoLogoOption}.
|
||||
@param psz_value: logo option value.
|
||||
'''
|
||||
f = _Cfunctions.get('libvlc_video_set_logo_string', None) or \
|
||||
@@ -7678,7 +7701,7 @@ def libvlc_video_set_logo_string(p_mi, option, psz_value):
|
||||
def libvlc_video_get_adjust_int(p_mi, option):
|
||||
'''Get integer adjust option.
|
||||
@param p_mi: libvlc media player instance.
|
||||
@param option: adjust option to get, values of libvlc_video_adjust_option_t.
|
||||
@param option: adjust option to get, values of L{VideoAdjustOption}.
|
||||
@version: LibVLC 1.1.1 and later.
|
||||
'''
|
||||
f = _Cfunctions.get('libvlc_video_get_adjust_int', None) or \
|
||||
@@ -7693,7 +7716,7 @@ def libvlc_video_set_adjust_int(p_mi, option, value):
|
||||
Passing libvlc_adjust_enable as option value has the side effect of
|
||||
starting (arg !0) or stopping (arg 0) the adjust filter.
|
||||
@param p_mi: libvlc media player instance.
|
||||
@param option: adust option to set, values of libvlc_video_adjust_option_t.
|
||||
@param option: adust option to set, values of L{VideoAdjustOption}.
|
||||
@param value: adjust option value.
|
||||
@version: LibVLC 1.1.1 and later.
|
||||
'''
|
||||
@@ -7706,7 +7729,7 @@ def libvlc_video_set_adjust_int(p_mi, option, value):
|
||||
def libvlc_video_get_adjust_float(p_mi, option):
|
||||
'''Get float adjust option.
|
||||
@param p_mi: libvlc media player instance.
|
||||
@param option: adjust option to get, values of libvlc_video_adjust_option_t.
|
||||
@param option: adjust option to get, values of L{VideoAdjustOption}.
|
||||
@version: LibVLC 1.1.1 and later.
|
||||
'''
|
||||
f = _Cfunctions.get('libvlc_video_get_adjust_float', None) or \
|
||||
@@ -7719,7 +7742,7 @@ def libvlc_video_set_adjust_float(p_mi, option, value):
|
||||
'''Set adjust option as float. Options that take a different type value
|
||||
are ignored.
|
||||
@param p_mi: libvlc media player instance.
|
||||
@param option: adust option to set, values of libvlc_video_adjust_option_t.
|
||||
@param option: adust option to set, values of L{VideoAdjustOption}.
|
||||
@param value: adjust option value.
|
||||
@version: LibVLC 1.1.1 and later.
|
||||
'''
|
||||
@@ -7972,7 +7995,7 @@ def libvlc_audio_set_track(p_mi, i_track):
|
||||
def libvlc_audio_get_channel(p_mi):
|
||||
'''Get current audio channel.
|
||||
@param p_mi: media player.
|
||||
@return: the audio channel See libvlc_audio_output_channel_t.
|
||||
@return: the audio channel See L{AudioOutputChannel}.
|
||||
'''
|
||||
f = _Cfunctions.get('libvlc_audio_get_channel', None) or \
|
||||
_Cfunction('libvlc_audio_get_channel', ((1,),), None,
|
||||
@@ -7983,7 +8006,7 @@ def libvlc_audio_get_channel(p_mi):
|
||||
def libvlc_audio_set_channel(p_mi, channel):
|
||||
'''Set current audio channel.
|
||||
@param p_mi: media player.
|
||||
@param channel: the audio channel, See libvlc_audio_output_channel_t.
|
||||
@param channel: the audio channel, See L{AudioOutputChannel}.
|
||||
@return: 0 on success, -1 on error.
|
||||
'''
|
||||
f = _Cfunctions.get('libvlc_audio_set_channel', None) or \
|
||||
@@ -8348,7 +8371,7 @@ def libvlc_media_list_player_is_playing(p_mlp):
|
||||
def libvlc_media_list_player_get_state(p_mlp):
|
||||
'''Get current libvlc_state of media list player.
|
||||
@param p_mlp: media list player instance.
|
||||
@return: libvlc_state_t for media list player.
|
||||
@return: L{State} for media list player.
|
||||
'''
|
||||
f = _Cfunctions.get('libvlc_media_list_player_get_state', None) or \
|
||||
_Cfunction('libvlc_media_list_player_get_state', ((1,),), None,
|
||||
@@ -8522,7 +8545,10 @@ def _dot2int(v):
|
||||
'''
|
||||
t = [int(i) for i in v.split('.')]
|
||||
if len(t) == 3:
|
||||
t.append(0)
|
||||
if t[2] < 100:
|
||||
t.append(0)
|
||||
else: # 100 is arbitrary
|
||||
t[2:4] = divmod(t[2], 100)
|
||||
elif len(t) != 4:
|
||||
raise ValueError('"i.i.i[.i]": %r' % (v,))
|
||||
if min(t) < 0 or max(t) > 255:
|
||||
|
||||
1519
app/ui/dialogs.glade
1519
app/ui/dialogs.glade
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
from app.commons import run_idle, run_task
|
||||
from app.ftp import download_data, DownloadDataType, upload_data
|
||||
from app.ftp import download_data, DownloadType, upload_data
|
||||
from app.properties import Profile
|
||||
from .uicommons import Gtk, UI_RESOURCES_PATH, TEXT_DOMAIN
|
||||
from .dialogs import show_dialog, DialogType, get_message
|
||||
@@ -39,7 +39,6 @@ class DownloadDialog:
|
||||
self._webtv_radio_button = builder.get_object("webtv_radio_button")
|
||||
if profile is Profile.NEUTRINO_MP:
|
||||
self._webtv_radio_button.set_visible(True)
|
||||
# self._dialog.get_content_area().set_border_width(0)
|
||||
|
||||
@run_idle
|
||||
def on_receive(self, item):
|
||||
@@ -51,13 +50,13 @@ class DownloadDialog:
|
||||
self.download(False, self.get_download_type())
|
||||
|
||||
def get_download_type(self):
|
||||
download_type = DownloadDataType.ALL
|
||||
download_type = DownloadType.ALL
|
||||
if self._bouquets_radio_button.get_active():
|
||||
download_type = DownloadDataType.BOUQUETS
|
||||
download_type = DownloadType.BOUQUETS
|
||||
elif self._satellites_radio_button.get_active():
|
||||
download_type = DownloadDataType.SATELLITES
|
||||
download_type = DownloadType.SATELLITES
|
||||
elif self._webtv_radio_button.get_active():
|
||||
download_type = DownloadDataType.WEBTV
|
||||
download_type = DownloadType.WEBTV
|
||||
return download_type
|
||||
|
||||
def run(self):
|
||||
@@ -70,7 +69,6 @@ class DownloadDialog:
|
||||
self._info_bar.set_visible(False)
|
||||
|
||||
@run_idle
|
||||
@run_task
|
||||
def download(self, download, d_type):
|
||||
""" Download/upload data from/to receiver """
|
||||
try:
|
||||
@@ -87,7 +85,7 @@ class DownloadDialog:
|
||||
message = str(getattr(e, "message", str(e)))
|
||||
self.show_info_message(message, Gtk.MessageType.ERROR)
|
||||
else:
|
||||
if download and d_type is not DownloadDataType.SATELLITES:
|
||||
if download and d_type is not DownloadType.SATELLITES:
|
||||
self._open_data()
|
||||
|
||||
@run_idle
|
||||
|
||||
1191
app/ui/iptv.glade
Normal file
1191
app/ui/iptv.glade
Normal file
File diff suppressed because it is too large
Load Diff
298
app/ui/iptv.py
298
app/ui/iptv.py
@@ -1,27 +1,41 @@
|
||||
import re
|
||||
from urllib.error import HTTPError
|
||||
|
||||
from urllib.parse import urlparse
|
||||
from urllib.request import Request, urlopen
|
||||
|
||||
from app.commons import run_idle, run_task
|
||||
from app.eparser.ecommons import BqServiceType, Service
|
||||
from app.eparser.iptv import NEUTRINO_FAV_ID_FORMAT, StreamType, ENIGMA2_FAV_ID_FORMAT
|
||||
from app.properties import Profile
|
||||
from .uicommons import Gtk, Gdk, TEXT_DOMAIN, UI_RESOURCES_PATH, IPTV_ICON
|
||||
from .dialogs import Action, show_dialog, DialogType
|
||||
from .main_helper import get_base_model
|
||||
from .main_helper import get_base_model, get_iptv_url
|
||||
|
||||
_DIGIT_ENTRY_NAME = "digit-entry"
|
||||
_ENIGMA2_REFERENCE = "{}:0:{}:{:X}:{:X}:{:X}:{:X}:0:0:0"
|
||||
_PATTERN = re.compile("(?:^[\s]*$|\D)")
|
||||
|
||||
|
||||
def is_data_correct(elems):
|
||||
for elem in elems:
|
||||
if elem.get_name() == _DIGIT_ENTRY_NAME:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class IptvDialog:
|
||||
_DIGIT_ENTRY_NAME = "digit-entry"
|
||||
_ENIGMA2_REFERENCE = "{}:0:{}:{:X}:{:X}:{:X}:{:X}:0:0:0"
|
||||
|
||||
def __init__(self, transient, view, services, bouquet, profile=Profile.ENIGMA_2, action=Action.ADD):
|
||||
handlers = {"on_entry_changed": self.on_entry_changed,
|
||||
"on_url_changed": self.on_url_changed,
|
||||
"on_save": self.on_save,
|
||||
"on_cancel": self.on_cancel,
|
||||
"on_stream_type_changed": self.on_stream_type_changed}
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(TEXT_DOMAIN)
|
||||
builder.add_objects_from_file(UI_RESOURCES_PATH + "dialogs.glade", ("iptv_dialog", "stream_type_liststore"))
|
||||
builder.add_objects_from_file(UI_RESOURCES_PATH + "iptv.glade", ("iptv_dialog", "stream_type_liststore"))
|
||||
builder.connect_signals(handlers)
|
||||
|
||||
self._dialog = builder.get_object("iptv_dialog")
|
||||
@@ -44,15 +58,16 @@ class IptvDialog:
|
||||
self._bouquet = bouquet
|
||||
self._services = services
|
||||
self._model, self._paths = view.get_selection().get_selected_rows()
|
||||
self._PATTERN = re.compile("(?:^[\s]*$|\D)")
|
||||
# style
|
||||
self._style_provider = Gtk.CssProvider()
|
||||
self._style_provider.load_from_path(UI_RESOURCES_PATH + "style.css")
|
||||
for el in (self._srv_type_entry, self._sid_entry, self._tr_id_entry, self._net_id_entry, self._namespace_entry):
|
||||
self._digit_elems = (self._srv_type_entry, self._sid_entry, self._tr_id_entry, self._net_id_entry,
|
||||
self._namespace_entry)
|
||||
for el in self._digit_elems:
|
||||
el.get_style_context().add_provider_for_screen(Gdk.Screen.get_default(), self._style_provider,
|
||||
Gtk.STYLE_PROVIDER_PRIORITY_USER)
|
||||
if profile is Profile.NEUTRINO_MP:
|
||||
builder.get_object("iptv_data_box").set_visible(False)
|
||||
builder.get_object("iptv_dialog_ts_data_frame").set_visible(False)
|
||||
builder.get_object("iptv_type_label").set_visible(False)
|
||||
builder.get_object("reference_entry").set_visible(False)
|
||||
builder.get_object("iptv_reference_label").set_visible(False)
|
||||
@@ -72,10 +87,13 @@ class IptvDialog:
|
||||
|
||||
def show(self):
|
||||
self._dialog.run()
|
||||
|
||||
def on_cancel(self, item):
|
||||
self._dialog.destroy()
|
||||
|
||||
def on_save(self, item):
|
||||
if not self.is_data_correct():
|
||||
self.on_url_changed(self._url_entry)
|
||||
if not is_data_correct(self._digit_elems) or self._url_entry.get_name() == _DIGIT_ENTRY_NAME:
|
||||
show_dialog(DialogType.ERROR, self._dialog, "Error. Verify the data!")
|
||||
return
|
||||
|
||||
@@ -112,26 +130,26 @@ class IptvDialog:
|
||||
|
||||
def _update_reference_entry(self):
|
||||
if self._profile is Profile.ENIGMA_2:
|
||||
self._reference_entry.set_text(self._ENIGMA2_REFERENCE.format(self.get_type(),
|
||||
self._srv_type_entry.get_text(),
|
||||
int(self._sid_entry.get_text()),
|
||||
int(self._tr_id_entry.get_text()),
|
||||
int(self._net_id_entry.get_text()),
|
||||
int(self._namespace_entry.get_text())))
|
||||
self._reference_entry.set_text(_ENIGMA2_REFERENCE.format(self.get_type(),
|
||||
self._srv_type_entry.get_text(),
|
||||
int(self._sid_entry.get_text()),
|
||||
int(self._tr_id_entry.get_text()),
|
||||
int(self._net_id_entry.get_text()),
|
||||
int(self._namespace_entry.get_text())))
|
||||
|
||||
def get_type(self):
|
||||
return 1 if self._stream_type_combobox.get_active() == 0 else 4097
|
||||
|
||||
def on_entry_changed(self, entry):
|
||||
if self._PATTERN.search(entry.get_text()):
|
||||
entry.set_name(self._DIGIT_ENTRY_NAME)
|
||||
if _PATTERN.search(entry.get_text()):
|
||||
entry.set_name(_DIGIT_ENTRY_NAME)
|
||||
else:
|
||||
entry.set_name("GtkEntry")
|
||||
self._update_reference_entry()
|
||||
|
||||
def on_url_changed(self, entry):
|
||||
url = urlparse(entry.get_text())
|
||||
entry.set_name("GtkEntry" if all([url.scheme, url.netloc, url.path]) else self._DIGIT_ENTRY_NAME)
|
||||
entry.set_name("GtkEntry" if all([url.scheme, url.netloc, url.path]) else _DIGIT_ENTRY_NAME)
|
||||
|
||||
def on_stream_type_changed(self, item):
|
||||
self._update_reference_entry()
|
||||
@@ -174,12 +192,246 @@ class IptvDialog:
|
||||
self._bouquet.insert(self._model.get_path(itr)[0], fav_id)
|
||||
self._services[fav_id] = Service(None, None, IPTV_ICON, name, *aggr[0:3], s_type, *aggr, fav_id, None)
|
||||
|
||||
def is_data_correct(self):
|
||||
for elem in (self._srv_type_entry, self._sid_entry, self._tr_id_entry, self._net_id_entry,
|
||||
self._namespace_entry, self._url_entry):
|
||||
if elem.get_name() == self._DIGIT_ENTRY_NAME:
|
||||
return False
|
||||
return True
|
||||
|
||||
class SearchUnavailableDialog:
|
||||
|
||||
def __init__(self, transient, model, fav_bouquet, iptv_rows, profile):
|
||||
handlers = {"on_search_unavailable_close": self.on_close}
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(TEXT_DOMAIN)
|
||||
builder.add_objects_from_file(UI_RESOURCES_PATH + "iptv.glade", ("search_unavailable_streams_dialog",))
|
||||
builder.connect_signals(handlers)
|
||||
|
||||
self._dialog = builder.get_object("search_unavailable_streams_dialog")
|
||||
self._dialog.set_transient_for(transient)
|
||||
self._model = model
|
||||
self._counter_label = builder.get_object("streams_rows_counter_label")
|
||||
self._level_bar = builder.get_object("unavailable_streams_level_bar")
|
||||
self._bouquet = fav_bouquet
|
||||
self._profile = profile
|
||||
self._iptv_rows = iptv_rows
|
||||
self._counter = -1
|
||||
self._max_rows = len(self._iptv_rows)
|
||||
self._level_bar.set_max_value(self._max_rows)
|
||||
self._download_task = True
|
||||
self._to_delete = []
|
||||
|
||||
self.update_counter()
|
||||
self.do_search()
|
||||
|
||||
@run_task
|
||||
def do_search(self):
|
||||
import concurrent.futures
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
|
||||
futures = {executor.submit(self.get_unavailable, row): row for row in self._iptv_rows}
|
||||
for future in concurrent.futures.as_completed(futures):
|
||||
if not self._download_task:
|
||||
executor.shutdown()
|
||||
return
|
||||
future.result()
|
||||
self._download_task = False
|
||||
self.on_close()
|
||||
|
||||
def get_unavailable(self, row):
|
||||
if not self._download_task:
|
||||
return
|
||||
try:
|
||||
req = Request(get_iptv_url(row, self._profile))
|
||||
self.update_bar()
|
||||
urlopen(req, timeout=2)
|
||||
except HTTPError as e:
|
||||
if e.code != 403:
|
||||
self.append_data(row)
|
||||
except Exception:
|
||||
self.append_data(row)
|
||||
|
||||
def append_data(self, row):
|
||||
self._to_delete.append(self._model.get_iter(row.path))
|
||||
self.update_counter()
|
||||
|
||||
@run_idle
|
||||
def update_bar(self):
|
||||
self._max_rows -= 1
|
||||
self._level_bar.set_value(self._max_rows)
|
||||
|
||||
@run_idle
|
||||
def update_counter(self):
|
||||
self._counter += 1
|
||||
self._counter_label.set_text(str(self._counter))
|
||||
|
||||
def show(self):
|
||||
response = self._dialog.run()
|
||||
self._dialog.destroy()
|
||||
|
||||
return self._to_delete if response not in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT) else False
|
||||
|
||||
@run_idle
|
||||
def on_close(self, item=None, event=None):
|
||||
if self._download_task and show_dialog(DialogType.QUESTION, self._dialog) == Gtk.ResponseType.CANCEL:
|
||||
return
|
||||
self._download_task = False
|
||||
self._dialog.destroy()
|
||||
|
||||
|
||||
class IptvListConfigurationDialog:
|
||||
|
||||
def __init__(self, transient, services, iptv_rows, bouquet, profile):
|
||||
handlers = {"on_apply": self.on_apply,
|
||||
"on_stream_type_default_togged": self.on_stream_type_default_togged,
|
||||
"on_stream_type_changed": self.on_stream_type_changed,
|
||||
"on_default_type_toggled": self.on_default_type_toggled,
|
||||
"on_auto_sid_toggled": self.on_auto_sid_toggled,
|
||||
"on_default_tid_toggled": self.on_default_tid_toggled,
|
||||
"on_default_nid_toggled": self.on_default_nid_toggled,
|
||||
"on_default_namespace_toggled": self.on_default_namespace_toggled,
|
||||
"on_reset_to_default": self.on_reset_to_default,
|
||||
"on_entry_changed": self.on_entry_changed,
|
||||
"on_info_bar_close": self.on_info_bar_close}
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(TEXT_DOMAIN)
|
||||
builder.add_objects_from_file(UI_RESOURCES_PATH + "iptv.glade",
|
||||
("iptv_list_configuration_dialog", "stream_type_liststore"))
|
||||
builder.connect_signals(handlers)
|
||||
|
||||
self._rows = iptv_rows
|
||||
self._services = services
|
||||
self._bouquet = bouquet
|
||||
self._profile = profile
|
||||
|
||||
self._dialog = builder.get_object("iptv_list_configuration_dialog")
|
||||
self._dialog.set_transient_for(transient)
|
||||
self._info_bar = builder.get_object("list_configuration_info_bar")
|
||||
self._reference_label = builder.get_object("reference_label")
|
||||
self._stream_type_check_button = builder.get_object("stream_type_default_check_button")
|
||||
self._type_check_button = builder.get_object("type_default_check_button")
|
||||
self._sid_auto_check_button = builder.get_object("sid_auto_check_button")
|
||||
self._tid_check_button = builder.get_object("tid_default_check_button")
|
||||
self._nid_check_button = builder.get_object("nid_default_check_button")
|
||||
self._namespace_check_button = builder.get_object("namespace_default_check_button")
|
||||
self._stream_type_combobox = builder.get_object("stream_type_list_combobox")
|
||||
self._list_srv_type_entry = builder.get_object("list_srv_type_entry")
|
||||
self._list_sid_entry = builder.get_object("list_sid_entry")
|
||||
self._list_tid_entry = builder.get_object("list_tid_entry")
|
||||
self._list_nid_entry = builder.get_object("list_nid_entry")
|
||||
self._list_namespace_entry = builder.get_object("list_namespace_entry")
|
||||
self._reset_to_default_switch = builder.get_object("reset_to_default_lists_switch")
|
||||
# style
|
||||
self._style_provider = Gtk.CssProvider()
|
||||
self._style_provider.load_from_path(UI_RESOURCES_PATH + "style.css")
|
||||
self._digit_elems = (self._list_srv_type_entry, self._list_sid_entry, self._list_tid_entry,
|
||||
self._list_nid_entry, self._list_namespace_entry)
|
||||
for el in self._digit_elems:
|
||||
el.get_style_context().add_provider_for_screen(Gdk.Screen.get_default(), self._style_provider,
|
||||
Gtk.STYLE_PROVIDER_PRIORITY_USER)
|
||||
|
||||
def show(self):
|
||||
self._dialog.run()
|
||||
self._dialog.destroy()
|
||||
|
||||
def on_stream_type_changed(self, box):
|
||||
self.update_reference()
|
||||
|
||||
def on_stream_type_default_togged(self, button):
|
||||
if button.get_active():
|
||||
self._stream_type_combobox.set_active(1)
|
||||
self._stream_type_combobox.set_sensitive(not button.get_active())
|
||||
|
||||
def on_default_type_toggled(self, button):
|
||||
if button.get_active():
|
||||
self._list_srv_type_entry.set_text("1")
|
||||
self._list_srv_type_entry.set_sensitive(not button.get_active())
|
||||
|
||||
def on_auto_sid_toggled(self, button):
|
||||
if button.get_active():
|
||||
self._list_sid_entry.set_text("0")
|
||||
self._list_sid_entry.set_sensitive(not button.get_active())
|
||||
|
||||
def on_default_tid_toggled(self, button):
|
||||
if button.get_active():
|
||||
self._list_tid_entry.set_text("0")
|
||||
self._list_tid_entry.set_sensitive(not button.get_active())
|
||||
|
||||
def on_default_nid_toggled(self, button):
|
||||
if button.get_active():
|
||||
self._list_nid_entry.set_text("0")
|
||||
self._list_nid_entry.set_sensitive(not button.get_active())
|
||||
|
||||
def on_default_namespace_toggled(self, button):
|
||||
if button.get_active():
|
||||
self._list_namespace_entry.set_text("0")
|
||||
self._list_namespace_entry.set_sensitive(not button.get_active())
|
||||
|
||||
@run_idle
|
||||
def on_reset_to_default(self, item, active):
|
||||
item.set_sensitive(not active)
|
||||
self._stream_type_combobox.set_active(1)
|
||||
self._list_srv_type_entry.set_text("1")
|
||||
for el in (self._list_sid_entry, self._list_nid_entry, self._list_tid_entry, self._list_namespace_entry):
|
||||
el.set_text("0")
|
||||
for el in (self._stream_type_check_button, self._type_check_button, self._sid_auto_check_button,
|
||||
self._tid_check_button, self._nid_check_button, self._namespace_check_button):
|
||||
el.set_active(True)
|
||||
|
||||
def on_info_bar_close(self, bar=None, resp=None):
|
||||
self._info_bar.set_visible(False)
|
||||
|
||||
@run_idle
|
||||
def on_apply(self, item):
|
||||
if not is_data_correct(self._digit_elems):
|
||||
show_dialog(DialogType.ERROR, self._dialog, "Error. Verify the data!")
|
||||
return
|
||||
|
||||
if len(self._bouquet) != len(self._rows):
|
||||
return
|
||||
|
||||
if self._profile is Profile.ENIGMA_2:
|
||||
reset = self._reset_to_default_switch.get_active()
|
||||
type_default = self._type_check_button.get_active()
|
||||
tid_default = self._tid_check_button.get_active()
|
||||
sid_auto = self._sid_auto_check_button.get_active()
|
||||
nid_default = self._nid_check_button.get_active()
|
||||
namespace_default = self._namespace_check_button.get_active()
|
||||
|
||||
for index, row in enumerate(self._rows):
|
||||
fav_id = row[7]
|
||||
data, sep, desc = fav_id.partition("http")
|
||||
data = data.split(":")
|
||||
|
||||
if reset:
|
||||
data[0] = " 4097"
|
||||
data[2], data[3], data[4], data[5], data[6] = "10000"
|
||||
else:
|
||||
data[0] = " 4097" if self._stream_type_combobox.get_active() == 1 else "1"
|
||||
data[2] = "1" if type_default else self._list_srv_type_entry.get_text()
|
||||
data[3] = "{:X}".format(index) if sid_auto else "0"
|
||||
data[4] = "0" if tid_default else "{:X}".format(int(self._list_tid_entry.get_text()))
|
||||
data[5] = "0" if nid_default else "{:X}".format(int(self._list_nid_entry.get_text()))
|
||||
data[6] = "0" if namespace_default else "{:X}".format(int(self._list_namespace_entry.get_text()))
|
||||
|
||||
data = ":".join(data)
|
||||
new_fav_id = "{}{}{}".format(data, sep, desc)
|
||||
row[7] = new_fav_id
|
||||
self._bouquet[index] = new_fav_id
|
||||
srv = self._services.pop(fav_id, None)
|
||||
self._services[new_fav_id] = srv._replace(fav_id=new_fav_id)
|
||||
|
||||
self._info_bar.set_visible(True)
|
||||
|
||||
@run_idle
|
||||
def update_reference(self):
|
||||
if is_data_correct(self._digit_elems):
|
||||
stream_type = "4097" if self._stream_type_combobox.get_active() == 1 else "1"
|
||||
self._reference_label.set_text(
|
||||
_ENIGMA2_REFERENCE.format(stream_type, *[int(elem.get_text()) for elem in self._digit_elems]))
|
||||
|
||||
def on_entry_changed(self, entry):
|
||||
if _PATTERN.search(entry.get_text()):
|
||||
entry.set_name(_DIGIT_ENTRY_NAME)
|
||||
else:
|
||||
entry.set_name("GtkEntry")
|
||||
self.update_reference()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
""" This is helper module for ui """
|
||||
import os
|
||||
import shutil
|
||||
from gi.repository import GdkPixbuf
|
||||
from gi.repository import GdkPixbuf, GLib
|
||||
|
||||
from app.commons import run_task
|
||||
from app.eparser import Service
|
||||
@@ -65,6 +65,8 @@ def move_items(key, view: Gtk.TreeView):
|
||||
|
||||
if paths:
|
||||
mod_length = len(model)
|
||||
if mod_length == len(paths):
|
||||
return
|
||||
cursor_path = view.get_cursor()[0]
|
||||
max_path = Gtk.TreePath.new_from_indices((mod_length,))
|
||||
min_path = Gtk.TreePath.new_from_indices((0,))
|
||||
@@ -138,15 +140,11 @@ def is_some_level(paths):
|
||||
# ***************** Rename *******************#
|
||||
|
||||
def rename(view, parent_window, target, fav_view=None, service_view=None, channels=None):
|
||||
model, paths = view.get_selection().get_selected_rows()
|
||||
model = get_base_model(model)
|
||||
|
||||
if not paths:
|
||||
return
|
||||
elif len(paths) > 1:
|
||||
show_dialog(DialogType.ERROR, parent_window, "Please, select only one item!")
|
||||
selection = get_selection(view, parent_window)
|
||||
if not selection:
|
||||
return
|
||||
|
||||
model, paths = selection
|
||||
itr = model.get_iter(paths)
|
||||
f_id = None
|
||||
channel_name = None
|
||||
@@ -185,9 +183,23 @@ def rename(view, parent_window, target, fav_view=None, service_view=None, channe
|
||||
channels[f_id] = old_ch._replace(service=channel_name)
|
||||
|
||||
|
||||
def get_selection(view, parent):
|
||||
""" Returns (model, paths) if possible """
|
||||
model, paths = view.get_selection().get_selected_rows()
|
||||
model = get_base_model(model)
|
||||
|
||||
if not paths:
|
||||
return
|
||||
elif len(paths) > 1:
|
||||
show_dialog(DialogType.ERROR, parent, "Please, select only one item!")
|
||||
return
|
||||
|
||||
return model, paths
|
||||
|
||||
|
||||
# ***************** Flags *******************#
|
||||
|
||||
def set_flags(flag, services_view, fav_view, channels, blacklist):
|
||||
def set_flags(flag, services_view, fav_view, services, blacklist):
|
||||
""" Updates flags for services. Returns True if any was changed. """
|
||||
target = ViewTarget.SERVICES if services_view.is_focus() else ViewTarget.FAV if fav_view.is_focus() else None
|
||||
if not target:
|
||||
@@ -207,19 +219,26 @@ def set_flags(flag, services_view, fav_view, channels, blacklist):
|
||||
|
||||
if flag is Flag.HIDE:
|
||||
if target is ViewTarget.SERVICES:
|
||||
set_hide(channels, model, paths)
|
||||
set_hide(services, model, paths)
|
||||
else:
|
||||
fav_ids = [model.get_value(model.get_iter(path), 7) for path in paths]
|
||||
srv_model = get_base_model(services_view.get_model())
|
||||
srv_paths = [row.path for row in srv_model if row[18] in fav_ids]
|
||||
set_hide(channels, srv_model, srv_paths)
|
||||
set_hide(services, srv_model, srv_paths)
|
||||
elif flag is Flag.LOCK:
|
||||
set_lock(blacklist, channels, model, paths, target, services_model=get_base_model(services_view.get_model()))
|
||||
set_lock(blacklist, services, model, paths, target, services_model=get_base_model(services_view.get_model()))
|
||||
|
||||
return True
|
||||
update_fav_model(fav_view, services)
|
||||
|
||||
|
||||
def set_lock(blacklist, channels, model, paths, target, services_model):
|
||||
def update_fav_model(fav_view, services):
|
||||
for row in get_base_model(fav_view.get_model()):
|
||||
srv = services.get(row[7], None)
|
||||
if srv:
|
||||
row[3], row[4] = srv.locked, srv.hide
|
||||
|
||||
|
||||
def set_lock(blacklist, services, model, paths, target, services_model):
|
||||
col_num = 4 if target is ViewTarget.SERVICES else 3
|
||||
locked = has_locked_hide(model, paths, col_num)
|
||||
|
||||
@@ -228,23 +247,29 @@ def set_lock(blacklist, channels, model, paths, target, services_model):
|
||||
for path in paths:
|
||||
itr = model.get_iter(path)
|
||||
fav_id = model.get_value(itr, 18 if target is ViewTarget.SERVICES else 7)
|
||||
channel = channels.get(fav_id, None)
|
||||
if channel:
|
||||
bq_id = to_bouquet_id(channel)
|
||||
srv = services.get(fav_id, None)
|
||||
if srv:
|
||||
bq_id = to_bouquet_id(srv)
|
||||
if not bq_id:
|
||||
continue
|
||||
blacklist.discard(bq_id) if locked else blacklist.add(bq_id)
|
||||
model.set_value(itr, col_num, None if locked else LOCKED_ICON)
|
||||
channels[fav_id] = channel._replace(locked=None if locked else LOCKED_ICON)
|
||||
services[fav_id] = srv._replace(locked=None if locked else LOCKED_ICON)
|
||||
ids.append(fav_id)
|
||||
|
||||
if target is ViewTarget.FAV and ids:
|
||||
for ch in services_model:
|
||||
if ch[18] in ids:
|
||||
ch[4] = None if locked else LOCKED_ICON
|
||||
gen = update_services_model(ids, locked, services_model)
|
||||
GLib.idle_add(lambda: next(gen, False))
|
||||
|
||||
|
||||
def set_hide(channels, model, paths):
|
||||
def update_services_model(ids, locked, services_model):
|
||||
for srv in services_model:
|
||||
if srv[18] in ids:
|
||||
srv[4] = None if locked else LOCKED_ICON
|
||||
yield True
|
||||
|
||||
|
||||
def set_hide(services, model, paths):
|
||||
col_num = 5
|
||||
hide = has_locked_hide(model, paths, col_num)
|
||||
|
||||
@@ -281,9 +306,9 @@ def set_hide(channels, model, paths):
|
||||
|
||||
model.set_value(itr, 0, (",".join(reversed(sorted(flags)))))
|
||||
fav_id = model.get_value(itr, 18)
|
||||
channel = channels.get(fav_id, None)
|
||||
if channel:
|
||||
channels[fav_id] = channel._replace(hide=None if hide else HIDE_ICON)
|
||||
srv = services.get(fav_id, None)
|
||||
if srv:
|
||||
services[fav_id] = srv._replace(hide=None if hide else HIDE_ICON)
|
||||
|
||||
|
||||
def has_locked_hide(model, paths, col_num):
|
||||
@@ -324,15 +349,22 @@ def scroll_to(index, view, paths=None):
|
||||
|
||||
# ***************** Picons *********************#
|
||||
|
||||
def update_picons(path, picons, model):
|
||||
def update_picons_data(path, picons):
|
||||
if not os.path.exists(path):
|
||||
return
|
||||
|
||||
for file in os.listdir(path):
|
||||
picons[file] = get_picon_pixbuf(path + file)
|
||||
|
||||
for r in model:
|
||||
model.set_value(model.get_iter(r.path), 8, picons.get(r[9], None))
|
||||
|
||||
def append_picons(picons, model):
|
||||
def append_picons_data(pcs, mod):
|
||||
for r in mod:
|
||||
mod.set_value(mod.get_iter(r.path), 8, pcs.get(r[9], None))
|
||||
yield True
|
||||
|
||||
app = append_picons_data(picons, model)
|
||||
GLib.idle_add(lambda: next(app, False), priority=GLib.PRIORITY_LOW)
|
||||
|
||||
|
||||
def assign_picon(target, srv_view, fav_view, transient, picons, options, services):
|
||||
@@ -391,13 +423,17 @@ def remove_picon(target, srv_view, fav_view, picons, options):
|
||||
fav_ids.append(model.get_value(itr, 18))
|
||||
picon_ids.append(model.get_value(itr, 9))
|
||||
else:
|
||||
fav_ids.append(model.get_value(itr, 7))
|
||||
srv_type, fav_id = model.get(itr, 5, 7)
|
||||
if srv_type == BqServiceType.IPTV.name:
|
||||
picon_ids.append("{}_{}_{}_{}_{}_{}_{}_{}_{}_{}.png".format(*fav_id.split(":")[0:10]).strip())
|
||||
else:
|
||||
fav_ids.append(fav_id)
|
||||
|
||||
def remove(md, path, itr):
|
||||
if md.get_value(itr, 7 if target is ViewTarget.SERVICES else 18) in fav_ids:
|
||||
md.set_value(itr, picon_pos, None)
|
||||
def remove(md, path, it):
|
||||
if md.get_value(it, 7 if target is ViewTarget.SERVICES else 18) in fav_ids:
|
||||
md.set_value(it, picon_pos, None)
|
||||
if target is ViewTarget.FAV:
|
||||
picon_ids.append(md.get_value(itr, 9))
|
||||
picon_ids.append(md.get_value(it, 9))
|
||||
|
||||
fav_view.get_model().foreach(remove) if target is ViewTarget.SERVICES else get_base_model(
|
||||
srv_view.get_model()).foreach(remove)
|
||||
@@ -533,5 +569,15 @@ def append_text_to_tview(char, view):
|
||||
view.scroll_to_mark(insert, 0.0, True, 0.0, 1.0)
|
||||
|
||||
|
||||
def get_iptv_url(row, profile):
|
||||
""" Returns url from iptv type row """
|
||||
data = row[7].split(":" if profile is Profile.ENIGMA_2 else "::")
|
||||
if profile is Profile.ENIGMA_2:
|
||||
data = list(filter(lambda x: "http" in x, data))
|
||||
if data:
|
||||
url = data[0]
|
||||
return url.replace("%3a", ":") if profile is Profile.ENIGMA_2 else url
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pass
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -6,10 +6,10 @@ import time
|
||||
from gi.repository import GLib, GdkPixbuf
|
||||
|
||||
from app.commons import run_idle, run_task
|
||||
from app.ftp import upload_data, DownloadDataType
|
||||
from app.ftp import upload_data, DownloadType
|
||||
from app.tools.picons import PiconsParser, parse_providers, Provider, convert_to
|
||||
from app.properties import Profile
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, TEXT_DOMAIN
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, TEXT_DOMAIN, TV_ICON
|
||||
from .dialogs import show_dialog, DialogType, get_message
|
||||
from .main_helper import update_entry_data, append_text_to_tview
|
||||
|
||||
@@ -38,9 +38,9 @@ class PiconsDialog:
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(TEXT_DOMAIN)
|
||||
builder.add_objects_from_file(UI_RESOURCES_PATH + "picons_dialog.glade",
|
||||
("picons_dialog", "receive_image", "providers_list_store"))
|
||||
builder.add_from_file(UI_RESOURCES_PATH + "picons_dialog.glade")
|
||||
builder.connect_signals(handlers)
|
||||
|
||||
self._dialog = builder.get_object("picons_dialog")
|
||||
self._dialog.set_transient_for(transient)
|
||||
self._providers_tree_view = builder.get_object("providers_tree_view")
|
||||
@@ -54,12 +54,12 @@ class PiconsDialog:
|
||||
self._info_bar = builder.get_object("info_bar")
|
||||
self._info_bar = builder.get_object("info_bar")
|
||||
self._message_label = builder.get_object("info_bar_message_label")
|
||||
self._load_providers_tool_button = builder.get_object("load_providers_tool_button")
|
||||
self._receive_tool_button = builder.get_object("receive_tool_button")
|
||||
self._convert_tool_button = builder.get_object("convert_tool_button")
|
||||
self._load_providers_button = builder.get_object("load_providers_button")
|
||||
self._receive_button = builder.get_object("receive_button")
|
||||
self._convert_button = builder.get_object("convert_button")
|
||||
self._enigma2_path_button = builder.get_object("enigma2_path_button")
|
||||
self._save_to_button = builder.get_object("save_to_button")
|
||||
self._send_tool_button = builder.get_object("send_tool_button")
|
||||
self._send_button = builder.get_object("send_button")
|
||||
self._enigma2_radio_button = builder.get_object("enigma2_radio_button")
|
||||
self._neutrino_mp_radio_button = builder.get_object("neutrino_mp_radio_button")
|
||||
self._resize_no_radio_button = builder.get_object("resize_no_radio_button")
|
||||
@@ -108,7 +108,7 @@ class PiconsDialog:
|
||||
providers = parse_providers(self._TMP_DIR + url[url.find("w"):])
|
||||
if providers:
|
||||
for p in providers:
|
||||
model.append((self.get_pixbuf(p[0]), p.name, p.pos, p.url, p.on_id, p.selected))
|
||||
model.append((self.get_pixbuf(p[0]) if p[0] else TV_ICON, p.name, p.pos, p.url, p.on_id, p.selected))
|
||||
self.update_receive_button_state()
|
||||
|
||||
def get_pixbuf(self, img_url):
|
||||
@@ -154,7 +154,8 @@ class PiconsDialog:
|
||||
PiconsParser.parse(path, self._picons_path, self._TMP_DIR, prv.on_id, pos,
|
||||
self._picon_ids, self.get_picons_format())
|
||||
self.resize(self._picons_path)
|
||||
self.show_info_message(get_message("Done!"), Gtk.MessageType.INFO)
|
||||
if not self._terminate:
|
||||
self.show_info_message(get_message("Done!"), Gtk.MessageType.INFO)
|
||||
|
||||
def write_to_buffer(self, fd, condition):
|
||||
if condition == GLib.IO_IN:
|
||||
@@ -175,11 +176,15 @@ class PiconsDialog:
|
||||
self.show_info_message(get_message("Resizing..."), Gtk.MessageType.INFO)
|
||||
command = "mogrify -resize {}! *.png".format(
|
||||
"320x240" if self._resize_220_132_radio_button.get_active() else "100x60").split()
|
||||
self._current_process = subprocess.Popen(command, universal_newlines=True, cwd=path)
|
||||
self._current_process.wait()
|
||||
try:
|
||||
self._current_process = subprocess.Popen(command, universal_newlines=True, cwd=path)
|
||||
self._current_process.wait()
|
||||
except FileNotFoundError as e:
|
||||
self.show_info_message("Conversion error. " + str(e), Gtk.MessageType.ERROR)
|
||||
self.on_cancel()
|
||||
|
||||
@run_task
|
||||
def on_cancel(self, item):
|
||||
def on_cancel(self, item=None):
|
||||
if self._current_process:
|
||||
self._terminate = True
|
||||
self._current_process.terminate()
|
||||
@@ -203,10 +208,13 @@ class PiconsDialog:
|
||||
self.show_dialog("The task is already running!", DialogType.ERROR)
|
||||
return
|
||||
|
||||
upload_data(properties=self._properties,
|
||||
download_type=DownloadDataType.PICONS,
|
||||
profile=self._profile,
|
||||
callback=lambda: self.show_info_message(get_message("Done!"), Gtk.MessageType.INFO))
|
||||
try:
|
||||
upload_data(properties=self._properties,
|
||||
download_type=DownloadType.PICONS,
|
||||
profile=self._profile,
|
||||
callback=lambda: self.show_info_message(get_message("Done!"), Gtk.MessageType.INFO))
|
||||
except OSError as e:
|
||||
self.show_info_message(str(e), Gtk.MessageType.ERROR)
|
||||
|
||||
def on_info_bar_close(self, bar=None, resp=None):
|
||||
self._info_bar.set_visible(False)
|
||||
@@ -229,7 +237,7 @@ class PiconsDialog:
|
||||
def on_url_changed(self, entry):
|
||||
suit = self._PATTERN.search(entry.get_text())
|
||||
entry.set_name("GtkEntry" if suit else "digit-entry")
|
||||
self._load_providers_tool_button.set_sensitive(suit if suit else False)
|
||||
self._load_providers_button.set_sensitive(suit if suit else False)
|
||||
|
||||
def on_position_edited(self, render, path, value):
|
||||
model = self._providers_tree_view.get_model()
|
||||
@@ -237,10 +245,10 @@ class PiconsDialog:
|
||||
|
||||
@run_idle
|
||||
def on_notebook_switch_page(self, nb, box, tab_num):
|
||||
self._load_providers_tool_button.set_visible(not tab_num)
|
||||
self._receive_tool_button.set_visible(not tab_num)
|
||||
self._convert_tool_button.set_visible(tab_num)
|
||||
self._send_tool_button.set_sensitive(not tab_num)
|
||||
self._load_providers_button.set_visible(not tab_num)
|
||||
self._receive_button.set_visible(not tab_num)
|
||||
self._convert_button.set_visible(tab_num)
|
||||
self._send_button.set_visible(not tab_num)
|
||||
|
||||
if self._enigma2_path_button.get_filename() is None:
|
||||
self._enigma2_path_button.set_current_folder(self._enigma2_picons_path)
|
||||
@@ -265,7 +273,7 @@ class PiconsDialog:
|
||||
|
||||
@run_idle
|
||||
def update_receive_button_state(self):
|
||||
self._receive_tool_button.set_sensitive(len(self.get_selected_providers()) > 0)
|
||||
self._receive_button.set_sensitive(len(self.get_selected_providers()) > 0)
|
||||
|
||||
def get_selected_providers(self):
|
||||
""" returns selected providers """
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -26,6 +26,7 @@ class SatellitesDialog:
|
||||
handlers = {"on_open": self.on_open,
|
||||
"on_remove": self.on_remove,
|
||||
"on_save": self.on_save,
|
||||
"on_save_as": self.on_save_as,
|
||||
"on_update": self.on_update,
|
||||
"on_up": self.on_up,
|
||||
"on_down": self.on_down,
|
||||
@@ -34,6 +35,7 @@ class SatellitesDialog:
|
||||
"on_transponder_add": self.on_transponder_add,
|
||||
"on_edit": self.on_edit,
|
||||
"on_key_release": self.on_key_release,
|
||||
"on_popover_release": self.on_popover_release,
|
||||
"on_row_activated": self.on_row_activated,
|
||||
"on_resize": self.on_resize,
|
||||
"on_quit": self.on_quit}
|
||||
@@ -41,22 +43,19 @@ class SatellitesDialog:
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(TEXT_DOMAIN)
|
||||
builder.add_objects_from_file(UI_RESOURCES_PATH + "satellites_dialog.glade",
|
||||
("satellites_editor_dialog", "satellites_tree_store", "popup_menu",
|
||||
"add_popup_menu", "add_menu_icon", "receive_menu_icon"))
|
||||
("satellites_editor_window", "satellites_tree_store", "popup_menu",
|
||||
"left_header_menu", "add_header_popover_menu"))
|
||||
builder.connect_signals(handlers)
|
||||
# Adding custom image for add_menu_tool_button
|
||||
add_menu_tool_button = builder.get_object("add_menu_tool_button")
|
||||
add_menu_tool_button.set_image(builder.get_object("add_menu_icon"))
|
||||
|
||||
self._dialog = builder.get_object("satellites_editor_dialog")
|
||||
self._dialog.set_transient_for(transient)
|
||||
self._dialog.get_content_area().set_border_width(0) # The width of the border around the app dialog area!
|
||||
self._window = builder.get_object("satellites_editor_window")
|
||||
self._window.set_transient_for(transient)
|
||||
# self._dialog.get_content_area().set_border_width(0) # The width of the border around the app dialog area!
|
||||
self._sat_view = builder.get_object("satellites_editor_tree_view")
|
||||
self._wait_dialog = WaitDialog(self._dialog)
|
||||
self._wait_dialog = WaitDialog(self._window)
|
||||
# Setting the last size of the dialog window if it was saved
|
||||
window_size = self._options.get("sat_editor_window_size", None)
|
||||
if window_size:
|
||||
self._dialog.resize(*window_size)
|
||||
self._window.resize(*window_size)
|
||||
|
||||
self._stores = {3: builder.get_object("pol_store"),
|
||||
4: builder.get_object("fec_store"),
|
||||
@@ -64,37 +63,41 @@ class SatellitesDialog:
|
||||
6: builder.get_object("mod_store")}
|
||||
self.on_satellites_list_load(self._sat_view.get_model())
|
||||
|
||||
@run_idle
|
||||
def show(self):
|
||||
self._dialog.run()
|
||||
self._dialog.destroy()
|
||||
self._window.show()
|
||||
|
||||
def on_resize(self, window):
|
||||
""" Stores new size properties for dialog window after resize """
|
||||
if self._options:
|
||||
self._options["sat_editor_window_size"] = window.get_size()
|
||||
|
||||
def on_quit(self, item):
|
||||
self._dialog.destroy()
|
||||
@run_idle
|
||||
def on_quit(self, *args):
|
||||
self._window.destroy()
|
||||
|
||||
@run_idle
|
||||
def on_open(self, model):
|
||||
file_filter = Gtk.FileFilter()
|
||||
file_filter.add_pattern("satellites.xml")
|
||||
file_filter.set_name("satellites.xml")
|
||||
response = show_dialog(dialog_type=DialogType.CHOOSER,
|
||||
transient=self._dialog,
|
||||
options=self._options,
|
||||
action_type=Gtk.FileChooserAction.OPEN,
|
||||
file_filter=file_filter)
|
||||
response = self.get_file_dialog_response(Gtk.FileChooserAction.OPEN)
|
||||
if response == Gtk.ResponseType.CANCEL:
|
||||
return
|
||||
|
||||
if not str(response).endswith("satellites.xml"):
|
||||
show_dialog(DialogType.ERROR, self._dialog, text="No satellites.xml file is selected!")
|
||||
show_dialog(DialogType.ERROR, self._window, text="No satellites.xml file is selected!")
|
||||
return
|
||||
self._data_path = response
|
||||
self.on_satellites_list_load(model)
|
||||
|
||||
def get_file_dialog_response(self, action: Gtk.FileChooserAction):
|
||||
file_filter = Gtk.FileFilter()
|
||||
file_filter.add_pattern("satellites.xml")
|
||||
file_filter.set_name("satellites.xml")
|
||||
response = show_dialog(dialog_type=DialogType.CHOOSER,
|
||||
transient=self._window,
|
||||
options=self._options,
|
||||
action_type=action,
|
||||
file_filter=file_filter)
|
||||
return response
|
||||
|
||||
@staticmethod
|
||||
def on_row_activated(view, path, column):
|
||||
if view.row_expanded(path):
|
||||
@@ -130,6 +133,9 @@ class SatellitesDialog:
|
||||
elif key == Gdk.KEY_Left or key == Gdk.KEY_Right:
|
||||
view.do_unselect_all(view)
|
||||
|
||||
def on_popover_release(self, menu, event):
|
||||
menu.hide()
|
||||
|
||||
@run_idle
|
||||
def on_satellites_list_load(self, model):
|
||||
""" Load satellites data into model """
|
||||
@@ -137,7 +143,7 @@ class SatellitesDialog:
|
||||
self._wait_dialog.show()
|
||||
satellites = get_satellites(self._data_path)
|
||||
except FileNotFoundError as e:
|
||||
show_dialog(DialogType.ERROR, self._dialog, getattr(e, "message", str(e)) +
|
||||
show_dialog(DialogType.ERROR, self._window, getattr(e, "message", str(e)) +
|
||||
"\n\nPlease, download files from receiver or setup your path for read data!")
|
||||
else:
|
||||
model.clear()
|
||||
@@ -177,7 +183,7 @@ class SatellitesDialog:
|
||||
|
||||
def on_satellite(self, satellite=None, edited_itr=None):
|
||||
""" Create or edit satellite"""
|
||||
sat_dialog = SatelliteDialog(self._dialog, satellite)
|
||||
sat_dialog = SatelliteDialog(self._window, satellite)
|
||||
sat = sat_dialog.run()
|
||||
sat_dialog.destroy()
|
||||
|
||||
@@ -198,10 +204,10 @@ class SatellitesDialog:
|
||||
if paths is None:
|
||||
return
|
||||
elif len(paths) == 0:
|
||||
show_dialog(DialogType.ERROR, self._dialog, "No satellite is selected!")
|
||||
show_dialog(DialogType.ERROR, self._window, "No satellite is selected!")
|
||||
return
|
||||
|
||||
dialog = TransponderDialog(self._dialog, transponder)
|
||||
dialog = TransponderDialog(self._window, transponder)
|
||||
tr = dialog.run()
|
||||
dialog.destroy()
|
||||
|
||||
@@ -252,7 +258,7 @@ class SatellitesDialog:
|
||||
"""
|
||||
model, paths = view.get_selection().get_selected_rows()
|
||||
if len(paths) > 1:
|
||||
show_dialog(DialogType.ERROR, self._dialog, message)
|
||||
show_dialog(DialogType.ERROR, self._window, message)
|
||||
return
|
||||
|
||||
return paths
|
||||
@@ -265,8 +271,9 @@ class SatellitesDialog:
|
||||
for itr in [model.get_iter(path) for path in paths]:
|
||||
model.remove(itr)
|
||||
|
||||
@run_idle
|
||||
def on_save(self, view):
|
||||
if show_dialog(DialogType.QUESTION, self._dialog) == Gtk.ResponseType.CANCEL:
|
||||
if show_dialog(DialogType.QUESTION, self._window) == Gtk.ResponseType.CANCEL:
|
||||
return
|
||||
|
||||
model = view.get_model()
|
||||
@@ -274,10 +281,15 @@ class SatellitesDialog:
|
||||
model.foreach(self.parse_data, satellites)
|
||||
write_satellites(satellites, self._data_path)
|
||||
|
||||
def on_save_as(self, item):
|
||||
response = self.get_file_dialog_response(Gtk.FileChooserAction.SAVE)
|
||||
if response == Gtk.ResponseType.CANCEL:
|
||||
return
|
||||
show_dialog(DialogType.ERROR, transient=self._window, text="Not implemented yet!")
|
||||
|
||||
@run_idle
|
||||
def on_update(self, item):
|
||||
dialog = SatellitesUpdateDialog(self._dialog, self._sat_view.get_model())
|
||||
dialog.run()
|
||||
dialog.destroy()
|
||||
SatellitesUpdateDialog(self._window, self._sat_view.get_model()).show()
|
||||
|
||||
@staticmethod
|
||||
def parse_data(model, path, itr, sats):
|
||||
@@ -444,6 +456,9 @@ class SatellitesUpdateDialog:
|
||||
"on_info_bar_close": self.on_info_bar_close,
|
||||
"on_filter_toggled": self.on_filter_toggled,
|
||||
"on_find_toggled": self.on_find_toggled,
|
||||
"on_popup_menu": self.on_popup_menu,
|
||||
"on_select_all": self.on_select_all,
|
||||
"on_unselect_all": self.on_unselect_all,
|
||||
"on_filter": self.on_filter,
|
||||
"on_search": self.on_search,
|
||||
"on_search_down": self.on_search_down,
|
||||
@@ -453,13 +468,14 @@ class SatellitesUpdateDialog:
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(TEXT_DOMAIN)
|
||||
builder.add_objects_from_file(UI_RESOURCES_PATH + "satellites_dialog.glade",
|
||||
("satellites_update_dialog", "update_source_store", "update_sat_list_store",
|
||||
("satellites_update_window", "update_source_store", "update_sat_list_store",
|
||||
"update_sat_list_model_filter", "update_sat_list_model_sort", "side_store",
|
||||
"pos_adjustment", "pos_adjustment2"))
|
||||
"pos_adjustment", "pos_adjustment2", "satellites_update_popup_menu",
|
||||
"remove_selection_image"))
|
||||
builder.connect_signals(handlers)
|
||||
|
||||
self._dialog = builder.get_object("satellites_update_dialog")
|
||||
self._dialog.set_transient_for(transient)
|
||||
self._window = builder.get_object("satellites_update_window")
|
||||
self._window.set_transient_for(transient)
|
||||
self._main_model = main_model
|
||||
# self._dialog.get_content_area().set_border_width(0)
|
||||
self._sat_view = builder.get_object("sat_update_tree_view")
|
||||
@@ -470,7 +486,7 @@ class SatellitesUpdateDialog:
|
||||
self._sat_update_info_bar = builder.get_object("sat_update_info_bar")
|
||||
self._info_bar_message_label = builder.get_object("info_bar_message_label")
|
||||
# Filter
|
||||
self._filter_info_bar = builder.get_object("sat_update_filter_info_bar")
|
||||
self._filter_bar = builder.get_object("sat_update_filter_bar")
|
||||
self._from_pos_button = builder.get_object("from_pos_button")
|
||||
self._to_pos_button = builder.get_object("to_pos_button")
|
||||
self._filter_from_combo_box = builder.get_object("filter_from_combo_box")
|
||||
@@ -479,7 +495,7 @@ class SatellitesUpdateDialog:
|
||||
self._filter_model.set_visible_func(self.filter_function)
|
||||
self._filter_positions = (0, 0)
|
||||
# Search
|
||||
self._search_info_bar = builder.get_object("sat_update_search_info_bar")
|
||||
self._search_bar = builder.get_object("sat_update_search_bar")
|
||||
self._search_provider = SearchProvider((self._sat_view,),
|
||||
builder.get_object("sat_update_search_down_button"),
|
||||
builder.get_object("sat_update_search_up_button"))
|
||||
@@ -487,17 +503,13 @@ class SatellitesUpdateDialog:
|
||||
self._download_task = False
|
||||
self._parser = None
|
||||
|
||||
def run(self):
|
||||
if self._dialog.run() == Gtk.ResponseType.CANCEL:
|
||||
self._download_task = False
|
||||
return
|
||||
|
||||
def destroy(self):
|
||||
self._dialog.destroy()
|
||||
def show(self):
|
||||
self._window.show()
|
||||
|
||||
@run_idle
|
||||
def on_update_satellites_list(self, item):
|
||||
if self._download_task:
|
||||
show_dialog(DialogType.ERROR, self._dialog, "The task is already running!")
|
||||
show_dialog(DialogType.ERROR, self._window, "The task is already running!")
|
||||
return
|
||||
|
||||
model = get_base_model(self._sat_view.get_model())
|
||||
@@ -522,18 +534,17 @@ class SatellitesUpdateDialog:
|
||||
for sat in sats:
|
||||
model.append(sat)
|
||||
|
||||
@run_task
|
||||
@run_idle
|
||||
def on_receive_satellites_list(self, item):
|
||||
if self._download_task:
|
||||
show_dialog(DialogType.ERROR, self._dialog, "The task is already running!")
|
||||
show_dialog(DialogType.ERROR, self._window, "The task is already running!")
|
||||
return
|
||||
self.receive_satellites()
|
||||
|
||||
@run_task
|
||||
def receive_satellites(self):
|
||||
self._download_task = True
|
||||
self._sat_update_expander.set_expanded(True)
|
||||
self._text_view.get_buffer().set_text("", 0)
|
||||
self.update_expander()
|
||||
model = self._sat_view.get_model()
|
||||
start = time.time()
|
||||
|
||||
@@ -545,9 +556,11 @@ class SatellitesUpdateDialog:
|
||||
futures = {executor.submit(self._parser.get_satellite, sat[:-1]): sat for sat in [r for r in model if r[4]]}
|
||||
for future in concurrent.futures.as_completed(futures):
|
||||
if not self._download_task:
|
||||
self._download_task = True
|
||||
executor.shutdown()
|
||||
appender.send("\nCanceled\n")
|
||||
appender.close()
|
||||
self._download_task = False
|
||||
return
|
||||
data = future.result()
|
||||
appender.send(text.format(data[0]))
|
||||
@@ -556,7 +569,7 @@ class SatellitesUpdateDialog:
|
||||
appender.send("-" * 75 + "\n")
|
||||
appender.send("Consumed : {:0.0f}s, {} satellites received.".format(start - time.time(), len(sats)))
|
||||
appender.close()
|
||||
# self.show_info_message(message, Gtk.MessageType.INFO)
|
||||
|
||||
sats = {s[2]: s for s in sats} # key = position, v = satellite
|
||||
|
||||
for row in self._main_model:
|
||||
@@ -571,6 +584,11 @@ class SatellitesUpdateDialog:
|
||||
|
||||
self._download_task = False
|
||||
|
||||
@run_idle
|
||||
def update_expander(self):
|
||||
self._sat_update_expander.set_expanded(True)
|
||||
self._text_view.get_buffer().set_text("", 0)
|
||||
|
||||
@run_idle
|
||||
def update_satellite(self, itr, row, sat):
|
||||
if self._main_model.iter_has_child(itr):
|
||||
@@ -590,14 +608,12 @@ class SatellitesUpdateDialog:
|
||||
text = yield
|
||||
append(text)
|
||||
|
||||
@run_idle
|
||||
def on_cancel_receive(self, item=None):
|
||||
self._download_task = False
|
||||
|
||||
def on_selected_toggled(self, toggle, path):
|
||||
s_model = self._sat_view.get_model()
|
||||
itr = self._filter_model.convert_iter_to_child_iter(s_model.convert_iter_to_child_iter(s_model.get_iter(path)))
|
||||
self._filter_model.get_model().set_value(itr, 4, not toggle.get_active())
|
||||
model = self._sat_view.get_model()
|
||||
self.update_state(model, path, not toggle.get_active())
|
||||
self.update_receive_button_state(self._filter_model)
|
||||
|
||||
@run_idle
|
||||
@@ -614,10 +630,10 @@ class SatellitesUpdateDialog:
|
||||
self._sat_update_info_bar.set_visible(False)
|
||||
|
||||
def on_find_toggled(self, button: Gtk.ToggleToolButton):
|
||||
self._search_info_bar.set_visible(button.get_active())
|
||||
self._search_bar.set_search_mode(button.get_active())
|
||||
|
||||
def on_filter_toggled(self, button: Gtk.ToggleToolButton):
|
||||
self._filter_info_bar.set_visible(button.get_active())
|
||||
self._filter_bar.set_search_mode(button.get_active())
|
||||
|
||||
@run_idle
|
||||
def on_filter(self, item):
|
||||
@@ -651,7 +667,28 @@ class SatellitesUpdateDialog:
|
||||
def on_search_up(self, item):
|
||||
self._search_provider.on_search_up()
|
||||
|
||||
def on_quit(self):
|
||||
def on_select_all(self, view):
|
||||
self.update_selection(view, True)
|
||||
|
||||
def on_unselect_all(self, view):
|
||||
self.update_selection(view, False)
|
||||
|
||||
def update_selection(self, view, select):
|
||||
model = view.get_model()
|
||||
view.get_model().foreach(lambda mod, path, itr: self.update_state(model, path, select))
|
||||
self.update_receive_button_state(self._filter_model)
|
||||
|
||||
def update_state(self, model, path, select):
|
||||
""" Updates checkbox state by given path in the list """
|
||||
itr = self._filter_model.convert_iter_to_child_iter(model.convert_iter_to_child_iter(model.get_iter(path)))
|
||||
self._filter_model.get_model().set_value(itr, 4, select)
|
||||
|
||||
def on_popup_menu(self, menu, event):
|
||||
""" Shows popup menu for the view """
|
||||
if event.get_event_type() == Gdk.EventType.BUTTON_PRESS and event.button == Gdk.BUTTON_SECONDARY:
|
||||
menu.popup(None, None, None, None, event.button, event.time)
|
||||
|
||||
def on_quit(self, window, event):
|
||||
self._download_task = False
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -164,11 +164,25 @@ class ServiceDetailsDialog:
|
||||
|
||||
def update_data_elements(self):
|
||||
model, paths = self._services_view.get_selection().get_selected_rows()
|
||||
itr = model.get_iter(paths)
|
||||
# Unpacking to search for an iterator for the base model
|
||||
filter_model = model.get_model()
|
||||
itr = filter_model.convert_iter_to_child_iter(model.convert_iter_to_child_iter(itr))
|
||||
self._current_model = get_base_model(model)
|
||||
itr = None
|
||||
if not paths:
|
||||
# If editing from bouquet list and services list in the filter mode
|
||||
fav_model, paths = self._fav_view.get_selection().get_selected_rows()
|
||||
fav_id = fav_model[paths][7]
|
||||
for row in self._current_model:
|
||||
if row[-2] == fav_id:
|
||||
itr = row.iter
|
||||
break
|
||||
else:
|
||||
itr = model.get_iter(paths)
|
||||
itr = filter_model.convert_iter_to_child_iter(model.convert_iter_to_child_iter(itr))
|
||||
|
||||
if not itr:
|
||||
return
|
||||
|
||||
srv = Service(*self._current_model[itr][:])
|
||||
self._old_service = srv
|
||||
self._current_itr = itr
|
||||
@@ -371,6 +385,9 @@ class ServiceDetailsDialog:
|
||||
8: new_service.picon})
|
||||
|
||||
def update_picon_name(self, old_name, new_name):
|
||||
if not os.path.isdir(self._picons_dir_path):
|
||||
return
|
||||
|
||||
for file_name in os.listdir(self._picons_dir_path):
|
||||
if file_name == old_name:
|
||||
old_file = os.path.join(self._picons_dir_path, old_name)
|
||||
|
||||
778
app/ui/settings_dialog.glade
Normal file
778
app/ui/settings_dialog.glade
Normal file
@@ -0,0 +1,778 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.22.1
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018 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
|
||||
|
||||
-->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.16"/>
|
||||
<!-- interface-license-type mit -->
|
||||
<!-- interface-name DemonEditor -->
|
||||
<!-- interface-description Enigma2 channel and satellites list editor for GNU/Linux. -->
|
||||
<!-- interface-copyright 2018 Dmitriy Yefremov -->
|
||||
<!-- interface-authors Dmitriy Yefremov -->
|
||||
<object class="GtkImage" id="reset_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-revert-to-saved</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="telnet_timeout_adjustment">
|
||||
<property name="lower">1</property>
|
||||
<property name="upper">11</property>
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
<object class="GtkDialog" id="settings_dialog">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="resizable">False</property>
|
||||
<property name="modal">True</property>
|
||||
<property name="destroy_with_parent">True</property>
|
||||
<property name="icon_name">preferences-desktop</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
<property name="skip_taskbar_hint">True</property>
|
||||
<property name="skip_pager_hint">True</property>
|
||||
<child type="titlebar">
|
||||
<object class="GtkHeaderBar">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="title" translatable="yes">Options</property>
|
||||
<property name="spacing">2</property>
|
||||
<property name="show_close_button">True</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<action-widgets>
|
||||
<action-widget response="-6">cancel_button</action-widget>
|
||||
<action-widget response="-5">ok_button</action-widget>
|
||||
</action-widgets>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkBox" id="main_box">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">2</property>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkButtonBox">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="layout_style">end</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="cancel_button">
|
||||
<property name="label">gtk-cancel</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="always_show_image">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="apply_button">
|
||||
<property name="label">gtk-apply</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="clicked" handler="apply_settings" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="ok_button">
|
||||
<property name="label">gtk-ok</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="always_show_image">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">6</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame" id="network_settings_frame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">1</property>
|
||||
<property name="margin_right">1</property>
|
||||
<property name="label_xalign">0.019999999552965164</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkNotebook" id="notebook">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="margin_left">2</property>
|
||||
<property name="margin_right">2</property>
|
||||
<property name="margin_top">2</property>
|
||||
<property name="margin_bottom">2</property>
|
||||
<child>
|
||||
<object class="GtkGrid" id="ftp_settings_grid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="row_spacing">2</property>
|
||||
<property name="column_spacing">2</property>
|
||||
<property name="column_homogeneous">True</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label4">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Host:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="host_field">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="text">127.0.0.1</property>
|
||||
<property name="primary_icon_name">network-transmit-receive-symbolic</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label13">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Login:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label14">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Password:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="port_field">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="text">21</property>
|
||||
<property name="primary_icon_name">network-workgroup-symbolic</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label15">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Port:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="login_field">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="text">root</property>
|
||||
<property name="primary_icon_name">avatar-default-symbolic</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="password_field">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="visibility">False</property>
|
||||
<property name="invisible_char">●</property>
|
||||
<property name="text">root</property>
|
||||
<property name="primary_icon_name">emblem-readonly</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="input_purpose">password</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="tab">
|
||||
<object class="GtkLabel" id="label1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">FTP</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="tab_fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkGrid" id="telnet_settings_grid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="row_spacing">2</property>
|
||||
<property name="column_spacing">2</property>
|
||||
<property name="column_homogeneous">True</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label3">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Port:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="telnet_password_field">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="primary_icon_name">emblem-readonly</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="telnet_login_field">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="primary_icon_name">avatar-default-symbolic</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label16">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Login:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label17">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Password:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="telnet_port_field">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="text" translatable="yes">23</property>
|
||||
<property name="primary_icon_name">network-workgroup-symbolic</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label19">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Timeout:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="telnet_timeout_spin_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Timeout between commands in seconds</property>
|
||||
<property name="max_length">2</property>
|
||||
<property name="primary_icon_name">alarm-symbolic</property>
|
||||
<property name="input_purpose">number</property>
|
||||
<property name="adjustment">telnet_timeout_adjustment</property>
|
||||
<property name="numeric">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child type="tab">
|
||||
<object class="GtkLabel" id="label2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Telnet</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
<property name="tab_fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child type="tab">
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Network settings:</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="settings_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">1</property>
|
||||
<property name="margin_right">1</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<object class="GtkFrame" id="stb_paths_frame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label_xalign">0.019999999552965164</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkGrid" id="stb_dirs_grid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="row_spacing">2</property>
|
||||
<property name="column_homogeneous">True</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label5">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Services and Bouquets files:</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="services_field">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="text">/etc/enigma2/</property>
|
||||
<property name="primary_icon_stock">gtk-edit</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label6">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">User bouquet files:</property>
|
||||
<property name="xalign">2.2351741291171123e-10</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="user_bouquet_field">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="text">/etc/enigma2/</property>
|
||||
<property name="primary_icon_stock">gtk-edit</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label7">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Satellites.xml file:</property>
|
||||
<property name="xalign">0.019999999552965164</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="satellites_xml_field">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="text">/etc/tuxbox/</property>
|
||||
<property name="primary_icon_stock">gtk-edit</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label20">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Picons:</property>
|
||||
<property name="xalign">2.2351741291171123e-10</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">6</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="picons_field">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="text">/usr/share/enigma2/picon</property>
|
||||
<property name="primary_icon_stock">gtk-edit</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">7</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkLabel" id="stb_paths_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">STB file paths:</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame" id="settings_profile_frame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label_xalign">0.20000000298023224</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="settings_profile_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="enigma_radio_button">
|
||||
<property name="label">Enigma2 </property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">neutrino_radio_button</property>
|
||||
<signal name="toggled" handler="on_profile_changed" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="support_ver5_check_button">
|
||||
<property name="label" translatable="yes">Ver. 5 support
|
||||
(experimental)</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparator" id="settings_prof_separator">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="padding">2</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="neutrino_radio_button">
|
||||
<property name="label">Neutrino-MP
|
||||
(experimental)</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">enigma_radio_button</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparator" id="settings_prof_separator2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="padding">2</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="reset_button">
|
||||
<property name="label" translatable="yes">Reset profile</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="margin_top">3</property>
|
||||
<property name="image">reset_image</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="clicked" handler="on_reset" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkLabel" id="label12">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Active profile:</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame" id="local_file_paths_frame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">1</property>
|
||||
<property name="margin_right">1</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="label_xalign">0.019999999552965164</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkGrid" id="local_paths_grid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="row_spacing">2</property>
|
||||
<property name="column_homogeneous">True</property>
|
||||
<child>
|
||||
<object class="GtkEntry" id="data_dir_field">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="text">/data</property>
|
||||
<property name="primary_icon_stock">gtk-edit</property>
|
||||
<property name="secondary_icon_name">folder-open-symbolic</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="secondary_icon_tooltip_text" translatable="yes">Select</property>
|
||||
<property name="secondary_icon_tooltip_markup" translatable="yes">Select</property>
|
||||
<signal name="icon-press" handler="on_data_dir_field_icon_press" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label8">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Data path:</property>
|
||||
<property name="lines">0</property>
|
||||
<property name="xalign">0.019999999552965164</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label18">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Picons path:</property>
|
||||
<property name="xalign">0.019999999552965164</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="picons_dir_field">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="text">/data/picons</property>
|
||||
<property name="primary_icon_stock">gtk-edit</property>
|
||||
<property name="secondary_icon_name">folder-open-symbolic</property>
|
||||
<signal name="icon-press" handler="on_picons_dir_field_icon_press" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Local file paths:</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<action-widgets>
|
||||
<action-widget response="-6">cancel_button</action-widget>
|
||||
<action-widget response="-5">ok_button</action-widget>
|
||||
</action-widgets>
|
||||
</object>
|
||||
</interface>
|
||||
@@ -18,8 +18,7 @@ class SettingsDialog:
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(TEXT_DOMAIN)
|
||||
builder.add_objects_from_file(UI_RESOURCES_PATH + "dialogs.glade",
|
||||
("settings_dialog", "telnet_timeout_adjustment"))
|
||||
builder.add_from_file(UI_RESOURCES_PATH + "settings_dialog.glade")
|
||||
builder.connect_signals(handlers)
|
||||
|
||||
self._dialog = builder.get_object("settings_dialog")
|
||||
|
||||
@@ -20,7 +20,7 @@ theme = Gtk.IconTheme.get_default()
|
||||
_IMAGE_MISSING = theme.load_icon("image-missing", 16, 0) if theme.lookup_icon("image-missing", 16, 0) else None
|
||||
CODED_ICON = theme.load_icon("emblem-readonly", 16, 0) if theme.lookup_icon(
|
||||
"emblem-readonly", 16, 0) else _IMAGE_MISSING
|
||||
LOCKED_ICON = theme.load_icon("system-lock-screen", 16, 0) if theme.lookup_icon(
|
||||
LOCKED_ICON = theme.load_icon("changes-prevent-symbolic", 16, 0) if theme.lookup_icon(
|
||||
"system-lock-screen", 16, 0) else _IMAGE_MISSING
|
||||
HIDE_ICON = theme.load_icon("go-jump", 16, 0) if theme.lookup_icon("go-jump", 16, 0) else _IMAGE_MISSING
|
||||
TV_ICON = theme.load_icon("tv-symbolic", 16, 0) if theme.lookup_icon("tv-symbolic", 16, 0) else _IMAGE_MISSING
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/bash
|
||||
VER="0.3.2_Pre-alpha"
|
||||
VER="0.4.0_Pre-alpha"
|
||||
B_PATH="dist/DemonEditor"
|
||||
DEB_PATH="$B_PATH/usr/share/demoneditor"
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
Package: DemonEditor
|
||||
Version: 0.3.2-Pre-alpha
|
||||
Version: 0.4.0-Pre-alpha
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Architecture: all
|
||||
Essential: no
|
||||
Depends: python3 (>= 3.5)
|
||||
Maintainer: Dmitriy Yefremov <dmitry.v.yefremov@gmail.com>
|
||||
Description: Enigma2 channels and satellites list editor
|
||||
Description: Enigma2 channel and satellites list editor
|
||||
|
||||
@@ -8,4 +8,4 @@ Exec=/usr/bin/demoneditor.sh
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Utility;Application;
|
||||
StartupNotify=true
|
||||
StartupNotify=false
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -58,9 +58,6 @@ msgstr "Копировать"
|
||||
msgid "Copy reference"
|
||||
msgstr "Копировать ссылку"
|
||||
|
||||
msgid "Data"
|
||||
msgstr ""
|
||||
|
||||
msgid "Download"
|
||||
msgstr "Загрузить"
|
||||
|
||||
@@ -94,6 +91,15 @@ msgstr "Импортировать m3u"
|
||||
msgid "Import m3u file"
|
||||
msgstr "Импортировать файл m3u"
|
||||
|
||||
msgid "List configuration"
|
||||
msgstr "Конфигурация списка"
|
||||
|
||||
msgid "Rename for this bouquet"
|
||||
msgstr "Переименовать для букета"
|
||||
|
||||
msgid "Set default name"
|
||||
msgstr "Имя по умолчанию"
|
||||
|
||||
msgid "Insert marker"
|
||||
msgstr "Вставить маркер"
|
||||
|
||||
@@ -148,18 +154,12 @@ msgstr "Загрузчик пиконов"
|
||||
msgid "Satellites downloader"
|
||||
msgstr "Загрузчик спутников"
|
||||
|
||||
msgid "Preferences"
|
||||
msgstr "Настройки"
|
||||
|
||||
msgid "Profile:"
|
||||
msgstr "Профиль:"
|
||||
|
||||
msgid "Radio"
|
||||
msgstr ""
|
||||
|
||||
msgid "Remove"
|
||||
msgstr "Удалить"
|
||||
|
||||
msgid "Remove all unavailable"
|
||||
msgstr "Удалить все недоступные"
|
||||
|
||||
msgid "Satellites editor"
|
||||
msgstr "Редактор спутников"
|
||||
|
||||
@@ -196,42 +196,18 @@ msgstr "Вы уверены?"
|
||||
msgid "Current data path:"
|
||||
msgstr "Текущий путь к данным:"
|
||||
|
||||
msgid "Data dir:"
|
||||
msgstr "Путь к данным:"
|
||||
|
||||
msgid "Data:"
|
||||
msgstr "Данные:"
|
||||
|
||||
msgid "Enigma2 channel and satellites list editor for GNU/Linux"
|
||||
msgstr "Редактор списка каналов и спутников Enigma2\n для GNU/Linux"
|
||||
|
||||
msgid "FTP"
|
||||
msgstr ""
|
||||
|
||||
msgid "Host:"
|
||||
msgstr "Адрес ресивера:"
|
||||
|
||||
msgid "Loading data..."
|
||||
msgstr "Загрузка данных..."
|
||||
|
||||
msgid "Login:"
|
||||
msgstr "Логин:"
|
||||
|
||||
msgid "Options"
|
||||
msgstr "Настройки"
|
||||
|
||||
msgid "Password:"
|
||||
msgstr "Пароль:"
|
||||
|
||||
msgid "Picons dir:"
|
||||
msgstr "Директория пиконов:"
|
||||
|
||||
msgid "Picons:"
|
||||
msgstr "Пиконы:"
|
||||
|
||||
msgid "Port:"
|
||||
msgstr "Порт:"
|
||||
|
||||
msgid "Receive"
|
||||
msgstr "Получить"
|
||||
|
||||
@@ -242,7 +218,7 @@ msgid "Receiver IP:"
|
||||
msgstr "IP адрес ресивера:"
|
||||
|
||||
msgid "Remove unused bouquets"
|
||||
msgstr "Удалить не испрльзуемые букеты"
|
||||
msgstr "Удалить неиспользуемые букеты"
|
||||
|
||||
msgid "Reset profile"
|
||||
msgstr "Сброс профиля"
|
||||
@@ -265,27 +241,44 @@ msgstr "Отправить файлы в ресивер"
|
||||
msgid "Services and Bouquets files:"
|
||||
msgstr "Файлы сервисов и букетов:"
|
||||
|
||||
msgid "Telnet"
|
||||
msgstr ""
|
||||
|
||||
msgid "Timeout between commands in seconds"
|
||||
msgstr "Пауза между коммандами в сек."
|
||||
|
||||
msgid "Timeout:"
|
||||
msgstr "Тайм-аут:"
|
||||
|
||||
msgid "User bouquet files:"
|
||||
msgstr "Файлы букетов:"
|
||||
|
||||
msgid "WebTV"
|
||||
msgstr ""
|
||||
|
||||
msgid "Extra:"
|
||||
msgstr "Дополнительно:"
|
||||
|
||||
# Filter bar
|
||||
msgid "Only free"
|
||||
msgstr "Только открытые"
|
||||
|
||||
msgid "All positions"
|
||||
msgstr "Все позиции"
|
||||
|
||||
msgid "All types"
|
||||
msgstr "Все типы"
|
||||
|
||||
# Streams player
|
||||
msgid "Play"
|
||||
msgstr "Воспроизведение"
|
||||
|
||||
msgid "Stop playback"
|
||||
msgstr "Останов"
|
||||
|
||||
msgid "Previous stream in the list"
|
||||
msgstr "Предыдущий поток в списке"
|
||||
|
||||
msgid "Next stream in the list"
|
||||
msgstr "Следующий поток в списке"
|
||||
|
||||
msgid "Toggle in fullscreen"
|
||||
msgstr "Показать во весь экран"
|
||||
|
||||
msgid "Close"
|
||||
msgstr "Закрыть"
|
||||
|
||||
# Picons dialog
|
||||
msgid "Load providers"
|
||||
msgstr "Загрузить провайдеров"
|
||||
msgstr "Загрузить провайдеры"
|
||||
|
||||
msgid "Providers"
|
||||
msgstr "Провайдеры"
|
||||
@@ -366,6 +359,13 @@ msgstr "Имя"
|
||||
msgid "Position"
|
||||
msgstr "Позиция"
|
||||
|
||||
# Satellites update dialog
|
||||
msgid "Satellites update"
|
||||
msgstr "Обновление спутников"
|
||||
|
||||
msgid "Remove selection"
|
||||
msgstr "Снять выделение"
|
||||
|
||||
# Service details dialog
|
||||
msgid "Service data:"
|
||||
msgstr "Данные сервиса:"
|
||||
@@ -419,6 +419,59 @@ msgstr "Поиск"
|
||||
msgid "Stream data"
|
||||
msgstr "Данные потока"
|
||||
|
||||
# IPTV list configuration dialog
|
||||
msgid "Starting values"
|
||||
msgstr "Стартовые значения"
|
||||
|
||||
msgid "Reset to default"
|
||||
msgstr "Сбросить по умолчанию"
|
||||
|
||||
msgid "IPTV streams list configuration"
|
||||
msgstr "Конфигурация списка IPTV потоков"
|
||||
|
||||
#Settings dialog
|
||||
msgid "Preferences"
|
||||
msgstr "Настройки"
|
||||
|
||||
msgid "Profile:"
|
||||
msgstr "Профиль:"
|
||||
|
||||
msgid "Timeout between commands in seconds"
|
||||
msgstr "Пауза между коммандами в сек."
|
||||
|
||||
msgid "Timeout:"
|
||||
msgstr "Тайм-аут:"
|
||||
|
||||
msgid "Login:"
|
||||
msgstr "Логин:"
|
||||
|
||||
msgid "Options"
|
||||
msgstr "Настройки"
|
||||
|
||||
msgid "Password:"
|
||||
msgstr "Пароль:"
|
||||
|
||||
msgid "Picons:"
|
||||
msgstr "Пиконы:"
|
||||
|
||||
msgid "Port:"
|
||||
msgstr "Порт:"
|
||||
|
||||
msgid "Data path:"
|
||||
msgstr "Путь к данным:"
|
||||
|
||||
msgid "Picons path:"
|
||||
msgstr "Путь к пиконам:"
|
||||
|
||||
msgid "Network settings:"
|
||||
msgstr "Настройки сети:"
|
||||
|
||||
msgid "STB file paths:"
|
||||
msgstr "Пути к файлам ресивера:"
|
||||
|
||||
msgid "Local file paths:"
|
||||
msgstr "Пути к локальным файлам:"
|
||||
|
||||
# Dialogs messages
|
||||
msgid "Error. No bouquet is selected!"
|
||||
msgstr "Ошибка. Не выбран букет!"
|
||||
@@ -492,6 +545,23 @@ msgstr "Недопустимая операция в данном контекс
|
||||
msgid "No VLC is found. Check that it is installed!"
|
||||
msgstr "VLC не найден. Проверьте, что он установлен!"
|
||||
|
||||
# Search unavailable streams dialog
|
||||
msgid "Please wait, streams testing in progress..."
|
||||
msgstr "Подождите, идет тестирование потоков ..."
|
||||
|
||||
msgid "Found"
|
||||
msgstr "Найдено"
|
||||
|
||||
msgid "unavailable streams."
|
||||
msgstr "недоступных потоков."
|
||||
|
||||
msgid "No changes required!"
|
||||
msgstr "Изменений не требуется!"
|
||||
|
||||
msgid "This list does not contains IPTV streams!"
|
||||
msgstr "Текущий список не содержит потоков IPTV!"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
5
repo/debian/changelog
Normal file
5
repo/debian/changelog
Normal file
@@ -0,0 +1,5 @@
|
||||
demon-editor (0.4.0-1~ppa1) bionic; urgency=low
|
||||
|
||||
* Initial release
|
||||
|
||||
-- Dmitriy Yefremov <dmitry.v.yefremov@gmail.com> Tue, 02 Oct 2018 12:41:40 +0300
|
||||
1
repo/debian/compat
Normal file
1
repo/debian/compat
Normal file
@@ -0,0 +1 @@
|
||||
10
|
||||
12
repo/debian/control
Normal file
12
repo/debian/control
Normal file
@@ -0,0 +1,12 @@
|
||||
Source: demon-editor
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Maintainer: Dmitriy Yefremov <dmitry.v.yefremov@gmail.com>
|
||||
Build-Depends: python3 (>= 3.5), debhelper (>= 10)
|
||||
Standards-Version: 4.1.2
|
||||
|
||||
Package: demon-editor
|
||||
Architecture: all
|
||||
Depends: python3 (>= 3.5)
|
||||
Description: Enigma2 channel and satellites list editor
|
||||
|
||||
26
repo/debian/copyright
Normal file
26
repo/debian/copyright
Normal file
@@ -0,0 +1,26 @@
|
||||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Contact: Dmitriy Yefremov <dmitry.v.yefremov@gmail.com>
|
||||
Source: https://github.com/DYefremov/DemonEditor
|
||||
|
||||
Files: *
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 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.
|
||||
2
repo/debian/install
Normal file
2
repo/debian/install
Normal file
@@ -0,0 +1,2 @@
|
||||
usr/share /usr
|
||||
usr/bin /usr
|
||||
6
repo/debian/rules
Executable file
6
repo/debian/rules
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/make -f
|
||||
export PYBUILD_NAME=demon-editor
|
||||
|
||||
%:
|
||||
dh $@
|
||||
|
||||
1
repo/debian/source/format
Normal file
1
repo/debian/source/format
Normal file
@@ -0,0 +1 @@
|
||||
3.0 (native)
|
||||
1
repo/debian/source/options
Normal file
1
repo/debian/source/options
Normal file
@@ -0,0 +1 @@
|
||||
extend-diff-ignore = "^[^/]*[.]egg-info/"
|
||||
Reference in New Issue
Block a user